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 <test/bootstrapfixture.hxx>
11 #include "helper/debughelper.hxx"
12 #include "helper/qahelper.hxx"
13 #include <markdata.hxx>
14 #include <calcconfig.hxx>
15 #include <clipparam.hxx>
16 #include <compiler.hxx>
17 #include <tokenarray.hxx>
18 #include <refdata.hxx>
19 #include <scopetools.hxx>
20 #include <formulacell.hxx>
21 #include <docsh.hxx>
22 #include <docfunc.hxx>
23 #include <inputopt.hxx>
24 #include <paramisc.hxx>
25 #include <tokenstringcontext.hxx>
26 #include <refupdatecontext.hxx>
27 #include <dbdata.hxx>
28 #include <scmatrix.hxx>
29 #include <validat.hxx>
30 #include <scitems.hxx>
31 #include <patattr.hxx>
32 #include <docpool.hxx>
33 #include <docoptio.hxx>
34 #include <formulaopt.hxx>
35 #include <externalrefmgr.hxx>
36 #include <scdll.hxx>
37 #include <scmod.hxx>
38 #include <svl/itemset.hxx>
39 
40 #include <formula/vectortoken.hxx>
41 #include <svl/broadcast.hxx>
42 #include <svl/intitem.hxx>
43 #include <sfx2/docfile.hxx>
44 
45 #include <memory>
46 #include <functional>
47 #include <set>
48 #include <algorithm>
49 
50 using namespace formula;
51 
52 namespace {
53 
getCachedRange(const ScExternalRefCache::TableTypeRef & pCacheTab)54 ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
55 {
56     ScRange aRange;
57 
58     vector<SCROW> aRows;
59     pCacheTab->getAllRows(aRows);
60     bool bFirst = true;
61     for (const SCROW nRow : aRows)
62     {
63         vector<SCCOL> aCols;
64         pCacheTab->getAllCols(nRow, aCols);
65         for (const SCCOL nCol : aCols)
66         {
67             if (bFirst)
68             {
69                 aRange.aStart = ScAddress(nCol, nRow, 0);
70                 aRange.aEnd = aRange.aStart;
71                 bFirst = false;
72             }
73             else
74             {
75                 if (nCol < aRange.aStart.Col())
76                     aRange.aStart.SetCol(nCol);
77                 else if (aRange.aEnd.Col() < nCol)
78                     aRange.aEnd.SetCol(nCol);
79 
80                 if (nRow < aRange.aStart.Row())
81                     aRange.aStart.SetRow(nRow);
82                 else if (aRange.aEnd.Row() < nRow)
83                     aRange.aEnd.SetRow(nRow);
84             }
85         }
86     }
87     return aRange;
88 }
89 
setExpandRefs(bool bExpand)90 void setExpandRefs(bool bExpand)
91 {
92     ScModule* pMod = SC_MOD();
93     ScInputOptions aOpt = pMod->GetInputOptions();
94     aOpt.SetExpandRefs(bExpand);
95     pMod->SetInputOptions(aOpt);
96 }
97 
testFormulaRefUpdateNameCopySheetCheckTab(ScDocument * pDoc,SCTAB nTab,bool bCheckNames)98 void testFormulaRefUpdateNameCopySheetCheckTab( ScDocument* pDoc, SCTAB nTab, bool bCheckNames )
99 {
100     if (bCheckNames)
101     {
102         const ScRangeData* pName;
103         pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL");
104         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL should exist", pName);
105         pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL");
106         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL should exist", pName);
107         pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_GLOBAL");
108         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_GLOBAL should exist", pName);
109         pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_LOCAL");
110         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_LOCAL should exist", pName);
111         pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED");
112         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED should exist", pName);
113         pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED_NOREF");
114         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED_NOREF should not exist", !pName);
115         pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_GLOBAL");
116         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_GLOBAL should exist", pName);
117         pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_LOCAL");
118         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_LOCAL should exist", pName);
119         pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED");
120         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED should exist", pName);
121         pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED_NOREF");
122         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED_NOREF should exist", pName);
123     }
124 
125     ScAddress aPos(0,0,0);
126     aPos.SetRow(0);
127     aPos.SetTab(nTab);
128     int nSheet = nTab + 1;
129     CPPUNIT_ASSERT_EQUAL( 1.0 * nSheet, pDoc->GetValue(aPos));
130     aPos.IncRow();
131     CPPUNIT_ASSERT_EQUAL( 11.0 * nSheet, pDoc->GetValue(aPos));
132     aPos.IncRow();
133     CPPUNIT_ASSERT_EQUAL( 100.0 * nSheet, pDoc->GetValue(aPos));
134     aPos.IncRow();
135     CPPUNIT_ASSERT_EQUAL( 11000.0 * nSheet, pDoc->GetValue(aPos));
136     aPos.IncRow();
137     CPPUNIT_ASSERT_EQUAL( 10000.0 * nSheet, pDoc->GetValue(aPos));
138     aPos.IncRow();
139     CPPUNIT_ASSERT_EQUAL( 1100000.0 * nSheet, pDoc->GetValue(aPos));
140 }
141 
142 class ColumnTest
143 {
144     ScDocument * m_pDoc;
145 
146     const SCROW m_nTotalRows;
147     const SCROW m_nStart1;
148     const SCROW m_nEnd1;
149     const SCROW m_nStart2;
150     const SCROW m_nEnd2;
151 
152 public:
ColumnTest(ScDocument * pDoc,SCROW nTotalRows,SCROW nStart1,SCROW nEnd1,SCROW nStart2,SCROW nEnd2)153     ColumnTest( ScDocument * pDoc, SCROW nTotalRows,
154                 SCROW nStart1, SCROW nEnd1, SCROW nStart2, SCROW nEnd2 )
155         : m_pDoc(pDoc), m_nTotalRows(nTotalRows)
156         , m_nStart1(nStart1), m_nEnd1(nEnd1)
157         , m_nStart2(nStart2), m_nEnd2(nEnd2)
158     {}
159 
operator ()(SCCOL nColumn,const OUString & rFormula,std::function<double (SCROW)> const & lExpected) const160     void operator() ( SCCOL nColumn, const OUString& rFormula,
161                       std::function<double(SCROW )> const & lExpected ) const
162     {
163         ScDocument aClipDoc(SCDOCMODE_CLIP);
164         ScMarkData aMark(m_pDoc->GetSheetLimits());
165 
166         ScAddress aPos(nColumn, m_nStart1, 0);
167         m_pDoc->SetString(aPos, rFormula);
168         ASSERT_DOUBLES_EQUAL( lExpected(m_nStart1), m_pDoc->GetValue(aPos) );
169 
170         // Copy formula cell to clipboard.
171         ScClipParam aClipParam(aPos, false);
172         aMark.SetMarkArea(aPos);
173         m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
174 
175         // Paste it to first range.
176         InsertDeleteFlags nFlags = InsertDeleteFlags::CONTENTS;
177         ScRange aDestRange(nColumn, m_nStart1, 0, nColumn, m_nEnd1, 0);
178         aMark.SetMarkArea(aDestRange);
179         m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
180 
181         // Paste it second range.
182         aDestRange = ScRange(nColumn, m_nStart2, 0, nColumn, m_nEnd2, 0);
183         aMark.SetMarkArea(aDestRange);
184         m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
185 
186         // Check the formula results for passed column.
187         for( SCROW i = 0; i < m_nTotalRows; ++i )
188         {
189             if( !((m_nStart1 <= i && i <= m_nEnd1) || (m_nStart2 <= i && i <= m_nEnd2)) )
190                 continue;
191             double fExpected = lExpected(i);
192             ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(nColumn,i,0)));
193         }
194     }
195 };
196 
197 }
198 
199 class TestFormula : public test::BootstrapFixture
200 {
201 public:
202     TestFormula();
203 
204     virtual void setUp() override;
205     virtual void tearDown() override;
206 
207     void testFormulaCreateStringFromTokens();
208     void testFormulaParseReference();
209     void testFetchVectorRefArray();
210     void testGroupConverter3D();
211     void testFormulaTokenEquality();
212     void testFormulaRefData();
213     void testFormulaCompiler();
214     void testFormulaCompilerJumpReordering();
215     void testFormulaCompilerImplicitIntersection2Param();
216     void testFormulaCompilerImplicitIntersection1ParamNoChange();
217     void testFormulaCompilerImplicitIntersection1ParamWithChange();
218     void testFormulaCompilerImplicitIntersection1NoGroup();
219     void testFormulaCompilerImplicitIntersectionOperators();
220     void testFormulaAnnotateTrimOnDoubleRefs();
221     void testFormulaRefUpdate();
222     void testFormulaRefUpdateRange();
223     void testFormulaRefUpdateSheets();
224     void testFormulaRefUpdateSheetsDelete();
225     void testFormulaRefUpdateInsertRows();
226     void testFormulaRefUpdateInsertColumns();
227     void testFormulaRefUpdateMove();
228     void testFormulaRefUpdateMoveUndo();
229     void testFormulaRefUpdateMoveUndo2();
230     void testFormulaRefUpdateMoveUndo3NonShared();
231     void testFormulaRefUpdateMoveUndo3Shared();
232     void testFormulaRefUpdateMoveUndoDependents();
233     void testFormulaRefUpdateMoveUndo4();
234     void testFormulaRefUpdateMoveToSheet();
235     void testFormulaRefUpdateDeleteContent();
236     void testFormulaRefUpdateDeleteAndShiftLeft();
237     void testFormulaRefUpdateDeleteAndShiftLeft2();
238     void testFormulaRefUpdateDeleteAndShiftUp();
239     void testFormulaRefUpdateName();
240     void testFormulaRefUpdateNameMove();
241     void testFormulaRefUpdateNameExpandRef();
242     void testFormulaRefUpdateNameExpandRef2();
243     void testFormulaRefUpdateNameDeleteRow();
244     void testFormulaRefUpdateNameCopySheet();
245     void testFormulaRefUpdateSheetLocalMove();
246     void testFormulaRefUpdateNameDelete();
247     void testFormulaRefUpdateValidity();
248     void testTokenArrayRefUpdateMove();
249     void testSingleCellCopyColumnLabel();
250     void testIntersectionOpExcel();
251     void testTdf97369();
252     void testTdf97587();
253     void testTdf93415();
254     void testTdf100818();
255     void testMatConcat();
256     void testMatConcatReplication();
257     void testExternalRef();
258     void testFormulaDepTracking();
259     void testFormulaDepTracking2();
260     void testFormulaDepTracking3();
261     void testFormulaDepTrackingDeleteRow();
262     void testFormulaDepTrackingDeleteCol();
263     void testFormulaMatrixResultUpdate();
264     void testExternalRefFunctions();
265     void testExternalRangeName();
266     void testExternalRefUnresolved();
267     void testRefR1C1WholeCol();
268     void testRefR1C1WholeRow();
269     void testIterations();
270     void testInsertColCellStoreEventSwap();
271     void testFormulaAfterDeleteRows();
272     void testMultipleOperations();
273     void testFuncCOLUMN();
274     void testFuncCOUNT();
275     void testFuncCOUNTBLANK();
276     void testFuncROW();
277     void testFuncSUM();
278     void testFuncPRODUCT();
279     void testFuncSUMPRODUCT();
280     void testFuncSUMXMY2();
281     void testFuncMIN();
282     void testFuncN();
283     void testFuncCOUNTIF();
284     void testFuncNUMBERVALUE();
285     void testFuncLEN();
286     void testFuncLOOKUP();
287     void testFuncLOOKUParrayWithError();
288     void testTdf141146();
289     void testFuncVLOOKUP();
290     void testFuncMATCH();
291     void testFuncCELL();
292     void testFuncDATEDIF();
293     void testFuncINDIRECT();
294     void testFuncINDIRECT2();
295     void testFunc_MATCH_INDIRECT();
296     void testFuncIF();
297     void testFuncCHOOSE();
298     void testFuncIFERROR();
299     void testFuncSHEET();
300     void testFuncNOW();
301     void testMatrixOp();
302     void testFuncRangeOp();
303     void testFuncFORMULA();
304     void testFuncTableRef();
305     void testFuncFTEST();
306     void testFuncFTESTBug();
307     void testFuncCHITEST();
308     void testFuncTTEST();
309     void testFuncSUMX2PY2();
310     void testFuncSUMX2MY2();
311     void testFuncGCD();
312     void testFuncLCM();
313     void testFuncSUMSQ();
314     void testFuncMDETERM();
315     void testFormulaErrorPropagation();
316     void testFuncRowsHidden();
317     void testFuncSUMIFS();
318     void testFuncRefListArraySUBTOTAL();
319     void testFuncJumpMatrixArrayIF();
320     void testFuncJumpMatrixArrayOFFSET();
321 
322     CPPUNIT_TEST_SUITE(TestFormula);
323 
324     CPPUNIT_TEST(testFormulaCreateStringFromTokens);
325     CPPUNIT_TEST(testFormulaParseReference);
326     CPPUNIT_TEST(testFetchVectorRefArray);
327     CPPUNIT_TEST(testGroupConverter3D);
328     CPPUNIT_TEST(testFormulaTokenEquality);
329     CPPUNIT_TEST(testFormulaRefData);
330     CPPUNIT_TEST(testFormulaCompiler);
331     CPPUNIT_TEST(testFormulaCompilerJumpReordering);
332     CPPUNIT_TEST(testFormulaCompilerImplicitIntersection2Param);
333     CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1ParamNoChange);
334     CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1ParamWithChange);
335     CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1NoGroup);
336     CPPUNIT_TEST(testFormulaCompilerImplicitIntersectionOperators);
337     CPPUNIT_TEST(testFormulaAnnotateTrimOnDoubleRefs);
338     CPPUNIT_TEST(testFormulaRefUpdate);
339     CPPUNIT_TEST(testFormulaRefUpdateRange);
340     CPPUNIT_TEST(testFormulaRefUpdateSheets);
341     CPPUNIT_TEST(testFormulaRefUpdateSheetsDelete);
342     CPPUNIT_TEST(testFormulaRefUpdateInsertRows);
343     CPPUNIT_TEST(testFormulaRefUpdateInsertColumns);
344     CPPUNIT_TEST(testFormulaRefUpdateMove);
345     CPPUNIT_TEST(testFormulaRefUpdateMoveUndo);
346     CPPUNIT_TEST(testFormulaRefUpdateMoveUndo2);
347     CPPUNIT_TEST(testFormulaRefUpdateMoveUndo3NonShared);
348     CPPUNIT_TEST(testFormulaRefUpdateMoveUndo3Shared);
349     CPPUNIT_TEST(testFormulaRefUpdateMoveUndoDependents);
350     CPPUNIT_TEST(testFormulaRefUpdateMoveUndo4);
351     CPPUNIT_TEST(testFormulaRefUpdateMoveToSheet);
352     CPPUNIT_TEST(testFormulaRefUpdateDeleteContent);
353     CPPUNIT_TEST(testFormulaRefUpdateDeleteAndShiftLeft);
354     CPPUNIT_TEST(testFormulaRefUpdateDeleteAndShiftLeft2);
355     CPPUNIT_TEST(testFormulaRefUpdateDeleteAndShiftUp);
356     CPPUNIT_TEST(testFormulaRefUpdateName);
357     CPPUNIT_TEST(testFormulaRefUpdateNameMove);
358     CPPUNIT_TEST(testFormulaRefUpdateNameExpandRef);
359     CPPUNIT_TEST(testFormulaRefUpdateNameExpandRef2);
360     CPPUNIT_TEST(testFormulaRefUpdateNameDeleteRow);
361     CPPUNIT_TEST(testFormulaRefUpdateNameCopySheet);
362     CPPUNIT_TEST(testFormulaRefUpdateSheetLocalMove);
363     CPPUNIT_TEST(testFormulaRefUpdateNameDelete);
364     CPPUNIT_TEST(testFormulaRefUpdateValidity);
365     CPPUNIT_TEST(testTokenArrayRefUpdateMove);
366     CPPUNIT_TEST(testSingleCellCopyColumnLabel);
367     CPPUNIT_TEST(testIntersectionOpExcel);
368     CPPUNIT_TEST(testTdf97369);
369     CPPUNIT_TEST(testTdf97587);
370     CPPUNIT_TEST(testTdf93415);
371     CPPUNIT_TEST(testTdf100818);
372     CPPUNIT_TEST(testMatConcat);
373     CPPUNIT_TEST(testMatConcatReplication);
374     CPPUNIT_TEST(testExternalRef);
375     CPPUNIT_TEST(testFormulaDepTracking);
376     CPPUNIT_TEST(testFormulaDepTracking2);
377     CPPUNIT_TEST(testFormulaDepTracking3);
378     CPPUNIT_TEST(testFormulaDepTrackingDeleteRow);
379     CPPUNIT_TEST(testFormulaDepTrackingDeleteCol);
380     CPPUNIT_TEST(testFormulaMatrixResultUpdate);
381     CPPUNIT_TEST(testExternalRefFunctions);
382     CPPUNIT_TEST(testExternalRangeName);
383     CPPUNIT_TEST(testExternalRefUnresolved);
384     CPPUNIT_TEST(testRefR1C1WholeCol);
385     CPPUNIT_TEST(testRefR1C1WholeRow);
386     CPPUNIT_TEST(testIterations);
387     CPPUNIT_TEST(testInsertColCellStoreEventSwap);
388     CPPUNIT_TEST(testFormulaAfterDeleteRows);
389     CPPUNIT_TEST(testMultipleOperations);
390     CPPUNIT_TEST(testFuncCOLUMN);
391     CPPUNIT_TEST(testFuncCOUNT);
392     CPPUNIT_TEST(testFuncCOUNTBLANK);
393     CPPUNIT_TEST(testFuncROW);
394     CPPUNIT_TEST(testFuncSUM);
395     CPPUNIT_TEST(testFuncPRODUCT);
396     CPPUNIT_TEST(testFuncSUMPRODUCT);
397     CPPUNIT_TEST(testFuncSUMXMY2);
398     CPPUNIT_TEST(testFuncMIN);
399     CPPUNIT_TEST(testFuncN);
400     CPPUNIT_TEST(testFuncCOUNTIF);
401     CPPUNIT_TEST(testFuncNUMBERVALUE);
402     CPPUNIT_TEST(testFuncLEN);
403     CPPUNIT_TEST(testFuncLOOKUP);
404     CPPUNIT_TEST(testFuncLOOKUParrayWithError);
405     CPPUNIT_TEST(testTdf141146);
406     CPPUNIT_TEST(testFuncVLOOKUP);
407     CPPUNIT_TEST(testFuncMATCH);
408     CPPUNIT_TEST(testFuncCELL);
409     CPPUNIT_TEST(testFuncDATEDIF);
410     CPPUNIT_TEST(testFuncINDIRECT);
411     CPPUNIT_TEST(testFuncINDIRECT2);
412     CPPUNIT_TEST(testFunc_MATCH_INDIRECT);
413     CPPUNIT_TEST(testFuncIF);
414     CPPUNIT_TEST(testFuncCHOOSE);
415     CPPUNIT_TEST(testFuncIFERROR);
416     CPPUNIT_TEST(testFuncSHEET);
417     CPPUNIT_TEST(testFuncNOW);
418     CPPUNIT_TEST(testMatrixOp);
419     CPPUNIT_TEST(testFuncRangeOp);
420     CPPUNIT_TEST(testFuncFORMULA);
421     CPPUNIT_TEST(testFuncTableRef);
422     CPPUNIT_TEST(testFuncFTEST);
423     CPPUNIT_TEST(testFuncFTESTBug);
424     CPPUNIT_TEST(testFuncCHITEST);
425     CPPUNIT_TEST(testFuncTTEST);
426     CPPUNIT_TEST(testFuncSUMX2PY2);
427     CPPUNIT_TEST(testFuncSUMX2MY2);
428     CPPUNIT_TEST(testFuncGCD);
429     CPPUNIT_TEST(testFuncLCM);
430     CPPUNIT_TEST(testFuncSUMSQ);
431     CPPUNIT_TEST(testFuncMDETERM);
432     CPPUNIT_TEST(testFormulaErrorPropagation);
433     CPPUNIT_TEST(testFuncRowsHidden);
434     CPPUNIT_TEST(testFuncSUMIFS);
435     CPPUNIT_TEST(testFuncRefListArraySUBTOTAL);
436     CPPUNIT_TEST(testFuncJumpMatrixArrayIF);
437     CPPUNIT_TEST(testFuncJumpMatrixArrayOFFSET);
438 
439     CPPUNIT_TEST_SUITE_END();
440 
441 private:
442     ScDocShellRef m_xDocShell;
443     ScDocument* m_pDoc;
444 };
445 
TestFormula()446 TestFormula::TestFormula()
447 {
448 }
449 
setUp()450 void TestFormula::setUp()
451 {
452     BootstrapFixture::setUp();
453 
454     ScDLL::Init();
455 
456     m_xDocShell = new ScDocShell(
457         SfxModelFlags::EMBEDDED_OBJECT |
458         SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
459         SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
460     m_xDocShell->SetIsInUcalc();
461     m_xDocShell->DoInitUnitTest();
462 
463     m_pDoc = &m_xDocShell->GetDocument();
464 }
465 
tearDown()466 void TestFormula::tearDown()
467 {
468     m_xDocShell->DoClose();
469     m_xDocShell.clear();
470 
471     test::BootstrapFixture::tearDown();
472 }
473 
testFormulaCreateStringFromTokens()474 void TestFormula::testFormulaCreateStringFromTokens()
475 {
476     // Insert sheets.
477     m_pDoc->InsertTab(0, "Test");
478     m_pDoc->InsertTab(1, "Kevin's Data");
479     m_pDoc->InsertTab(2, "Past Data");
480     m_pDoc->InsertTab(3, "2013");
481 
482     // Insert named ranges.
483     static const struct {
484         bool bGlobal;
485         const char* pName;
486         const char* pExpr;
487     } aNames[] = {
488         { true, "x", "Test.H1" },
489         { true, "y", "Test.H2" },
490         { true, "z", "Test.H3" },
491 
492         { false, "sheetx", "Test.J1" }
493     };
494 
495     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
496     ScRangeName* pSheetNames = m_pDoc->GetRangeName(0);
497     CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
498     CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames);
499 
500     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
501     {
502         ScRangeData* pName = new ScRangeData(
503             *m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
504             ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
505 
506         if (aNames[i].bGlobal)
507         {
508             bool bInserted = pGlobalNames->insert(pName);
509             CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
510         }
511         else
512         {
513             bool bInserted = pSheetNames->insert(pName);
514             CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
515         }
516     }
517 
518     // Insert DB ranges.
519     static const struct {
520         const char* pName;
521         SCTAB nTab;
522         SCCOL nCol1;
523         SCROW nRow1;
524         SCCOL nCol2;
525         SCROW nRow2;
526     } aDBs[] = {
527         { "Table1", 0, 0, 0, 10, 10 },
528         { "Table2", 1, 0, 0, 10, 10 },
529         { "Table3", 2, 0, 0, 10, 10 }
530     };
531 
532     ScDBCollection* pDBs = m_pDoc->GetDBCollection();
533     CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
534 
535     for (size_t i = 0; i < SAL_N_ELEMENTS(aDBs); ++i)
536     {
537         std::unique_ptr<ScDBData> pData( new ScDBData(
538             OUString::createFromAscii(
539                 aDBs[i].pName), aDBs[i].nTab, aDBs[i].nCol1, aDBs[i].nRow1, aDBs[i].nCol2,aDBs[i].nRow2) );
540         bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
541         CPPUNIT_ASSERT_MESSAGE(
542             OString(
543                 OString::Concat("Failed to insert \"") + aDBs[i].pName + "\"").getStr(),
544             bInserted);
545     }
546 
547     const char* aTests[] = {
548         "1+2",
549         "SUM(A1:A10;B1:B10;C5;D6)",
550         "IF(Test.B10<>10;\"Good\";\"Bad\")",
551         "AVERAGE('2013'.B10:C20)",
552         "'Kevin''s Data'.B10",
553         "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
554         "x+y*z", // named ranges
555         "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
556         "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
557         "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
558         "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
559     };
560     (void) aTests;
561 
562     sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
563 
564     // Artificially add external reference data after the context object is
565     // initialized.
566     aCxt.maExternalFileNames.emplace_back("file:///path/to/fake.file");
567     std::vector<OUString> aExtTabNames;
568     aExtTabNames.emplace_back("Sheet");
569     aCxt.maExternalCachedTabNames.emplace(0, aExtTabNames);
570 
571     ScAddress aPos(0,0,0);
572 
573     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
574     {
575 #if 0
576         OUString aFormula = OUString::createFromAscii(aTests[i]);
577 #endif
578         ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH);
579 #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
580         pArray.reset(aComp.CompileString(aFormula));
581         CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray.get());
582 
583         OUString aCheck = pArray->CreateString(aCxt, aPos);
584         CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
585 #endif
586     }
587 
588     m_pDoc->DeleteTab(3);
589     m_pDoc->DeleteTab(2);
590     m_pDoc->DeleteTab(1);
591     m_pDoc->DeleteTab(0);
592 }
593 
594 namespace {
595 
isEmpty(const formula::VectorRefArray & rArray,size_t nPos)596 bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
597 {
598     if (rArray.mpStringArray)
599     {
600         if (rArray.mpStringArray[nPos])
601             return false;
602     }
603 
604     if (rArray.mpNumericArray)
605         return std::isnan(rArray.mpNumericArray[nPos]);
606     else
607         return true;
608 }
609 
equals(const formula::VectorRefArray & rArray,size_t nPos,double fVal)610 bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
611 {
612     if (rArray.mpStringArray && rArray.mpStringArray[nPos])
613         // This is a string cell.
614         return false;
615 
616     return rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal;
617 }
618 
equals(const formula::VectorRefArray & rArray,size_t nPos,const OUString & rVal)619 bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
620 {
621     if (!rArray.mpStringArray)
622         return false;
623 
624     bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
625     if (!bEquals)
626     {
627         cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
628         cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
629     }
630     return bEquals;
631 }
632 
633 }
634 
testFormulaParseReference()635 void TestFormula::testFormulaParseReference()
636 {
637     OUString aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
638     m_pDoc->InsertTab(0, "Dummy"); // just to shift the sheet indices...
639     m_pDoc->InsertTab(1, aTab1); // name with a single quote.
640     m_pDoc->InsertTab(2, aTab2); // name with 2 single quotes.
641     m_pDoc->InsertTab(3, aTab3); // name without single quotes.
642     m_pDoc->InsertTab(4, aTab4); // name that doesn't require to be quoted.
643 
644     OUString aTabName;
645     m_pDoc->GetName(1, aTabName);
646     CPPUNIT_ASSERT_EQUAL(aTab1, aTabName);
647     m_pDoc->GetName(2, aTabName);
648     CPPUNIT_ASSERT_EQUAL(aTab2, aTabName);
649     m_pDoc->GetName(3, aTabName);
650     CPPUNIT_ASSERT_EQUAL(aTab3, aTabName);
651     m_pDoc->GetName(4, aTabName);
652     CPPUNIT_ASSERT_EQUAL(aTab4, aTabName);
653 
654     // Make sure the formula input and output match.
655     {
656         const char* aChecks[] = {
657             "'90''s Music'.B12",
658             "'90''s and 70''s'.$AB$100",
659             "'All Others'.Z$100",
660             "NoQuote.$C111"
661         };
662 
663         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
664         {
665             // Use the 'Dummy' sheet for this.
666             OUString aInput = "=" + OUString::createFromAscii(aChecks[i]);
667             m_pDoc->SetString(ScAddress(0,0,0), aInput);
668             ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), aChecks[i], "Wrong formula");
669         }
670     }
671 
672     ScAddress aPos;
673     ScAddress::ExternalInfo aExtInfo;
674     ScRefFlags nRes = aPos.Parse("'90''s Music'.D10", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
675     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
676     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(1), aPos.Tab());
677     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), aPos.Col());
678     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), aPos.Row());
679     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
680 
681     nRes = aPos.Parse("'90''s and 70''s'.C100", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
682     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
683     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(2), aPos.Tab());
684     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aPos.Col());
685     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(99), aPos.Row());
686     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
687 
688     nRes = aPos.Parse("'All Others'.B3", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
689     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
690     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(3), aPos.Tab());
691     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aPos.Col());
692     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), aPos.Row());
693     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
694 
695     nRes = aPos.Parse("NoQuote.E13", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
696     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
697     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aPos.Tab());
698     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(4), aPos.Col());
699     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), aPos.Row());
700     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
701 
702     ScRange aRange;
703 
704     aRange.aStart.SetTab(0);
705     nRes = aRange.Parse(":B", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
706     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
707 
708     aRange.aStart.SetTab(0);
709     nRes = aRange.Parse("B:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
710     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
711 
712     aRange.aStart.SetTab(0);
713     nRes = aRange.Parse(":B2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
714     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
715 
716     aRange.aStart.SetTab(0);
717     nRes = aRange.Parse("B2:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
718     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
719 
720     aRange.aStart.SetTab(0);
721     nRes = aRange.Parse(":2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
722     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
723 
724     aRange.aStart.SetTab(0);
725     nRes = aRange.Parse("2:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
726     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
727 
728     aRange.aStart.SetTab(0);
729     nRes = aRange.Parse(":2B", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
730     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
731 
732     aRange.aStart.SetTab(0);
733     nRes = aRange.Parse("2B:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
734     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
735 
736     aRange.aStart.SetTab(0);
737     nRes = aRange.Parse("abc_foo:abc_bar", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
738     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
739 
740     aRange.aStart.SetTab(0);
741     nRes = aRange.Parse("B1:B2~C1", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
742     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
743 
744     aRange.aStart.SetTab(0);
745     nRes = aRange.Parse("B:B", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
746     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
747     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
748     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
749     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
750     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
751     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
752     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
753     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
754                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
755                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
756                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
757     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
758                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
759     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
760                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
761 
762     aRange.aStart.SetTab(0);
763     nRes = aRange.Parse("2:2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
764     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
765     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
766     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
767     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
768     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
769     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
770     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
771     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
772                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
773                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
774                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
775     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
776                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
777     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
778                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
779 
780     nRes = aRange.Parse("NoQuote.B:C", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
781     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
782     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aRange.aStart.Tab());
783     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
784     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
785     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aRange.aEnd.Tab());
786     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aRange.aEnd.Col());
787     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
788     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
789                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
790                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
791                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
792     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
793                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
794     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
795                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
796 
797     // Both rows at sheet bounds and relative => convert to absolute => entire column reference.
798     aRange.aStart.SetTab(0);
799     nRes = aRange.Parse("B1:B1048576", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
800     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
801     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
802     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
803     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
804     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
805     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
806     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
807     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
808                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
809                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
810                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
811     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
812                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
813     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
814                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
815 
816     // Both columns at sheet bounds and relative => convert to absolute => entire row reference.
817     aRange.aStart.SetTab(0);
818     nRes = aRange.Parse("A2:AMJ2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
819     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
820     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
821     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
822     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
823     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
824     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
825     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
826     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
827                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
828                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
829                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
830     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
831                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
832     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
833                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
834 
835     // Check for reference input conversion to and display string of entire column/row.
836     {
837         const char* aChecks[][2] = {
838             { "=B:B",           "B:B" },
839             { "=B1:B1048576",   "B:B" },
840             { "=B1:B$1048576",  "B1:B$1048576" },
841             { "=B$1:B1048576",  "B$1:B1048576" },
842             { "=B$1:B$1048576", "B:B" },
843             { "=2:2",           "2:2" },
844             { "=A2:AMJ2",       "2:2" },
845             { "=A2:$AMJ2",      "A2:$AMJ2" },
846             { "=$A2:AMJ2",      "$A2:AMJ2" },
847             { "=$A2:$AMJ2",     "2:2" }
848         };
849 
850         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
851         {
852             // Use the 'Dummy' sheet for this.
853             m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(aChecks[i][0]));
854             ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), aChecks[i][1], "Wrong formula");
855         }
856     }
857 
858     m_pDoc->DeleteTab(4);
859     m_pDoc->DeleteTab(3);
860     m_pDoc->DeleteTab(2);
861     m_pDoc->DeleteTab(1);
862     m_pDoc->DeleteTab(0);
863 }
864 
testFetchVectorRefArray()865 void TestFormula::testFetchVectorRefArray()
866 {
867     m_pDoc->InsertTab(0, "Test");
868 
869     // All numeric cells in Column A.
870     m_pDoc->SetValue(ScAddress(0,0,0), 1);
871     m_pDoc->SetValue(ScAddress(0,1,0), 2);
872     m_pDoc->SetValue(ScAddress(0,2,0), 3);
873     m_pDoc->SetValue(ScAddress(0,3,0), 4);
874 
875     formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 4);
876     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
877     CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
878     CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
879     CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
880     CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
881     CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
882 
883     aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 5);
884     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
885     CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
886     CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
887     CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
888     CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
889     CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
890     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
891 
892     // All string cells in Column B.  Note that the fetched string arrays are
893     // only to be compared case-insensitively.  Right now, we use upper cased
894     // strings to achieve case-insensitive-ness, but that may change. So,
895     // don't count on that.
896     m_pDoc->SetString(ScAddress(1,0,0), "Andy");
897     m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
898     m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
899     m_pDoc->SetString(ScAddress(1,3,0), "David");
900     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 5);
901     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
902     CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
903     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Andy"));
904     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Bruce"));
905     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "Charlie"));
906     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "David"));
907     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
908 
909     // Mixture of numeric, string, and empty cells in Column C.
910     m_pDoc->SetString(ScAddress(2,0,0), "Header");
911     m_pDoc->SetValue(ScAddress(2,1,0), 11);
912     m_pDoc->SetValue(ScAddress(2,2,0), 12);
913     m_pDoc->SetValue(ScAddress(2,3,0), 13);
914     m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
915     m_pDoc->CalcAll();
916 
917     aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,0,0), 7);
918     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
919     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
920     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
921     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Header"));
922     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 11));
923     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 12));
924     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 13));
925     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
926     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 36));
927     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
928 
929     // Mixed type again in Column D, but it starts with a numeric cell.
930     m_pDoc->SetValue(ScAddress(3,0,0), 10);
931     m_pDoc->SetString(ScAddress(3,1,0), "Below 10");
932     // Leave 2 empty cells.
933     m_pDoc->SetValue(ScAddress(3,4,0), 11);
934     m_pDoc->SetString(ScAddress(3,5,0), "=12");
935     m_pDoc->SetString(ScAddress(3,6,0), "=13");
936     m_pDoc->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
937     m_pDoc->CalcAll();
938 
939     aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,0,0), 8);
940     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
941     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
942     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
943     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 10));
944     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Below 10"));
945     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 2));
946     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
947     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 11));
948     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 12));
949     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13));
950     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC"));
951 
952     // Column E consists of formula cells whose results are all numeric.
953     for (SCROW i = 0; i <= 6; ++i)
954         m_pDoc->SetString(ScAddress(4,i,0), "=ROW()");
955     m_pDoc->CalcAll();
956 
957     // Leave row 7 empty.
958     m_pDoc->SetString(ScAddress(4,8,0), "Andy");
959     m_pDoc->SetValue(ScAddress(4,9,0), 123);
960 
961     // This array fits within a single formula block.
962     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 5);
963     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
964     CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray);
965     CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", !aArray.mpStringArray);
966     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
967     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
968     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
969     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
970     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
971 
972     // This array spans over multiple blocks.
973     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 11);
974     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
975     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
976     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
977     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
978     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
979     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
980     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
981     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
982     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6));
983     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7));
984     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
985     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy"));
986     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123));
987     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10));
988 
989     // Hit the cache but at a different start row.
990     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 3);
991     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
992     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
993     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3));
994     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4));
995     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5));
996 
997     // Column F begins with empty rows at the top.
998     m_pDoc->SetValue(ScAddress(5,2,0), 1.1);
999     m_pDoc->SetValue(ScAddress(5,3,0), 1.2);
1000     m_pDoc->SetString(ScAddress(5,4,0), "=2*8");
1001     m_pDoc->CalcAll();
1002 
1003     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 4);
1004     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1005     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
1006     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1));
1007     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2));
1008     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16));
1009     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
1010 
1011     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 3);
1012     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1013     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
1014     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
1015     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
1016     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
1017 
1018     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 10);
1019     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1020     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
1021     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
1022     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
1023     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
1024     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2));
1025     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16));
1026     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5));
1027     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
1028     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
1029     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8));
1030     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9));
1031 
1032     // Get the array for F3:F4. This array should only consist of numeric array.
1033     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 3);
1034     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1035     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1036     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1037 
1038     // Column G consists only of strings.
1039     m_pDoc->SetString(ScAddress(6,0,0), "Title");
1040     m_pDoc->SetString(ScAddress(6,1,0), "foo");
1041     m_pDoc->SetString(ScAddress(6,2,0), "bar");
1042     m_pDoc->SetString(ScAddress(6,3,0), "foo");
1043     m_pDoc->SetString(ScAddress(6,4,0), "baz");
1044     m_pDoc->SetString(ScAddress(6,5,0), "quack");
1045     m_pDoc->SetString(ScAddress(6,6,0), "beep");
1046     m_pDoc->SetString(ScAddress(6,7,0), "kerker");
1047 
1048     aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
1049     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1050     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
1051     CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
1052     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "foo"));
1053     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "bar"));
1054     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "foo"));
1055     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "baz"));
1056 
1057     aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
1058     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1059     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
1060     CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
1061     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "bar"));
1062     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "foo"));
1063     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "baz"));
1064     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "quack"));
1065 
1066     // Column H starts with formula cells.
1067     for (SCROW i = 0; i < 10; ++i)
1068         m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
1069 
1070     m_pDoc->CalcAll();
1071     aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
1072     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1073     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1074     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1075     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 4.0));
1076     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, 5.0));
1077     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, 6.0));
1078 
1079     aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
1080     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1081     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1082     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1083     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 5.0));
1084 
1085     // Clear everything and start over.
1086     clearRange(m_pDoc, ScRange(0,0,0,m_pDoc->MaxCol(),m_pDoc->MaxRow(),0));
1087     m_pDoc->PrepareFormulaCalc();
1088 
1089     // Totally empty range in a totally empty column (Column A).
1090     aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
1091     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1092     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1093     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1094     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1095     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1096 
1097     // Totally empty range in a non-empty column (Column B).
1098     m_pDoc->SetString(ScAddress(1,10,0), "Some text"); // B11
1099     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
1100     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1101     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1102     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1103     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1104     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1105 
1106     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
1107     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1108     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1109     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1110     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1111     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1112 
1113     // These values come from a cache because of the call above.
1114     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
1115     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1116     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1117     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1118     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1119     CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1120 
1121     // The column begins with a string header at row 1 (Column C).
1122     m_pDoc->SetString(ScAddress(2,0,0), "MyHeader");
1123     for (SCROW i = 1; i <= 9; ++i) // rows 2-10 are numeric.
1124         m_pDoc->SetValue(ScAddress(2,i,0), i);
1125 
1126     aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,1,0), 9); // C2:C10
1127     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1128     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1129     for (size_t i = 0; i < 9; ++i)
1130         CPPUNIT_ASSERT_EQUAL(double(i+1), aArray.mpNumericArray[i]);
1131 
1132     // The column begins with a number, followed by a string then followed by
1133     // a block of numbers (Column D).
1134     m_pDoc->SetValue(ScAddress(3,0,0), 0.0);
1135     m_pDoc->SetString(ScAddress(3,1,0), "Some string");
1136     for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
1137         m_pDoc->SetValue(ScAddress(3,i,0), i);
1138 
1139     aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,2,0), 8); // D3:D10
1140     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1141     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1142     for (size_t i = 0; i < 8; ++i)
1143         CPPUNIT_ASSERT_EQUAL(double(i+2), aArray.mpNumericArray[i]);
1144 
1145     // The column begins with a formula, followed by a string then followed by
1146     // a block of numbers (Column E).
1147     m_pDoc->SetString(ScAddress(4,0,0), "=1*2");
1148     m_pDoc->SetString(ScAddress(4,1,0), "Some string");
1149     for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
1150         m_pDoc->SetValue(ScAddress(4,i,0), i*2);
1151 
1152     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 8); // E3:E10
1153     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1154     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1155     for (size_t i = 0; i < 8; ++i)
1156         CPPUNIT_ASSERT_EQUAL(double((i+2)*2), aArray.mpNumericArray[i]);
1157 
1158     m_pDoc->DeleteTab(0);
1159 }
1160 
testGroupConverter3D()1161 void TestFormula::testGroupConverter3D()
1162 {
1163     m_pDoc->InsertTab(0, "Test");
1164     m_pDoc->InsertTab(1, "Test2");
1165 
1166     m_pDoc->SetValue(1, 0, 0, 1.0);
1167     m_pDoc->SetValue(1, 0, 1, 2.0);
1168 
1169     for (SCROW nRow = 0; nRow < 200; ++nRow)
1170     {
1171         OUString aFormula = "=SUM(Test.B" + OUString::number(nRow+1) + ":Test2.B" + OUString::number(nRow+1) + ")";
1172         m_pDoc->SetString(0, nRow, 0, aFormula);
1173     }
1174 
1175     double nVal = m_pDoc->GetValue(0, 0, 0);
1176     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
1177 
1178     m_pDoc->DeleteTab(1);
1179     m_pDoc->DeleteTab(0);
1180 }
1181 
testFormulaTokenEquality()1182 void TestFormula::testFormulaTokenEquality()
1183 {
1184     struct FormulaTokenEqualityTest
1185     {
1186         const char* mpFormula1;
1187         const char* mpFormula2;
1188         bool mbEqual;
1189     };
1190 
1191     static const FormulaTokenEqualityTest aTests[] = {
1192         { "R1C2", "R1C2", true },
1193         { "R1C2", "R1C3", false },
1194         { "R1C2", "R2C2", false },
1195         { "RC2",  "RC[1]", false },
1196         { "R1C2:R10C2", "R1C2:R10C2", true },
1197         { "R1C2:R10C2", "R1C2:R11C2", false },
1198         { "1", "2", false },
1199         { "RC[1]+1.2", "RC[1]+1.2", true },
1200         { "RC[1]*0.2", "RC[1]*0.5", false },
1201         { "\"Test1\"", "\"Test2\"", false },
1202         { "\"Test\"", "\"Test\"", true },
1203         { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
1204         { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
1205     };
1206 
1207     formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
1208     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
1209     {
1210         ScFormulaCell aCell1(*m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
1211         ScFormulaCell aCell2(*m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
1212 
1213         ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
1214         if (aTests[i].mbEqual)
1215         {
1216             if (eComp == ScFormulaCell::NotEqual)
1217             {
1218                 std::ostringstream os;
1219                 os << "These two formulas should be evaluated equal: '"
1220                     << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
1221                 CPPUNIT_FAIL(os.str());
1222             }
1223         }
1224         else
1225         {
1226             if (eComp != ScFormulaCell::NotEqual)
1227             {
1228                 std::ostringstream os;
1229                 os << "These two formulas should be evaluated non-equal: '"
1230                     << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
1231                 CPPUNIT_FAIL(os.str());
1232             }
1233         }
1234     }
1235 }
1236 
testFormulaRefData()1237 void TestFormula::testFormulaRefData()
1238 {
1239     std::unique_ptr<ScDocument> pDoc = std::make_unique<ScDocument>();
1240 
1241     ScAddress aAddr(4,5,3), aPos(2,2,2);
1242     ScSingleRefData aRef;
1243     aRef.InitAddress(aAddr);
1244     CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsRowRel());
1245     CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsColRel());
1246     CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsTabRel());
1247     CPPUNIT_ASSERT_EQUAL(SCCOL(4), aRef.Col());
1248     CPPUNIT_ASSERT_EQUAL(SCROW(5), aRef.Row());
1249     CPPUNIT_ASSERT_EQUAL(SCTAB(3), aRef.Tab());
1250 
1251     aRef.SetRowRel(true);
1252     aRef.SetColRel(true);
1253     aRef.SetTabRel(true);
1254     aRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos);
1255     CPPUNIT_ASSERT_EQUAL(SCCOL(2), aRef.Col());
1256     CPPUNIT_ASSERT_EQUAL(SCROW(3), aRef.Row());
1257     CPPUNIT_ASSERT_EQUAL(SCTAB(1), aRef.Tab());
1258 
1259     // Test extension of range reference.
1260 
1261     ScComplexRefData aDoubleRef;
1262     aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
1263 
1264     aRef.InitAddress(ScAddress(6,5,0));
1265 
1266     aDoubleRef.Extend(pDoc->GetSheetLimits(), aRef, ScAddress());
1267     ScRange aTest = aDoubleRef.toAbs(*pDoc, ScAddress());
1268     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(2,2,0), aTest.aStart);
1269     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(6,5,0), aTest.aEnd);
1270 
1271     ScComplexRefData aDoubleRef2;
1272     aDoubleRef2.InitRangeRel(*pDoc, ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
1273     aDoubleRef.Extend(pDoc->GetSheetLimits(), aDoubleRef2, ScAddress(5,5,0));
1274     aTest = aDoubleRef.toAbs(*pDoc, ScAddress(5,5,0));
1275 
1276     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(1,2,0), aTest.aStart);
1277     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(8,6,0), aTest.aEnd);
1278 }
1279 
testFormulaCompiler()1280 void TestFormula::testFormulaCompiler()
1281 {
1282     static const struct {
1283         const char* pInput; FormulaGrammar::Grammar eInputGram;
1284         const char* pOutput; FormulaGrammar::Grammar eOutputGram;
1285     } aTests[] = {
1286         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF },
1287         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE },
1288         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1 },
1289         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1 },
1290     };
1291 
1292     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
1293     {
1294         std::unique_ptr<ScTokenArray> pArray = compileFormula(m_pDoc, OUString::createFromAscii(aTests[i].pInput), aTests[i].eInputGram);
1295         CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray);
1296 
1297         OUString aFormula = toString(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
1298         CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
1299     }
1300 }
1301 
testFormulaCompilerJumpReordering()1302 void TestFormula::testFormulaCompilerJumpReordering()
1303 {
1304     struct TokenCheck
1305     {
1306         OpCode meOp;
1307         StackVar meType;
1308     };
1309 
1310     {
1311         // Compile formula string first.
1312         std::unique_ptr<ScTokenArray> pCode(compileFormula(m_pDoc, "=IF(B1;12;\"text\")"));
1313         CPPUNIT_ASSERT(pCode);
1314 
1315         // Then generate RPN tokens.
1316         ScCompiler aCompRPN(*m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1317         aCompRPN.CompileTokenArray();
1318 
1319         // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
1320         static const TokenCheck aCheckRPN[] =
1321         {
1322             { ocPush,  svSingleRef },
1323             { ocIf,    svUnknown   }, // type is context dependent, don't test it
1324             { ocPush,  svDouble    },
1325             { ocSep,   svSep       },
1326             { ocPush,  svString    },
1327             { ocClose, svSep       },
1328         };
1329 
1330         sal_uInt16 nLen = pCode->GetCodeLen();
1331         CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN)), nLen);
1332 
1333         FormulaToken** ppTokens = pCode->GetCode();
1334         for (sal_uInt16 i = 0; i < nLen; ++i)
1335         {
1336             const FormulaToken* p = ppTokens[i];
1337             CPPUNIT_ASSERT_EQUAL(aCheckRPN[i].meOp, p->GetOpCode());
1338             if (aCheckRPN[i].meOp != ocIf )
1339                 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN[i].meType), static_cast<int>(p->GetType()));
1340         }
1341 
1342         // Generate RPN tokens again, but this time no jump command reordering.
1343         pCode->DelRPN();
1344         ScCompiler aCompRPN2(*m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1345         aCompRPN2.EnableJumpCommandReorder(false);
1346         aCompRPN2.CompileTokenArray();
1347 
1348         static const TokenCheck aCheckRPN2[] =
1349         {
1350             { ocPush,  svSingleRef },
1351             { ocPush,  svDouble    },
1352             { ocPush,  svString    },
1353             { ocIf,    svUnknown   }, // type is context dependent, don't test it
1354         };
1355 
1356         nLen = pCode->GetCodeLen();
1357         CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN2)), nLen);
1358         ppTokens = pCode->GetCode();
1359         for (sal_uInt16 i = 0; i < nLen; ++i)
1360         {
1361             const FormulaToken* p = ppTokens[i];
1362             CPPUNIT_ASSERT_EQUAL(aCheckRPN2[i].meOp, p->GetOpCode());
1363             if (aCheckRPN[i].meOp == ocPush)
1364                 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN2[i].meType), static_cast<int>(p->GetType()));
1365         }
1366     }
1367 }
1368 
testFormulaCompilerImplicitIntersection2Param()1369 void TestFormula::testFormulaCompilerImplicitIntersection2Param()
1370 {
1371     struct TestCaseFormula
1372     {
1373         OUString  aFormula;
1374         ScAddress aCellAddress;
1375         ScRange   aSumRange;
1376         bool      bStartColRel;  // SumRange-StartCol
1377         bool      bEndColRel;    // SumRange-EndCol
1378     };
1379 
1380     m_pDoc->InsertTab(0, "Formula");
1381     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1382 
1383     {
1384         TestCaseFormula aTestCases[] =
1385         {
1386             // Formula, FormulaCellAddress, SumRange with Implicit Intersection
1387 
1388             // Sumrange is single cell, address is abs
1389             {
1390                 OUString("=SUMIF($B$2:$B$10;F2;$D$5)"),
1391                 ScAddress(7, 5, 0),
1392                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1393                 false,
1394                 false
1395             },
1396 
1397             // Sumrange is single cell, address is relative
1398             {
1399                 OUString("=SUMIF($B$2:$B$10;F2;D5)"),
1400                 ScAddress(7, 5, 0),
1401                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1402                 true,
1403                 true
1404             },
1405 
1406             // Baserange(abs,abs), Sumrange(abs,abs)
1407             {
1408                 OUString("=SUMIF($B$2:$B$10;F2;$D$5:$D$10)"),
1409                 ScAddress(7, 5, 0),
1410                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1411                 false,
1412                 false
1413             },
1414 
1415             // Baserange(abs,rel), Sumrange(abs,abs)
1416             {
1417                 OUString("=SUMIF($B$2:B10;F2;$D$5:$D$10)"),
1418                 ScAddress(7, 5, 0),
1419                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1420                 false,
1421                 false
1422             },
1423 
1424             // Baserange(rel,abs), Sumrange(abs,abs)
1425             {
1426                 OUString("=SUMIF(B2:$B$10;F2;$D$5:$D$10)"),
1427                 ScAddress(7, 5, 0),
1428                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1429                 false,
1430                 false
1431             },
1432 
1433             // Baserange(rel,rel), Sumrange(abs,abs)
1434             {
1435                 OUString("=SUMIF(B2:B10;F2;$D$5:$D$10)"),
1436                 ScAddress(7, 5, 0),
1437                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1438                 false,
1439                 false
1440             },
1441 
1442             // Baserange(abs,abs), Sumrange(abs,rel)
1443             {
1444                 OUString("=SUMIF($B$2:$B$10;F2;$D$5:D10)"),
1445                 ScAddress(7, 5, 0),
1446                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1447                 false,
1448                 true
1449             },
1450 
1451             // Baserange(abs,abs), Sumrange(rel,abs)
1452             {
1453                 OUString("=SUMIF($B$2:$B$10;F2;D5:$D$10)"),
1454                 ScAddress(7, 5, 0),
1455                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1456                 true,
1457                 false
1458             },
1459 
1460             // Baserange(abs,abs), Sumrange(rel,rel)
1461             {
1462                 OUString("=SUMIF($B$2:$B$10;F2;D5:D10)"),
1463                 ScAddress(7, 5, 0),
1464                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1465                 true,
1466                 true
1467             }
1468         };
1469 
1470         for (const auto& rCase : aTestCases)
1471         {
1472             m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1473             const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1474             const ScTokenArray* pCode = pCell->GetCode();
1475             CPPUNIT_ASSERT(pCode);
1476 
1477             sal_uInt16 nLen = pCode->GetCodeLen();
1478             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(4), nLen);
1479 
1480             FormulaToken** ppTokens = pCode->GetCode();
1481 
1482             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(first argument to SUMIF)", svDoubleRef, ppTokens[0]->GetType());
1483             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(third argument to SUMIF)", svDoubleRef, ppTokens[2]->GetType());
1484 
1485             ScComplexRefData aSumRangeData = *ppTokens[2]->GetDoubleRef();
1486             ScRange aSumRange = aSumRangeData.toAbs(*m_pDoc, rCase.aCellAddress);
1487             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sum-range in RPN array", rCase.aSumRange, aSumRange);
1488 
1489             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for start column address in sum-range", rCase.bStartColRel, aSumRangeData.Ref1.IsColRel());
1490             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for end column address in sum-range", rCase.bEndColRel, aSumRangeData.Ref2.IsColRel());
1491         }
1492     }
1493 }
1494 
testFormulaCompilerImplicitIntersection1ParamNoChange()1495 void TestFormula::testFormulaCompilerImplicitIntersection1ParamNoChange()
1496 {
1497     struct TestCaseFormulaNoChange
1498     {
1499         OUString  aFormula;
1500         ScAddress aCellAddress;
1501         bool      bMatrixFormula;
1502         bool      bForcedArray;
1503     };
1504 
1505     m_pDoc->InsertTab(0, "Formula");
1506     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1507 
1508     {
1509         ScAddress aStartAddr(4, 5, 0);
1510         TestCaseFormulaNoChange aCasesNoChange[] =
1511         {
1512             {
1513                 OUString("=COS(A$2:A$100)"),  // No change because of abs col ref.
1514                 aStartAddr,
1515                 false,
1516                 false
1517             },
1518             {
1519                 OUString("=COS($A7:$A100)"),  // No intersection
1520                 aStartAddr,
1521                 false,
1522                 false
1523             },
1524             {
1525                 OUString("=COS($A5:$C7)"),   // No intersection 2-D range
1526                 aStartAddr,
1527                 false,
1528                 false
1529             },
1530             {
1531                 OUString("=SUMPRODUCT(COS(A6:A10))"),  // COS() in forced array mode
1532                 aStartAddr,
1533                 false,
1534                 true
1535             },
1536             {
1537                 OUString("=COS(A6:A10)"),  // Matrix formula
1538                 aStartAddr,
1539                 true,
1540                 false
1541             }
1542         };
1543 
1544         for (const auto& rCase : aCasesNoChange)
1545         {
1546             if (rCase.bMatrixFormula)
1547             {
1548                 ScMarkData aMark(m_pDoc->GetSheetLimits());
1549                 aMark.SelectOneTable(0);
1550                 SCCOL nColStart = rCase.aCellAddress.Col();
1551                 SCROW nRowStart = rCase.aCellAddress.Row();
1552                 m_pDoc->InsertMatrixFormula(nColStart, nRowStart, nColStart, nRowStart + 4,
1553                                             aMark, rCase.aFormula);
1554             }
1555             else
1556                 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1557 
1558             const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1559             const ScTokenArray* pCode = pCell->GetCode();
1560             CPPUNIT_ASSERT(pCode);
1561 
1562             sal_uInt16 nRPNLen = pCode->GetCodeLen();
1563             sal_uInt16 nRawLen = pCode->GetLen();
1564             sal_uInt16 nRawArgPos;
1565             if (rCase.bForcedArray)
1566             {
1567                 nRawArgPos = 4;
1568                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(7), nRawLen);
1569                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(3), nRPNLen);
1570             }
1571             else
1572             {
1573                 nRawArgPos = 2;
1574                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(4), nRawLen);
1575                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1576             }
1577 
1578             FormulaToken** ppRawTokens = pCode->GetArray();
1579             FormulaToken** ppRPNTokens = pCode->GetCode();
1580 
1581             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of raw token(argument to COS)", svDoubleRef, ppRawTokens[nRawArgPos]->GetType());
1582             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svDoubleRef, ppRPNTokens[0]->GetType());
1583 
1584             ScComplexRefData aArgRangeRaw = *ppRawTokens[nRawArgPos]->GetDoubleRef();
1585             ScComplexRefData aArgRangeRPN = *ppRPNTokens[0]->GetDoubleRef();
1586             bool bRawMatchRPNToken(aArgRangeRaw == aArgRangeRPN);
1587             CPPUNIT_ASSERT_MESSAGE("raw arg token and RPN arg token contents do not match", bRawMatchRPNToken);
1588         }
1589     }
1590 }
1591 
testFormulaCompilerImplicitIntersection1ParamWithChange()1592 void TestFormula::testFormulaCompilerImplicitIntersection1ParamWithChange()
1593 {
1594     struct TestCaseFormula
1595     {
1596         OUString  aFormula;
1597         ScAddress aCellAddress;
1598         ScAddress aArgAddr;
1599     };
1600 
1601     m_pDoc->InsertTab(0, "Formula");
1602     m_pDoc->InsertTab(1, "Formula1");
1603     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1604 
1605     {
1606         ScAddress aStartAddr(10, 5, 0);
1607         TestCaseFormula aCasesWithChange[] =
1608         {
1609             {
1610                 OUString("=COS($A6:$A100)"),  // Corner case with intersection
1611                 aStartAddr,
1612                 ScAddress(0, 5, 0)
1613             },
1614             {
1615                 OUString("=COS($A2:$A6)"),    // Corner case with intersection
1616                 aStartAddr,
1617                 ScAddress(0, 5, 0)
1618             },
1619             {
1620                 OUString("=COS($A2:$A100)"),    // Typical 1D case
1621                 aStartAddr,
1622                 ScAddress(0, 5, 0)
1623             },
1624             {
1625                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1626                 ScAddress(0, 0, 1),                      // Formula in sheet 1
1627                 ScAddress(0, 0, 0)
1628             },
1629             {
1630                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1631                 ScAddress(0, 2, 1),                      // Formula in sheet 1
1632                 ScAddress(0, 2, 0)
1633             },
1634             {
1635                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1636                 ScAddress(2, 0, 1),                      // Formula in sheet 1
1637                 ScAddress(2, 0, 0)
1638             },
1639             {
1640                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1641                 ScAddress(2, 2, 1),                      // Formula in sheet 1
1642                 ScAddress(2, 2, 0)
1643             },
1644             {
1645                 OUString("=COS($Formula.$A1:$C3)"),      // Typical 2D case
1646                 ScAddress(1, 1, 1),                      // Formula in sheet 1
1647                 ScAddress(1, 1, 0)
1648             }
1649         };
1650 
1651         for (const auto& rCase : aCasesWithChange)
1652         {
1653             m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1654 
1655             const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1656             const ScTokenArray* pCode = pCell->GetCode();
1657             CPPUNIT_ASSERT(pCode);
1658 
1659             sal_uInt16 nRPNLen = pCode->GetCodeLen();
1660             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1661 
1662             FormulaToken** ppRPNTokens = pCode->GetCode();
1663 
1664             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef, ppRPNTokens[0]->GetType());
1665 
1666             ScSingleRefData aArgAddrRPN = *ppRPNTokens[0]->GetSingleRef();
1667             ScAddress aArgAddrActual = aArgAddrRPN.toAbs(*m_pDoc, rCase.aCellAddress);
1668             CPPUNIT_ASSERT_EQUAL_MESSAGE("Computed implicit intersection singleref is wrong", rCase.aArgAddr, aArgAddrActual);
1669         }
1670     }
1671 }
1672 
testFormulaCompilerImplicitIntersection1NoGroup()1673 void TestFormula::testFormulaCompilerImplicitIntersection1NoGroup()
1674 {
1675     m_pDoc->InsertTab(0, "Formula");
1676     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1677 
1678     m_pDoc->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
1679     m_pDoc->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
1680 
1681     // Implicit intersection optimization in ScCompiler::HandleIIOpCode() internally changes
1682     // these to "=COS(A3)" and "=COS(A4)", but these shouldn't be merged into a formula group,
1683     // otherwise B4's formula would then be "=COS(A2:A6)".
1684     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "COS(A1:A5)", "Formula in B3 has changed.");
1685     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,3,0), "COS(A1:A5)", "Formula in B4 has changed.");
1686 
1687     m_pDoc->DeleteTab(0);
1688 }
1689 
testFormulaCompilerImplicitIntersectionOperators()1690 void TestFormula::testFormulaCompilerImplicitIntersectionOperators()
1691 {
1692     struct TestCase
1693     {
1694         OUString formula[3];
1695         double result[3];
1696     };
1697 
1698     m_pDoc->InsertTab(0, "Test");
1699     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1700 
1701     m_pDoc->SetValue(2, 0, 0, 5); // C1
1702     m_pDoc->SetValue(2, 1, 0, 4); // C2
1703     m_pDoc->SetValue(2, 2, 0, 3); // C3
1704     m_pDoc->SetValue(3, 0, 0, 1); // D1
1705     m_pDoc->SetValue(3, 1, 0, 2); // D2
1706     m_pDoc->SetValue(3, 2, 0, 3); // D3
1707 
1708     TestCase tests[] =
1709     {
1710         { OUString("=C:C/D:D"), OUString("=C:C/D:D"), OUString("=C:C/D:D"), 5, 2, 1 },
1711         { OUString("=C1:C2/D1:D2"), OUString("=C2:C3/D2:D3"), OUString("=C3:C4/D3:D4"), 5, 2, 1 }
1712     };
1713 
1714     for (const TestCase& test : tests)
1715     {
1716         for(int i = 0; i < 2; ++i )
1717             m_pDoc->SetString(ScAddress(4,i,0), test.formula[i]); // E1-3
1718         for(int i = 0; i < 2; ++i )
1719             CPPUNIT_ASSERT_EQUAL_MESSAGE(OUString( test.formula[i] + " result incorrect in row " + OUString::number(i+1)).toUtf8().getStr(),
1720                 test.result[i], m_pDoc->GetValue(ScAddress(4,i,0)));
1721     }
1722 
1723     m_pDoc->DeleteTab(0);
1724 }
1725 
testFormulaAnnotateTrimOnDoubleRefs()1726 void TestFormula::testFormulaAnnotateTrimOnDoubleRefs()
1727 {
1728     m_pDoc->InsertTab(0, "Test");
1729     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1730 
1731     constexpr sal_Int32 nCols = 2;
1732     constexpr sal_Int32 nRows = 5;
1733 
1734     // Values in A1:B5
1735     constexpr sal_Int32 aMat[nRows][nCols] = {
1736         {4, 50},
1737         {5, 30},
1738         {4, 40},
1739         {0, 70},
1740         {5, 90}
1741     };
1742 
1743     for (sal_Int32 nCol = 0; nCol < nCols; ++nCol)
1744     {
1745         for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
1746             m_pDoc->SetValue(nCol, nRow, 0, aMat[nRow][nCol]);
1747     }
1748 
1749     m_pDoc->SetValue(2, 0, 0, 4); // C1 = 4
1750     m_pDoc->SetValue(3, 0, 0, 5); // D1 = 5
1751 
1752     ScMarkData aMark(m_pDoc->GetSheetLimits());
1753     aMark.SelectOneTable(0);
1754 
1755     struct TestCase
1756     {
1757         OUString aFormula;
1758         ScRange aTrimmableRange;
1759         double fResult;
1760         bool bMatrixFormula;
1761     };
1762 
1763     constexpr sal_Int32 nTestCases = 5;
1764     TestCase aTestCases[nTestCases] = {
1765         {
1766             "=SUM(IF($C$1=A:A;B:B)/10*D1)",
1767             ScRange(0, 0, 0, 0, 1048575, 0),
1768             45.0,
1769             true
1770         },
1771 
1772         {
1773             "=SUM(IF(A:A=5;B:B)/10*D1)",
1774             ScRange(0, 0, 0, 0, 1048575, 0),
1775             60.0,
1776             true
1777         },
1778 
1779         {
1780             "=SUM(IF($C$1=A:A;B:B;B:B)/10*D1)",  // IF has else clause
1781             ScRange(-1, -1, -1, -1, -1, -1),     // Has no trimmable double-ref.
1782             140.0,
1783             true
1784         },
1785 
1786         {
1787             "=SUM(IF($C$1=A:A;B:B)/10*D1)",
1788             ScRange(-1, -1, -1, -1, -1, -1),     // Has no trimmable double-ref.
1789             25,
1790             false                                // Not in matrix mode.
1791         },
1792 
1793         {
1794             "=SUMPRODUCT(A:A=$C$1; 1-(A:A=$C$1))",
1795             ScRange(-1, -1, -1, -1, -1, -1),     // Has no trimmable double-ref.
1796             0.0,
1797             false                                // Not in matrix mode.
1798         },
1799     };
1800 
1801     for (sal_Int32 nTestIdx = 0; nTestIdx < nTestCases; ++nTestIdx)
1802     {
1803         TestCase& rTestCase = aTestCases[nTestIdx];
1804         if (rTestCase.bMatrixFormula)
1805             m_pDoc->InsertMatrixFormula(4, 0, 4, 0, aMark, rTestCase.aFormula); // Formula in E1
1806         else
1807             m_pDoc->SetString(ScAddress(4, 0, 0), rTestCase.aFormula);          // Formula in E1
1808 
1809         std::string aMsgStart = "TestCase#" + std::to_string(nTestIdx + 1) + " : ";
1810         CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsgStart + "Incorrect formula result", rTestCase.fResult, m_pDoc->GetValue(ScAddress(4, 0, 0)));
1811 
1812         ScTokenArray* pCode = getTokens(*m_pDoc, ScAddress(4, 0, 0));
1813         sal_Int32 nLen = pCode->GetCodeLen();
1814         FormulaToken** pRPNArray = pCode->GetCode();
1815 
1816         for (sal_Int32 nIdx = 0; nIdx < nLen; ++nIdx)
1817         {
1818             FormulaToken* pTok = pRPNArray[nIdx];
1819             if (pTok && pTok->GetType() == svDoubleRef)
1820             {
1821                 ScRange aRange = pTok->GetDoubleRef()->toAbs(*m_pDoc, ScAddress(4, 0, 0));
1822                 if (aRange == rTestCase.aTrimmableRange)
1823                     CPPUNIT_ASSERT_MESSAGE(aMsgStart + "Double ref is incorrectly flagged as not trimmable to data",
1824                         pTok->GetDoubleRef()->IsTrimToData());
1825                 else
1826                     CPPUNIT_ASSERT_MESSAGE(aMsgStart + "Double ref is incorrectly flagged as trimmable to data",
1827                         !pTok->GetDoubleRef()->IsTrimToData());
1828             }
1829         }
1830     }
1831 
1832     m_pDoc->DeleteTab(0);
1833 }
1834 
testFormulaRefUpdate()1835 void TestFormula::testFormulaRefUpdate()
1836 {
1837     m_pDoc->InsertTab(0, "Formula");
1838 
1839     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1840 
1841     m_pDoc->SetValue(ScAddress(0,0,0), 2.0); // A1
1842     m_pDoc->SetString(ScAddress(2,2,0), "=A1");   // C3
1843     m_pDoc->SetString(ScAddress(2,3,0), "=$A$1"); // C4
1844 
1845     ScAddress aPos(2,2,0);
1846     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1847 
1848     aPos = ScAddress(2,3,0);
1849     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1850 
1851     // Delete row 2 to push formula cells up (to C2:C3).
1852     m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1853 
1854     aPos = ScAddress(2,1,0);
1855     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C2.");
1856 
1857     aPos = ScAddress(2,2,0);
1858     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C3.");
1859 
1860     // Insert one row at row 2 to move them back.
1861     m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1862 
1863     aPos = ScAddress(2,2,0);
1864     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1865 
1866     aPos = ScAddress(2,3,0);
1867     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1868 
1869     // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
1870     m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1871 
1872     aPos = ScAddress(2,4,0);
1873     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A3", "Wrong formula in C5.");
1874 
1875     aPos = ScAddress(2,5,0);
1876     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$3", "Wrong formula in C6.");
1877 
1878     // Delete 2 rows at row 1 to shift them back.
1879     m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1880 
1881     aPos = ScAddress(2,2,0);
1882     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1883 
1884     aPos = ScAddress(2,3,0);
1885     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1886 
1887     // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
1888     m_pDoc->InsertCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1889 
1890     aPos = ScAddress(5,2,0);
1891     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in F3.");
1892 
1893     aPos = ScAddress(5,3,0);
1894     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in F4.");
1895 
1896     // Delete columns B:D to shift them back.
1897     m_pDoc->DeleteCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1898 
1899     aPos = ScAddress(2,2,0);
1900     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1901 
1902     aPos = ScAddress(2,3,0);
1903     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1904 
1905     // Insert cells over A1:A3 to only shift A1 down to A4.
1906     m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
1907 
1908     aPos = ScAddress(2,2,0);
1909     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A4", "Wrong formula in C3.");
1910 
1911     aPos = ScAddress(2,3,0);
1912     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$4", "Wrong formula in C4.");
1913 
1914     // .. and back.
1915     m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
1916 
1917     aPos = ScAddress(2,2,0);
1918     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1919 
1920     aPos = ScAddress(2,3,0);
1921     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1922 
1923     // Delete row 1 which will delete the value cell (A1).
1924     m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1925 
1926     aPos = ScAddress(2,1,0);
1927     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
1928     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1929     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1930     aPos = ScAddress(2,2,0);
1931     pFC = m_pDoc->GetFormulaCell(aPos);
1932     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1933     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1934 
1935     // Clear all and start over.
1936     clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
1937 
1938     // Test range updates
1939 
1940     // Fill B2:C3 with values.
1941     m_pDoc->SetValue(ScAddress(1,1,0), 1);
1942     m_pDoc->SetValue(ScAddress(1,2,0), 2);
1943     m_pDoc->SetValue(ScAddress(2,1,0), 3);
1944     m_pDoc->SetValue(ScAddress(2,2,0), 4);
1945 
1946     m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
1947     m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
1948 
1949     aPos = ScAddress(0,5,0);
1950     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1951 
1952     aPos = ScAddress(0,6,0);
1953     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1954 
1955     // Insert a row at row 1.
1956     m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1957 
1958     aPos = ScAddress(0,6,0);
1959     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B3:C4)", "Wrong formula in A7.");
1960 
1961     aPos = ScAddress(0,7,0);
1962     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$3:$C$4)", "Wrong formula in A8.");
1963 
1964     // ... and back.
1965     m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1966 
1967     aPos = ScAddress(0,5,0);
1968     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1969 
1970     aPos = ScAddress(0,6,0);
1971     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1972 
1973     // Insert columns B:C to shift only the value range.
1974     m_pDoc->InsertCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1975 
1976     aPos = ScAddress(0,5,0);
1977     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(D2:E3)", "Wrong formula in A6.");
1978 
1979     aPos = ScAddress(0,6,0);
1980     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($D$2:$E$3)", "Wrong formula in A7.");
1981 
1982     // ... and back.
1983     m_pDoc->DeleteCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1984 
1985     aPos = ScAddress(0,5,0);
1986     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1987 
1988     aPos = ScAddress(0,6,0);
1989     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1990 
1991     // Insert rows 5:6 to shift the formula cells only.
1992     m_pDoc->InsertRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1993 
1994     aPos = ScAddress(0,7,0);
1995     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A8.");
1996 
1997     aPos = ScAddress(0,8,0);
1998     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A9.");
1999 
2000     // ... and back.
2001     m_pDoc->DeleteRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
2002 
2003     aPos = ScAddress(0,5,0);
2004     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
2005 
2006     aPos = ScAddress(0,6,0);
2007     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
2008 
2009     // Check the values of the formula cells in A6:A7.
2010     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2011     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2012 
2013     // Insert cells over B1:B2 to partially shift value range.
2014     m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
2015 
2016     // Check the values of the formula cells in A6:A7 again.
2017     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2018     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2019 
2020     // ... and shift them back.
2021     m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
2022 
2023     // The formula cell results should be back too.
2024     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2025     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2026 
2027     // Delete rows 2:3 to completely remove the referenced range.
2028     m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),2,0));
2029 
2030     // Both A4 and A5 should show #REF! errors.
2031     pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
2032     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
2033     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
2034 
2035     pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
2036     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
2037     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
2038 
2039     m_pDoc->DeleteTab(0);
2040 }
2041 
testFormulaRefUpdateRange()2042 void TestFormula::testFormulaRefUpdateRange()
2043 {
2044     m_pDoc->InsertTab(0, "Formula");
2045 
2046     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2047 
2048     setExpandRefs(false);
2049 
2050     // Set values to B2:C5.
2051     m_pDoc->SetValue(ScAddress(1,1,0), 1);
2052     m_pDoc->SetValue(ScAddress(1,2,0), 2);
2053     m_pDoc->SetValue(ScAddress(1,3,0), 3);
2054     m_pDoc->SetValue(ScAddress(1,4,0), 4);
2055     m_pDoc->SetValue(ScAddress(2,1,0), 5);
2056     m_pDoc->SetValue(ScAddress(2,2,0), 6);
2057     m_pDoc->SetValue(ScAddress(2,3,0), 7);
2058     m_pDoc->SetValue(ScAddress(2,4,0), 8);
2059 
2060     // Set formula cells to A7 and A8.
2061     m_pDoc->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
2062     m_pDoc->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
2063 
2064     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(B2:C5)", "Wrong formula in A7.");
2065 
2066     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($B$2:$C$5)", "Wrong formula in A8.");
2067 
2068     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2069     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2070 
2071     // Delete row 3. This should shrink the range references by one row.
2072     m_pDoc->DeleteRow(ScRange(0,2,0,m_pDoc->MaxCol(),2,0));
2073 
2074     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(B2:C4)", "Wrong formula in A6.");
2075 
2076     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($B$2:$C$4)", "Wrong formula in A7.");
2077 
2078     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2079     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2080 
2081     // Delete row 4 - bottom of range
2082     m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
2083 
2084     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(B2:C3)", "Wrong formula in A5.");
2085 
2086     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($B$2:$C$3)", "Wrong formula in A6.");
2087 
2088     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,4,0)));
2089     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2090 
2091     // Delete row 2 - top of range
2092     m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
2093 
2094     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,3,0), "SUM(B2:C2)", "Wrong formula in A4.");
2095 
2096     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM($B$2:$C$2)", "Wrong formula in A5.");
2097 
2098     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,3,0)));
2099     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,4,0)));
2100 
2101     // Clear the range and start over.
2102     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
2103 
2104     // Fill C2:F3 with values.
2105     m_pDoc->SetValue(ScAddress(2,1,0), 1);
2106     m_pDoc->SetValue(ScAddress(3,1,0), 2);
2107     m_pDoc->SetValue(ScAddress(4,1,0), 3);
2108     m_pDoc->SetValue(ScAddress(5,1,0), 4);
2109     m_pDoc->SetValue(ScAddress(2,2,0), 5);
2110     m_pDoc->SetValue(ScAddress(3,2,0), 6);
2111     m_pDoc->SetValue(ScAddress(4,2,0), 7);
2112     m_pDoc->SetValue(ScAddress(5,2,0), 8);
2113 
2114     // Set formulas to A2 and A3.
2115     m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
2116     m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
2117 
2118     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:F3)", "Wrong formula in A2.");
2119 
2120     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$F$3)", "Wrong formula in A3.");
2121 
2122     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2123     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2124 
2125     // Delete column D.
2126     m_pDoc->DeleteCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
2127 
2128     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:E3)", "Wrong formula in A2.");
2129 
2130     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$E$3)", "Wrong formula in A3.");
2131 
2132     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2133     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2134 
2135     // Delete column E - the right edge of reference range.
2136     m_pDoc->DeleteCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
2137 
2138     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:D3)", "Wrong formula in A2.");
2139 
2140     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$D$3)", "Wrong formula in A3.");
2141 
2142     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2143     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2144 
2145     // Delete column C - the left edge of reference range.
2146     m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2147 
2148     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:C3)", "Wrong formula in A2.");
2149 
2150     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$C$3)", "Wrong formula in A3.");
2151 
2152     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2153     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2154 
2155     // Clear the range and start over.
2156     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
2157 
2158     // Disable expansion of range reference on insertion in adjacent areas.
2159     setExpandRefs(false);
2160 
2161     // Fill C2:D3 with values.
2162     m_pDoc->SetValue(ScAddress(2,1,0), 1);
2163     m_pDoc->SetValue(ScAddress(3,1,0), 2);
2164     m_pDoc->SetValue(ScAddress(2,2,0), 3);
2165     m_pDoc->SetValue(ScAddress(3,2,0), 4);
2166 
2167     // Set formulas at A5 and A6.
2168     m_pDoc->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
2169     m_pDoc->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
2170 
2171     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
2172 
2173     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
2174 
2175     // Insert a column at column C. This should simply shift the reference without expansion.
2176     m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2177 
2178     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(D2:E3)", "Wrong formula in A5.");
2179 
2180     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($D$2:$E$3)", "Wrong formula in A6.");
2181 
2182     // Shift it back.
2183     m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2184 
2185     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
2186 
2187     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
2188 
2189     // Insert at column D. This should expand the reference by one column length.
2190     m_pDoc->InsertCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
2191 
2192     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
2193 
2194     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
2195 
2196     // Insert at column F. No expansion should occur since the edge expansion is turned off.
2197     m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
2198 
2199     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
2200 
2201     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
2202 
2203     // Insert at row 2. No expansion should occur with edge expansion turned off.
2204     m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
2205 
2206     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(C3:E4)", "Wrong formula in A6.");
2207 
2208     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($C$3:$E$4)", "Wrong formula in A7.");
2209 
2210     // Insert at row 4 to expand the reference range.
2211     m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
2212 
2213     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(C3:E5)", "Wrong formula in A7.");
2214 
2215     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($C$3:$E$5)", "Wrong formula in A8.");
2216 
2217     // Insert at row 6. No expansion with edge expansion turned off.
2218     m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
2219 
2220     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM(C3:E5)", "Wrong formula in A8.");
2221 
2222     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM($C$3:$E$5)", "Wrong formula in A9.");
2223 
2224     // Clear the range and start over.
2225     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
2226 
2227     // Turn edge expansion on.
2228     setExpandRefs(true);
2229 
2230     // Fill C6:D7 with values.
2231     m_pDoc->SetValue(ScAddress(2,5,0), 1);
2232     m_pDoc->SetValue(ScAddress(2,6,0), 2);
2233     m_pDoc->SetValue(ScAddress(3,5,0), 3);
2234     m_pDoc->SetValue(ScAddress(3,6,0), 4);
2235 
2236     // Set formulas at A2 and A3.
2237     m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
2238     m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
2239 
2240     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:D7)", "Wrong formula in A2.");
2241 
2242     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$D$7)", "Wrong formula in A3.");
2243 
2244     // Insert at column E. This should expand the reference range by one column.
2245     m_pDoc->InsertCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
2246 
2247     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:E7)", "Wrong formula in A2.");
2248 
2249     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$E$7)", "Wrong formula in A3.");
2250 
2251     // Insert at column C to edge-expand the reference range.
2252     m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2253 
2254     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F7)", "Wrong formula in A2.");
2255 
2256     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$7)", "Wrong formula in A3.");
2257 
2258     // Insert at row 8 to edge-expand.
2259     m_pDoc->InsertRow(ScRange(0,7,0,m_pDoc->MaxCol(),7,0));
2260 
2261     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F8)", "Wrong formula in A2.");
2262 
2263     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$8)", "Wrong formula in A3.");
2264 
2265     // Insert at row 6 to edge-expand.
2266     m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
2267 
2268     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F9)", "Wrong formula in A2.");
2269 
2270     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$9)", "Wrong formula in A3.");
2271 
2272     m_pDoc->InsertTab(1, "StickyRange");
2273 
2274     // A3:A18 all possible combinations of relative and absolute addressing,
2275     // leaving one row above and below unreferenced.
2276     ScAddress aPos(0,2,1);
2277     m_pDoc->SetString( aPos, "=B2:B1048575");
2278     aPos.IncRow();
2279     m_pDoc->SetString( aPos, "=B2:B$1048575");
2280     aPos.IncRow();
2281     m_pDoc->SetString( aPos, "=B2:$B1048575");
2282     aPos.IncRow();
2283     m_pDoc->SetString( aPos, "=B2:$B$1048575");
2284     aPos.IncRow();
2285     m_pDoc->SetString( aPos, "=B$2:B1048575");
2286     aPos.IncRow();
2287     m_pDoc->SetString( aPos, "=B$2:B$1048575");
2288     aPos.IncRow();
2289     m_pDoc->SetString( aPos, "=B$2:$B1048575");
2290     aPos.IncRow();
2291     m_pDoc->SetString( aPos, "=B$2:$B$1048575");
2292     aPos.IncRow();
2293     m_pDoc->SetString( aPos, "=$B2:B1048575");
2294     aPos.IncRow();
2295     m_pDoc->SetString( aPos, "=$B2:B$1048575");
2296     aPos.IncRow();
2297     m_pDoc->SetString( aPos, "=$B2:$B1048575");
2298     aPos.IncRow();
2299     m_pDoc->SetString( aPos, "=$B2:$B$1048575");
2300     aPos.IncRow();
2301     m_pDoc->SetString( aPos, "=$B$2:B1048575");
2302     aPos.IncRow();
2303     m_pDoc->SetString( aPos, "=$B$2:B$1048575");
2304     aPos.IncRow();
2305     m_pDoc->SetString( aPos, "=$B$2:$B1048575");
2306     aPos.IncRow();
2307     m_pDoc->SetString( aPos, "=$B$2:$B$1048575");
2308     aPos.IncRow();
2309     // A19 reference to two cells on one row.
2310     m_pDoc->SetString( aPos, "=B1048575:C1048575");
2311     aPos.IncRow();
2312 
2313     // Insert 2 rows in the middle to shift bottom reference down and make it
2314     // sticky.
2315     m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
2316 
2317     // A3:A18 must not result in #REF! anywhere.
2318     aPos.Set(0,2,1);
2319     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:B1048576", "Wrong reference in A3 after insertion.");
2320     aPos.IncRow();
2321     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:B$1048576", "Wrong reference in A4 after insertion.");
2322     aPos.IncRow();
2323     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:$B1048576", "Wrong reference in A5 after insertion.");
2324     aPos.IncRow();
2325     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:$B$1048576", "Wrong reference in A6 after insertion.");
2326     aPos.IncRow();
2327     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:B1048576", "Wrong reference in A7 after insertion.");
2328     aPos.IncRow();
2329     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:B$1048576", "Wrong reference in A8 after insertion.");
2330     aPos.IncRow();
2331     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:$B1048576", "Wrong reference in A9 after insertion.");
2332     aPos.IncRow();
2333     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:$B$1048576", "Wrong reference in A10 after insertion.");
2334     aPos.IncRow();
2335     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:B1048576", "Wrong reference in A11 after insertion.");
2336     aPos.IncRow();
2337     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:B$1048576", "Wrong reference in A12 after insertion.");
2338     aPos.IncRow();
2339     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:$B1048576", "Wrong reference in A13 after insertion.");
2340     aPos.IncRow();
2341     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:$B$1048576", "Wrong reference in A14 after insertion.");
2342     aPos.IncRow();
2343     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:B1048576", "Wrong reference in A15 after insertion.");
2344     aPos.IncRow();
2345     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:B$1048576", "Wrong reference in A16 after insertion.");
2346     aPos.IncRow();
2347     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:$B1048576", "Wrong reference in A17 after insertion.");
2348     aPos.IncRow();
2349     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:$B$1048576", "Wrong reference in A18 after insertion.");
2350     aPos.IncRow();
2351 
2352     // A19 reference to one row shifted out should be #REF!
2353     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B#REF!:C#REF!", "Wrong reference in A19 after insertion.");
2354     // A19 enter reference to last row.
2355     m_pDoc->SetString( aPos, "=B1048576:C1048576");
2356     aPos.IncRow();
2357 
2358     // Delete row 1 to shift top reference up, bottom reference stays sticky.
2359     m_pDoc->DeleteRow(ScRange(0,0,1,m_pDoc->MaxCol(),0,1));
2360 
2361     // Check sticky bottom references and display of entire column references,
2362     // now in A2:A17.
2363     aPos.Set(0,1,1);
2364     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A2 after deletion.");
2365     aPos.IncRow();
2366     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:B$1048576", "Wrong reference in A3 after deletion.");
2367     aPos.IncRow();
2368     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A4 after deletion.");
2369     aPos.IncRow();
2370     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
2371     aPos.IncRow();
2372     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:B1048576", "Wrong reference in A6 after deletion.");
2373     aPos.IncRow();
2374     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A7 after deletion.");
2375     aPos.IncRow();
2376     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
2377     aPos.IncRow();
2378     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A9 after deletion.");
2379     aPos.IncRow();
2380     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A10 after deletion.");
2381     aPos.IncRow();
2382     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
2383     aPos.IncRow();
2384     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A12 after deletion.");
2385     aPos.IncRow();
2386     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
2387     aPos.IncRow();
2388     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
2389     aPos.IncRow();
2390     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A15 after deletion.");
2391     aPos.IncRow();
2392     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2393     aPos.IncRow();
2394     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A17 after deletion.");
2395     aPos.IncRow();
2396 
2397     // A18 reference to one last row should be shifted up.
2398     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1048575:C1048575", "Wrong reference in A18 after deletion.");
2399     aPos.IncRow();
2400 
2401     // Insert 4 rows in the middle.
2402     m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+3, 1));
2403     // Delete 2 rows in the middle.
2404     m_pDoc->DeleteRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
2405 
2406     // References in A2:A17 must still be the same.
2407     aPos.Set(0,1,1);
2408     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A2 after deletion.");
2409     aPos.IncRow();
2410     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:B$1048576", "Wrong reference in A3 after deletion.");
2411     aPos.IncRow();
2412     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A4 after deletion.");
2413     aPos.IncRow();
2414     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
2415     aPos.IncRow();
2416     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:B1048576", "Wrong reference in A6 after deletion.");
2417     aPos.IncRow();
2418     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A7 after deletion.");
2419     aPos.IncRow();
2420     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
2421     aPos.IncRow();
2422     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A9 after deletion.");
2423     aPos.IncRow();
2424     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A10 after deletion.");
2425     aPos.IncRow();
2426     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
2427     aPos.IncRow();
2428     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A12 after deletion.");
2429     aPos.IncRow();
2430     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
2431     aPos.IncRow();
2432     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
2433     aPos.IncRow();
2434     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A15 after deletion.");
2435     aPos.IncRow();
2436     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2437     aPos.IncRow();
2438     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A17 after deletion.");
2439     aPos.IncRow();
2440 
2441     // Enter values in B1 and B1048576 (last row).
2442     m_pDoc->SetValue( 1,0,1, 1.0);
2443     m_pDoc->SetValue( 1,m_pDoc->MaxRow(),1, 2.0);
2444     // Sticky reference including last row.
2445     m_pDoc->SetString( 2,0,1, "=SUM(B:B)");
2446     // Reference to last row.
2447     CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxRow() changed, adapt unit test.", 1048575, int(m_pDoc->MaxRow()));
2448     m_pDoc->SetString( 2,1,1, "=SUM(B1048576:C1048576)");
2449     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 3.0, m_pDoc->GetValue(2,0,1));
2450     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C2.", 2.0, m_pDoc->GetValue(2,1,1));
2451     // Delete last row.
2452     m_pDoc->DeleteRow( ScRange( 0, m_pDoc->MaxRow(), 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2453     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 1.0, m_pDoc->GetValue(2,0,1));
2454     CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C2 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,1,1));
2455 
2456     // Enter values in A23 and AMJ23 (last column).
2457     m_pDoc->SetValue( 0,22,1, 1.0);
2458     m_pDoc->SetValue( m_pDoc->MaxCol(),22,1, 2.0);
2459     // C3 with sticky reference including last column.
2460     m_pDoc->SetString( 2,2,1, "=SUM(23:23)");
2461     // C4 with reference to last column.
2462     CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxCol() changed, adapt unit test.", 1023, int(m_pDoc->MaxCol()));
2463     m_pDoc->SetString( 2,3,1, "=SUM(AMJ22:AMJ23)");
2464     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 3.0, m_pDoc->GetValue(2,2,1));
2465     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C4.", 2.0, m_pDoc->GetValue(2,3,1));
2466     // Delete last column.
2467     m_pDoc->DeleteCol( ScRange( m_pDoc->MaxCol(), 0, 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2468     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 1.0, m_pDoc->GetValue(2,2,1));
2469     CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C4 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,3,1));
2470 
2471     m_pDoc->DeleteTab(1);
2472 
2473     m_pDoc->DeleteTab(0);
2474 }
2475 
testFormulaRefUpdateSheets()2476 void TestFormula::testFormulaRefUpdateSheets()
2477 {
2478     m_pDoc->InsertTab(0, "Sheet1");
2479     m_pDoc->InsertTab(1, "Sheet2");
2480 
2481     OUString aName;
2482     m_pDoc->GetName(0, aName);
2483     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2484     m_pDoc->GetName(1, aName);
2485     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2486 
2487     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2488 
2489     // Set values to B2:C3 on sheet Sheet1.
2490     m_pDoc->SetValue(ScAddress(1,1,0), 1);
2491     m_pDoc->SetValue(ScAddress(1,2,0), 2);
2492     m_pDoc->SetValue(ScAddress(2,1,0), 3);
2493     m_pDoc->SetValue(ScAddress(2,2,0), 4);
2494 
2495     // Set formulas to B2 and B3 on sheet Sheet2.
2496     m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
2497     m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
2498 
2499     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2500 
2501     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2502 
2503     // Swap the sheets.
2504     m_pDoc->MoveTab(0, 1);
2505     m_pDoc->GetName(0, aName);
2506     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2507     m_pDoc->GetName(1, aName);
2508     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2509 
2510     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2511 
2512     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2513 
2514     // Swap back.
2515     m_pDoc->MoveTab(0, 1);
2516     m_pDoc->GetName(0, aName);
2517     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2518     m_pDoc->GetName(1, aName);
2519     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2520 
2521     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2522 
2523     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2524 
2525     // Insert a new sheet between the two.
2526     m_pDoc->InsertTab(1, "Temp");
2527 
2528     m_pDoc->GetName(1, aName);
2529     CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName);
2530     m_pDoc->GetName(2, aName);
2531     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2532 
2533     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2534 
2535     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2536 
2537     // Move the last sheet (Sheet2) to the first position.
2538     m_pDoc->MoveTab(2, 0);
2539 
2540     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2541 
2542     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2543 
2544     // Move back.
2545     m_pDoc->MoveTab(0, 2);
2546 
2547     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2548 
2549     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2550 
2551     // Move the "Temp" sheet to the last position.
2552     m_pDoc->MoveTab(1, 2);
2553 
2554     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2555 
2556     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2557 
2558     // Move back.
2559     m_pDoc->MoveTab(2, 1);
2560 
2561     // Delete the temporary sheet.
2562     m_pDoc->DeleteTab(1);
2563 
2564     m_pDoc->GetName(1, aName);
2565     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2566 
2567     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2568 
2569     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2570 
2571     // Insert a new sheet before the first one.
2572     m_pDoc->InsertTab(0, "Temp");
2573 
2574     m_pDoc->GetName(1, aName);
2575     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2576     m_pDoc->GetName(2, aName);
2577     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2578 
2579     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2580 
2581     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2582 
2583     // Delete the temporary sheet.
2584     m_pDoc->DeleteTab(0);
2585 
2586     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2587 
2588     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2589 
2590     // Append a bunch of sheets.
2591     m_pDoc->InsertTab(2, "Temp1");
2592     m_pDoc->InsertTab(3, "Temp2");
2593     m_pDoc->InsertTab(4, "Temp3");
2594 
2595     // Move these tabs around. This shouldn't affects the first 2 sheets.
2596     m_pDoc->MoveTab(2, 4);
2597     m_pDoc->MoveTab(3, 2);
2598 
2599     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2600 
2601     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2602 
2603     // Delete the temp sheets.
2604     m_pDoc->DeleteTab(4);
2605     m_pDoc->DeleteTab(3);
2606     m_pDoc->DeleteTab(2);
2607 
2608     // Delete Sheet1.
2609     m_pDoc->DeleteTab(0);
2610     m_pDoc->GetName(0, aName);
2611     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2612 
2613     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(#REF!.B2:C3)", "Wrong formula in Sheet2.B2.");
2614 
2615     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2616 
2617     m_pDoc->DeleteTab(0);
2618 }
2619 
testFormulaRefUpdateInsertRows()2620 void TestFormula::testFormulaRefUpdateInsertRows()
2621 {
2622     setExpandRefs(false);
2623 
2624     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2625     m_pDoc->InsertTab(0, "Formula");
2626 
2627     // Insert raw values in B2:B4.
2628     m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
2629     m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
2630     m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
2631 
2632     // Insert a formula in B5 to sum up B2:B4.
2633     m_pDoc->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
2634 
2635     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2636 
2637     // Insert rows over rows 1:2.
2638     ScMarkData aMark(m_pDoc->GetSheetLimits());
2639     aMark.SelectOneTable(0);
2640     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2641     rFunc.InsertCells(ScRange(0,0,0,m_pDoc->MaxCol(),1,0), &aMark, INS_INSROWS_BEFORE, false, true);
2642 
2643     // The raw data should have shifted to B4:B6.
2644     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2645     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2646     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,5,0)));
2647 
2648     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,6,0), "SUM(B4:B6)", "Wrong formula!");
2649 
2650     // Clear and start over.
2651     clearSheet(m_pDoc, 0);
2652 
2653     // Set raw values in A4:A6.
2654     m_pDoc->SetValue(ScAddress(0,3,0), 1.0);
2655     m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
2656     m_pDoc->SetValue(ScAddress(0,5,0), 3.0);
2657 
2658     // Set formula in A3 to reference A4:A6.
2659     m_pDoc->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
2660 
2661     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2662 
2663     // Insert 3 rows over 2:4.  This should push A3:A6 to A6:A9.
2664     rFunc.InsertCells(ScRange(0,1,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
2665     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
2666     CPPUNIT_ASSERT(pFC);
2667     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula cell should not be an error.", 0, static_cast<int>(pFC->GetErrCode()));
2668     ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2669 
2670     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "MAX(A7:A9)", "Wrong formula!");
2671 
2672     m_pDoc->DeleteTab(0);
2673 }
2674 
testFormulaRefUpdateSheetsDelete()2675 void TestFormula::testFormulaRefUpdateSheetsDelete()
2676 {
2677     m_pDoc->InsertTab(0, "Sheet1");
2678     m_pDoc->InsertTab(1, "Sheet2");
2679     m_pDoc->InsertTab(2, "Sheet3");
2680     m_pDoc->InsertTab(3, "Sheet4");
2681 
2682     m_pDoc->SetString(ScAddress(4,1,0), "=SUM(Sheet2.A4:Sheet4.A4)");
2683     m_pDoc->SetString(ScAddress(4,2,0), "=SUM($Sheet2.A4:$Sheet4.A4)");
2684     m_pDoc->DeleteTab(1);
2685 
2686     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,1,0), "SUM(Sheet3.A4:Sheet4.A4)", "Wrong Formula");
2687     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,2,0), "SUM($Sheet3.A4:$Sheet4.A4)", "Wrong Formula");
2688 
2689     m_pDoc->InsertTab(1, "Sheet2");
2690 
2691     m_pDoc->SetString(ScAddress(5,1,3), "=SUM(Sheet1.A5:Sheet3.A5)");
2692     m_pDoc->SetString(ScAddress(5,2,3), "=SUM($Sheet1.A5:$Sheet3.A5)");
2693     m_pDoc->DeleteTab(2);
2694 
2695     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(5,1,2), "SUM(Sheet1.A5:Sheet2.A5)", "Wrong Formula");
2696     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(5,2,2), "SUM($Sheet1.A5:$Sheet2.A5)", "Wrong Formula");
2697 
2698     m_pDoc->InsertTab(2, "Sheet3");
2699 
2700     m_pDoc->SetString(ScAddress(6,1,3), "=SUM(Sheet1.A6:Sheet3.A6)");
2701     m_pDoc->SetString(ScAddress(6,2,3), "=SUM($Sheet1.A6:$Sheet3.A6)");
2702     m_pDoc->DeleteTabs(0,3);
2703 
2704     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(6,1,0), "SUM(#REF!.A6:#REF!.A6)", "Wrong Formula");
2705     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(6,2,0), "SUM($#REF!.A6:$#REF!.A6)", "Wrong Formula");
2706 
2707     m_pDoc->InsertTab(0, "Sheet1");
2708     m_pDoc->InsertTab(1, "Sheet2");
2709     m_pDoc->InsertTab(2, "Sheet3");
2710 
2711     m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.A2:Sheet3.A2");
2712     m_pDoc->SetString(ScAddress(2,1,1), "=SUM(Sheet1.A1:Sheet2.A1");
2713     m_pDoc->SetString(ScAddress(3,1,1), "=SUM(Sheet2.A3:Sheet4.A3");
2714 
2715     m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.A2:$Sheet3.A2");
2716     m_pDoc->SetString(ScAddress(2,2,1), "=SUM($Sheet1.A1:$Sheet2.A1");
2717     m_pDoc->SetString(ScAddress(3,2,1), "=SUM($Sheet2.A3:$Sheet4.A3");
2718 
2719     m_pDoc->DeleteTab(2);
2720 
2721     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.A2:Sheet2.A2)", "Wrong Formula");
2722 
2723     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,1), "SUM(Sheet1.A1:Sheet2.A1)", "Wrong Formula");
2724 
2725     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,1), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2726 
2727     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.A2:$Sheet2.A2)", "Wrong Formula");
2728 
2729     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,1), "SUM($Sheet1.A1:$Sheet2.A1)", "Wrong Formula");
2730 
2731     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,1), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2732 
2733     m_pDoc->DeleteTab(0);
2734 
2735     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet2.A2:Sheet2.A2)", "Wrong Formula");
2736 
2737     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "SUM(Sheet2.A1:Sheet2.A1)", "Wrong Formula");
2738 
2739     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2740 
2741     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet2.A2:$Sheet2.A2)", "Wrong Formula");
2742 
2743     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,0), "SUM($Sheet2.A1:$Sheet2.A1)", "Wrong Formula");
2744 
2745     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,0), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2746 
2747     m_pDoc->DeleteTab(0);
2748     m_pDoc->DeleteTab(0);
2749 }
2750 
testFormulaRefUpdateInsertColumns()2751 void TestFormula::testFormulaRefUpdateInsertColumns()
2752 {
2753     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2754     setExpandRefs(false);
2755 
2756     m_pDoc->InsertTab(0, "Formula");
2757 
2758     // Set named range for B2 with absolute column and relative same row.
2759     const ScAddress aNamePos(0,1,0);
2760     bool bInserted = m_pDoc->InsertNewRangeName("RowRelativeRange", aNamePos, "$Formula.$B2");
2761     CPPUNIT_ASSERT(bInserted);
2762 
2763     // Set named range for entire absolute column B.
2764     bInserted = m_pDoc->InsertNewRangeName("EntireColumn", aNamePos, "$B:$B");
2765     CPPUNIT_ASSERT(bInserted);
2766 
2767     // Set named range for entire absolute row 2.
2768     bInserted = m_pDoc->InsertNewRangeName("EntireRow", aNamePos, "$2:$2");
2769     CPPUNIT_ASSERT(bInserted);
2770 
2771     // Set values in B1:B3.
2772     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
2773     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
2774     m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
2775 
2776     // Reference them in B4.
2777     m_pDoc->SetString(ScAddress(1,3,0), "=SUM(B1:B3)");
2778     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2779 
2780     // Use named range in C2 to reference B2.
2781     m_pDoc->SetString(ScAddress(2,1,0), "=RowRelativeRange");
2782     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
2783 
2784     // Use named range in C3 to reference column B, values in B1,B2,B3,B4
2785     m_pDoc->SetString(ScAddress(2,2,0), "=SUM(EntireColumn)");
2786     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2,2,0)));
2787 
2788     // Use named range in C4 to reference row 2, values in B2 and C2.
2789     m_pDoc->SetString(ScAddress(2,3,0), "=SUM(EntireRow)");
2790     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,3,0)));
2791 
2792     // Insert columns over A:B.
2793     ScMarkData aMark(m_pDoc->GetSheetLimits());
2794     aMark.SelectOneTable(0);
2795     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2796     rFunc.InsertCells(ScRange(0,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
2797 
2798     // Now, the original column B has moved to column D.
2799     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,3,0), "SUM(D1:D3)", "Wrong formula in D4 after column insertion.");
2800 
2801     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
2802 
2803     // Check that the named reference points to the moved cell, now D2.
2804     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("ROWRELATIVERANGE");
2805     CPPUNIT_ASSERT(pName);
2806     OUString aSymbol;
2807     pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2808     CPPUNIT_ASSERT_EQUAL(OUString("$Formula.$D2"), aSymbol);
2809 
2810     // Check that the formula using the name, now in E2, still has the same result.
2811     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,1,0), "RowRelativeRange", "Wrong formula in E2 after column insertion.");
2812 
2813     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,1,0)));
2814 
2815     // Check that the named column reference points to the moved column, now D.
2816     pName = m_pDoc->GetRangeName()->findByUpperName("ENTIRECOLUMN");
2817     CPPUNIT_ASSERT(pName);
2818     pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2819     CPPUNIT_ASSERT_EQUAL(OUString("$D:$D"), aSymbol);
2820 
2821     // Check that the formula using the name, now in E3, still has the same result.
2822     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,2,0), "SUM(EntireColumn)", "Wrong formula in E3 after column insertion.");
2823 
2824     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(4,2,0)));
2825 
2826     // Check that the named row reference still points to the same entire row
2827     // and does not have a #REF! error due to inserted columns.
2828     pName = m_pDoc->GetRangeName()->findByUpperName("ENTIREROW");
2829     CPPUNIT_ASSERT(pName);
2830     pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2831     CPPUNIT_ASSERT_EQUAL(OUString("$2:$2"), aSymbol);
2832 
2833     // Check that the formula using the name, now in E4, still has the same result.
2834     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,3,0), "SUM(EntireRow)", "Wrong formula in E4 after column insertion.");
2835 
2836     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(4,3,0)));
2837 
2838     m_pDoc->DeleteTab(0);
2839 }
2840 
testFormulaRefUpdateMove()2841 void TestFormula::testFormulaRefUpdateMove()
2842 {
2843     m_pDoc->InsertTab(0, "Sheet1");
2844 
2845     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2846 
2847     // Set value to B4:B6.
2848     m_pDoc->SetValue(ScAddress(1,3,0), 1);
2849     m_pDoc->SetValue(ScAddress(1,4,0), 2);
2850     m_pDoc->SetValue(ScAddress(1,5,0), 3);
2851 
2852     // Set formulas to A9:A12 that references B4:B6.
2853     m_pDoc->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
2854     m_pDoc->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
2855     m_pDoc->SetString(ScAddress(0,10,0), "=B5");
2856     m_pDoc->SetString(ScAddress(0,11,0), "=$B$6");
2857 
2858     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2859     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2860     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2861     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2862 
2863     // Move B4:B6 to D4 (two columns to the right).
2864     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2865     bool bMoved = rFunc.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
2866     CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved);
2867 
2868     // The results of the formula cells that reference the moved range should remain the same.
2869     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2870     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2871     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2872     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2873 
2874     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM(D4:D6)", "Wrong formula.");
2875     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM($D$4:$D$6)", "Wrong formula.");
2876     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "D5", "Wrong formula.");
2877     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "$D$6", "Wrong formula.");
2878 
2879     // Move A9:A12 to B10:B13.
2880     bMoved = rFunc.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
2881     CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved);
2882 
2883     // The results of these formula cells should still stay the same.
2884     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,9,0));
2885     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,10,0));
2886     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2887     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2888 
2889     // Displayed formulas should stay the same since the referenced range hasn't moved.
2890     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2891     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2892     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,11,0), "D5", "Wrong formula.");
2893     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2894 
2895     // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
2896     // where it is.
2897     bMoved = rFunc.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
2898     CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved);
2899 
2900     // Only the values of B10 and B11 should be updated.
2901     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,9,0));
2902     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,10,0));
2903     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2904     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2905 
2906     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2907     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2908     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,11,0), "E5", "Wrong formula.");
2909     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2910 
2911     m_pDoc->DeleteTab(0);
2912 }
2913 
testFormulaRefUpdateMoveUndo()2914 void TestFormula::testFormulaRefUpdateMoveUndo()
2915 {
2916     m_pDoc->InsertTab(0, "Test");
2917 
2918     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2919 
2920     // Set values in A1:A4.
2921     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2922     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
2923     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
2924     m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
2925 
2926     // Set formulas with single cell references in A6:A8.
2927     m_pDoc->SetString(ScAddress(0,5,0), "=A1");
2928     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2929     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "A1", "Wrong formula.");
2930 
2931     m_pDoc->SetString(ScAddress(0,6,0), "=A1+A2+A3");
2932     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2933     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2934 
2935     m_pDoc->SetString(ScAddress(0,7,0), "=A1+A3+A4");
2936     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2937     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2938 
2939     // Set formulas with range references in A10:A12.
2940     m_pDoc->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
2941     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2942     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2943 
2944     m_pDoc->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
2945     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2946     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2947 
2948     m_pDoc->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
2949     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2950     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2951 
2952     // Move A1:A3 to C1:C3. Note that A4 remains.
2953     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2954     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
2955     CPPUNIT_ASSERT(bMoved);
2956 
2957     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2958     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "C1", "Wrong formula.");
2959 
2960     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2961     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "C1+C2+C3", "Wrong formula.");
2962 
2963     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2964     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "C1+C3+A4", "Wrong formula.");
2965 
2966     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2967     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(C1:C2)", "Wrong formula.");
2968 
2969     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2970     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(C1:C3)", "Wrong formula.");
2971 
2972     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2973     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2974 
2975     // Undo the move.
2976     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2977     CPPUNIT_ASSERT(pUndoMgr);
2978     pUndoMgr->Undo();
2979 
2980     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2981     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "A1", "Wrong formula.");
2982 
2983     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2984     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2985 
2986     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2987     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2988 
2989     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2990     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2991 
2992     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2993     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2994 
2995     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2996     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)","Wrong formula." );
2997 
2998     // Make sure the broadcasters are still valid by changing the value of A1.
2999     m_pDoc->SetValue(ScAddress(0,0,0), 20);
3000 
3001     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(0,5,0)));
3002     CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3003     CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc->GetValue(ScAddress(0,7,0)));
3004 
3005     CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3006     CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,10,0)));
3007     CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc->GetValue(ScAddress(0,11,0)));
3008 
3009     m_pDoc->DeleteTab(0);
3010 }
3011 
testFormulaRefUpdateMoveUndo2()3012 void TestFormula::testFormulaRefUpdateMoveUndo2()
3013 {
3014     m_pDoc->InsertTab(0, "Test");
3015 
3016     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3017 
3018     std::vector<std::vector<const char*>> aData = {
3019         { "1", "2", "=A2*10",      "=SUM(A1:B1)" },
3020         { "3", "4", "=SUM(A2:B2)", "=SUM(A2:B2)" },
3021         { "=SUM(A1:B1)" },
3022     };
3023 
3024     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3025 
3026     std::vector<std::vector<const char*>> aCheckInitial = {
3027         { "1",     "2",    "30",     "3" },
3028         { "3",     "4",     "7",     "7" },
3029         { "3", nullptr, nullptr, nullptr },
3030     };
3031 
3032     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3033     CPPUNIT_ASSERT(bGood);
3034 
3035     // D1:D2 should be grouped.
3036     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
3037     CPPUNIT_ASSERT(pFC);
3038     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3039 
3040     // Drag A1:B1 into A2:B2 thereby overwriting the old A2:B2 content.
3041     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3042     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,1,0,0), ScAddress(0,1,0), true, true, false, true);
3043     CPPUNIT_ASSERT(bMoved);
3044 
3045     std::vector<std::vector<const char*>> aCheckAfter = {
3046         { nullptr, nullptr,    "10",     "3" },
3047         {     "1",     "2",     "3",     "3" },
3048         {     "3", nullptr, nullptr, nullptr },
3049     };
3050 
3051     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:B1 moved to A2:B2");
3052     CPPUNIT_ASSERT(bGood);
3053 
3054     // Undo the move.
3055     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3056     CPPUNIT_ASSERT(pUndoMgr);
3057     pUndoMgr->Undo();
3058 
3059     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3060     CPPUNIT_ASSERT(bGood);
3061 
3062     // D1:D2 should be grouped.
3063     pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
3064     CPPUNIT_ASSERT(pFC);
3065     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3066 
3067     // Redo and check.
3068     pUndoMgr->Redo();
3069 
3070     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3071     CPPUNIT_ASSERT(bGood);
3072 
3073     m_pDoc->DeleteTab(0);
3074 }
3075 
testFormulaRefUpdateMoveUndo3NonShared()3076 void TestFormula::testFormulaRefUpdateMoveUndo3NonShared()
3077 {
3078     m_pDoc->InsertTab(0, "Test");
3079 
3080     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3081 
3082     std::vector<std::vector<const char*>> aData = {
3083         { "10",       nullptr,  nullptr },
3084         { "=A1",      nullptr,  nullptr },
3085         { "=A2+A1",   nullptr,  nullptr },
3086     };
3087 
3088     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3089 
3090     std::vector<std::vector<const char*>> aCheckInitial = {
3091         { "10", nullptr,  nullptr },
3092         { "10", nullptr,  nullptr },
3093         { "20", nullptr,  nullptr },
3094     };
3095 
3096     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3097     CPPUNIT_ASSERT(bGood);
3098 
3099     // Drag A2:A3 into C2:C3.
3100     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3101     bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,2,0), ScAddress(2,1,0), true, true, false, true);
3102     CPPUNIT_ASSERT(bMoved);
3103 
3104     std::vector<std::vector<const char*>> aCheckAfter = {
3105         { "10",    nullptr, nullptr},
3106         { nullptr, nullptr, "10" },
3107         { nullptr, nullptr, "20" },
3108     };
3109 
3110     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A3 moved to C2:C3");
3111     CPPUNIT_ASSERT(bGood);
3112 
3113     // Undo the move.
3114     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3115     CPPUNIT_ASSERT(pUndoMgr);
3116     pUndoMgr->Undo();
3117 
3118     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3119     CPPUNIT_ASSERT(bGood);
3120 
3121     // Redo and check.
3122     pUndoMgr->Redo();
3123 
3124     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3125     CPPUNIT_ASSERT(bGood);
3126 
3127     m_pDoc->DeleteTab(0);
3128 }
3129 
testFormulaRefUpdateMoveUndo3Shared()3130 void TestFormula::testFormulaRefUpdateMoveUndo3Shared()
3131 {
3132     m_pDoc->InsertTab(0, "Test");
3133 
3134     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3135 
3136     std::vector<std::vector<const char*>> aData = {
3137         { "10",       nullptr,  nullptr },
3138         { "=A1",      nullptr,  nullptr },
3139         { "=A2+$A$1", nullptr,  nullptr },
3140         { "=A3+$A$1", nullptr,  nullptr },
3141     };
3142 
3143     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3144 
3145     std::vector<std::vector<const char*>> aCheckInitial = {
3146         { "10", nullptr,  nullptr },
3147         { "10", nullptr,  nullptr },
3148         { "20", nullptr,  nullptr },
3149         { "30", nullptr,  nullptr },
3150     };
3151 
3152     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3153     CPPUNIT_ASSERT(bGood);
3154 
3155     // A3:A4 should be grouped.
3156     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
3157     CPPUNIT_ASSERT(pFC);
3158     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3159 
3160     // Drag A2:A4 into C2:C4.
3161     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3162     bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,3,0), ScAddress(2,1,0), true, true, false, true);
3163     CPPUNIT_ASSERT(bMoved);
3164 
3165     std::vector<std::vector<const char*>> aCheckAfter = {
3166         { "10",    nullptr, nullptr},
3167         { nullptr, nullptr, "10" },
3168         { nullptr, nullptr, "20" },
3169         { nullptr, nullptr, "30" },
3170     };
3171 
3172     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A4 moved to C2:C4");
3173     CPPUNIT_ASSERT(bGood);
3174 
3175     // C3:C4 should be grouped.
3176     pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
3177     CPPUNIT_ASSERT(pFC);
3178     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3179 
3180     // Undo the move.
3181     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3182     CPPUNIT_ASSERT(pUndoMgr);
3183     pUndoMgr->Undo();
3184 
3185     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3186     CPPUNIT_ASSERT(bGood);
3187 
3188     // A3:A4 should be grouped.
3189     pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
3190     CPPUNIT_ASSERT(pFC);
3191     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3192 
3193     // Redo and check.
3194     pUndoMgr->Redo();
3195 
3196     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3197     CPPUNIT_ASSERT(bGood);
3198 
3199     m_pDoc->DeleteTab(0);
3200 }
3201 
testFormulaRefUpdateMoveUndoDependents()3202 void TestFormula::testFormulaRefUpdateMoveUndoDependents()
3203 {
3204     m_pDoc->InsertTab(0, "Test");
3205 
3206     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3207     std::vector<std::vector<const char*>> aData = {
3208         { "1"           },
3209         { "22"          },
3210         { "3"           },
3211         { "4"           },
3212         { "5"           },
3213         { "=SUM(C1:C5)" },
3214         { "=C6"         },
3215     };
3216 
3217     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData);
3218 
3219     std::vector<std::vector<const char*>> aCheckInitial = {
3220         { "1"   },
3221         { "22"  },
3222         { "3"   },
3223         { "4"   },
3224         { "5"   },
3225         { "35"  },
3226         { "35"  },
3227     };
3228 
3229     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3230     CPPUNIT_ASSERT(bGood);
3231 
3232     // Drag C2 into D2.
3233     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3234     bool bMoved = rFunc.MoveBlock(ScRange(2, 1, 0, 2, 1, 0), ScAddress(3, 1, 0), true, true, false, true);
3235     CPPUNIT_ASSERT(bMoved);
3236 
3237     std::vector<std::vector<const char*>> aCheckAfter = {
3238         { "1"     },
3239         { nullptr },
3240         { "3"     },
3241         { "4"     },
3242         { "5"     },
3243         { "13"    },
3244         { "13"    },
3245     };
3246 
3247     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "C2 moved to D2");
3248     CPPUNIT_ASSERT(bGood);
3249 
3250     // Undo the move.
3251     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3252     CPPUNIT_ASSERT(pUndoMgr);
3253     pUndoMgr->Undo();
3254 
3255     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3256     CPPUNIT_ASSERT(bGood);
3257 
3258     // Redo and check.
3259     pUndoMgr->Redo();
3260 
3261     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3262     CPPUNIT_ASSERT(bGood);
3263 
3264     m_pDoc->DeleteTab(0);
3265 }
3266 
testFormulaRefUpdateMoveUndo4()3267 void TestFormula::testFormulaRefUpdateMoveUndo4()
3268 {
3269     m_pDoc->InsertTab(0, "Test");
3270 
3271     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3272     std::vector<std::vector<const char*>> aData = {
3273         { "1",  nullptr,  "=B1", "=A1" },
3274         { "2",  nullptr,  "=B2", "=A2" },
3275     };
3276 
3277     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3278 
3279     std::vector<std::vector<const char*>> aCheckInitial = {
3280         { "1",  nullptr,  "0", "1" },
3281         { "2",  nullptr,  "0", "2" },
3282     };
3283 
3284     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3285     CPPUNIT_ASSERT(bGood);
3286 
3287     // Drag A1:A2 into B1:B2.
3288     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3289     bool bMoved = rFunc.MoveBlock(ScRange(0, 0, 0, 0, 1, 0), ScAddress(1, 0, 0), true, true, false, true);
3290     CPPUNIT_ASSERT(bMoved);
3291 
3292     std::vector<std::vector<const char*>> aCheckAfter = {
3293         { nullptr, "1", "1", "1" },
3294         { nullptr, "2", "2", "2" },
3295     };
3296 
3297     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:A2 moved to B1:B2");
3298     CPPUNIT_ASSERT(bGood);
3299 
3300     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
3301     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
3302     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
3303     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
3304 
3305     // Undo the move.
3306     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3307     CPPUNIT_ASSERT(pUndoMgr);
3308     pUndoMgr->Undo();
3309 
3310     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3311     CPPUNIT_ASSERT(bGood);
3312 
3313     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
3314     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
3315     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "A1", "Wrong formula"); // D1
3316     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "A2", "Wrong formula"); // D2
3317 
3318     // Redo and check.
3319     pUndoMgr->Redo();
3320 
3321     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3322     CPPUNIT_ASSERT(bGood);
3323 
3324     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
3325     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
3326     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
3327     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
3328 
3329     m_pDoc->DeleteTab(0);
3330 }
3331 
testFormulaRefUpdateMoveToSheet()3332 void TestFormula::testFormulaRefUpdateMoveToSheet()
3333 {
3334     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3335 
3336     m_pDoc->InsertTab(0, "Sheet1");
3337     m_pDoc->InsertTab(1, "Sheet2");
3338 
3339     // Set values to A1:A2 on Sheet1, and B1:B2 to reference them.
3340     m_pDoc->SetValue(ScAddress(0,0,0), 11);
3341     m_pDoc->SetValue(ScAddress(0,1,0), 12);
3342     m_pDoc->SetString(ScAddress(1,0,0), "=A1");
3343     m_pDoc->SetString(ScAddress(1,1,0), "=A2");
3344 
3345     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A1", "Wrong formula");
3346     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A2", "Wrong formula");
3347 
3348     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
3349     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(1,1,0)));
3350 
3351     // Move A1:A2 on Sheet1 to B3:B4 on Sheet2.
3352     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3353     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(1,2,1), true, true, false, true);
3354     CPPUNIT_ASSERT(bMoved);
3355 
3356     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
3357     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
3358 
3359     // Undo and check again.
3360     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3361     pUndoMgr->Undo();
3362 
3363     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A1", "Wrong formula");
3364     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A2", "Wrong formula");
3365 
3366     // Redo and check.
3367     pUndoMgr->Redo();
3368 
3369     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
3370     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
3371 
3372     m_pDoc->DeleteTab(1);
3373     m_pDoc->DeleteTab(0);
3374 }
3375 
testFormulaRefUpdateDeleteContent()3376 void TestFormula::testFormulaRefUpdateDeleteContent()
3377 {
3378     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3379 
3380     m_pDoc->InsertTab(0, "Test");
3381 
3382     // Set value in B2.
3383     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3384     // Set formula in C2 to reference B2.
3385     m_pDoc->SetString(ScAddress(2,1,0), "=B2");
3386 
3387     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3388 
3389     // Delete B2.
3390     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3391     ScMarkData aMark(m_pDoc->GetSheetLimits());
3392     aMark.SetMarkArea(ScAddress(1,1,0));
3393     rFunc.DeleteContents(aMark, InsertDeleteFlags::CONTENTS, true, true);
3394 
3395     CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3396     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3397 
3398     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3399     CPPUNIT_ASSERT(pUndoMgr);
3400 
3401     // Undo and check the result of C2.
3402     pUndoMgr->Undo();
3403     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3404     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3405 
3406     // Redo and check.
3407     pUndoMgr->Redo();
3408     CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3409     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3410 
3411     m_pDoc->DeleteTab(0);
3412 }
3413 
testFormulaRefUpdateDeleteAndShiftLeft()3414 void TestFormula::testFormulaRefUpdateDeleteAndShiftLeft()
3415 {
3416     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3417 
3418     m_pDoc->InsertTab(0, "Test");
3419 
3420     // Insert 1,2,3,4,5 in C1:G1.
3421     for (SCCOL i = 0; i <= 4; ++i)
3422         m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3423 
3424     // Insert formula in H1.
3425     ScAddress aPos(7,0,0);
3426     m_pDoc->SetString(aPos, "=SUM(C1:G1)");
3427 
3428     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3429 
3430     // Delete columns D:E (middle of the reference).
3431     ScMarkData aMark(m_pDoc->GetSheetLimits());
3432     aMark.SelectOneTable(0);
3433     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3434     bool bDeleted = rFunc.DeleteCells(ScRange(3,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3435     CPPUNIT_ASSERT(bDeleted);
3436 
3437     aPos.IncCol(-2);
3438     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3439     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3440 
3441     // Undo and check.
3442     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3443     CPPUNIT_ASSERT(pUndo);
3444 
3445     pUndo->Undo();
3446     aPos.IncCol(2);
3447     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3448     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3449 
3450     // Delete columns C:D (left end of the reference).
3451     bDeleted = rFunc.DeleteCells(ScRange(2,0,0,3,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3452     CPPUNIT_ASSERT(bDeleted);
3453 
3454     aPos.IncCol(-2);
3455     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3456     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3457 
3458     // Undo and check again.
3459     pUndo->Undo();
3460     aPos.IncCol(2);
3461     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3462     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3463 
3464     // Delete columns B:E (overlaps on the left).
3465     bDeleted = rFunc.DeleteCells(ScRange(1,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3466     CPPUNIT_ASSERT(bDeleted);
3467 
3468     aPos.IncCol(-4);
3469     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3470     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:C1)", "Wrong formula!");
3471 
3472     // Undo and check again.
3473     pUndo->Undo();
3474     aPos.IncCol(4);
3475     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3476     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3477 
3478     // Start over with a new scenario.
3479     clearSheet(m_pDoc, 0);
3480 
3481     // Insert 1,2,3,4,5,6 into C1:H1.
3482     for (SCCOL i = 0; i <= 5; ++i)
3483         m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3484 
3485     // Set formula in B1.
3486     aPos = ScAddress(1,0,0);
3487     m_pDoc->SetString(aPos, "=SUM(C1:H1)");
3488     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3489 
3490     // Delete columns F:H (right end of the reference).
3491     bDeleted = rFunc.DeleteCells(ScRange(5,0,0,7,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3492     CPPUNIT_ASSERT(bDeleted);
3493 
3494     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3495     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3496 
3497     // Undo and check.
3498     pUndo->Undo();
3499     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3500     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:H1)", "Wrong formula!");
3501 
3502     // Delete columns G:I (overlaps on the right).
3503     bDeleted = rFunc.DeleteCells(ScRange(6,0,0,8,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3504     CPPUNIT_ASSERT(bDeleted);
3505 
3506     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3507     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:F1)", "Wrong formula!");
3508 
3509     // Undo and check again.
3510     pUndo->Undo();
3511     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3512     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:H1)", "Wrong formula!");
3513 
3514     m_pDoc->DeleteTab(0);
3515 }
3516 
testFormulaRefUpdateDeleteAndShiftLeft2()3517 void TestFormula::testFormulaRefUpdateDeleteAndShiftLeft2()
3518 {
3519     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3520 
3521     m_pDoc->InsertTab(0, "Test");
3522 
3523     std::vector<std::vector<const char*>> aData = {
3524         { "1", "=COUNT($A$1:$A$4)", "=COUNT(A1)" },
3525         { "2", "=COUNT($A$1:$A$4)", "=COUNT(A2)" },
3526         { "3", "=COUNT($A$1:$A$4)", "=COUNT(A3)" },
3527         { "4", "=COUNT($A$1:$A$4)", "=COUNT(A4)" },
3528     };
3529 
3530     insertRangeData(m_pDoc, ScAddress(), aData);
3531 
3532     auto funcCheckOriginal = [&]()
3533     {
3534         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0))); // A1
3535         CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0))); // A2
3536         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0))); // A3
3537         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0))); // A4
3538 
3539         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,0,0))); // B1
3540         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3541         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0))); // B3
3542         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0))); // B4
3543 
3544         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,0,0))); // C1
3545         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3546         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,2,0))); // C3
3547         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,3,0))); // C4
3548     };
3549 
3550     auto funcCheckDeleted = [&]()
3551     {
3552         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,0,0))); // A1
3553         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,1,0))); // A2
3554         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,2,0))); // A3
3555         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,3,0))); // A4
3556 
3557         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,0,0))); // B1
3558         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,1,0))); // B2
3559         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,2,0))); // B3
3560         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,3,0))); // B4
3561     };
3562 
3563     funcCheckOriginal();
3564 
3565     // Delete Column A.
3566     ScMarkData aMark(m_pDoc->GetSheetLimits());
3567     aMark.SelectOneTable(0);
3568     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3569     bool bDeleted = rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3570     CPPUNIT_ASSERT(bDeleted);
3571 
3572     funcCheckDeleted();
3573 
3574     // Undo and check.
3575     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3576     CPPUNIT_ASSERT(pUndo);
3577 
3578     pUndo->Undo();
3579     funcCheckOriginal();
3580 
3581     // Redo and check.
3582     pUndo->Redo();
3583     funcCheckDeleted();
3584 
3585     m_pDoc->DeleteTab(0);
3586 }
3587 
testFormulaRefUpdateDeleteAndShiftUp()3588 void TestFormula::testFormulaRefUpdateDeleteAndShiftUp()
3589 {
3590     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3591 
3592     m_pDoc->InsertTab(0, "Test");
3593 
3594     // Insert 1,2,3,4,5 in A3:A7.
3595     for (SCROW i = 0; i <= 4; ++i)
3596         m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3597 
3598     // Insert formula in A8.
3599     ScAddress aPos(0,7,0);
3600     m_pDoc->SetString(aPos, "=SUM(A3:A7)");
3601 
3602     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3603 
3604     // Delete rows 4:5 (middle of the reference).
3605     ScMarkData aMark(m_pDoc->GetSheetLimits());
3606     aMark.SelectOneTable(0);
3607     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3608     bool bDeleted = rFunc.DeleteCells(ScRange(0,3,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3609     CPPUNIT_ASSERT(bDeleted);
3610 
3611     aPos.IncRow(-2);
3612     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3613     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3614 
3615     // Undo and check.
3616     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3617     CPPUNIT_ASSERT(pUndo);
3618 
3619     pUndo->Undo();
3620     aPos.IncRow(2);
3621     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3622     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3623 
3624     // Delete rows 3:4 (top end of the reference).
3625     bDeleted = rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),3,0), &aMark, DelCellCmd::CellsUp, true);
3626     CPPUNIT_ASSERT(bDeleted);
3627 
3628     aPos.IncRow(-2);
3629     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3630     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3631 
3632     // Undo and check again.
3633     pUndo->Undo();
3634     aPos.IncRow(2);
3635     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3636     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3637 
3638     // Delete rows 2:5 (overlaps on the top).
3639     bDeleted = rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3640     CPPUNIT_ASSERT(bDeleted);
3641 
3642     aPos.IncRow(-4);
3643     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3644     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A2:A3)", "Wrong formula!");
3645 
3646     // Undo and check again.
3647     pUndo->Undo();
3648     aPos.IncRow(4);
3649     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3650     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3651 
3652     // Start over with a new scenario.
3653     clearSheet(m_pDoc, 0);
3654 
3655     // Insert 1,2,3,4,5,6 into A3:A8.
3656     for (SCROW i = 0; i <= 5; ++i)
3657         m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3658 
3659     // Set formula in B1.
3660     aPos = ScAddress(0,1,0);
3661     m_pDoc->SetString(aPos, "=SUM(A3:A8)");
3662     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3663 
3664     // Delete rows 6:8 (bottom end of the reference).
3665     bDeleted = rFunc.DeleteCells(ScRange(0,5,0,m_pDoc->MaxCol(),7,0), &aMark, DelCellCmd::CellsUp, true);
3666     CPPUNIT_ASSERT(bDeleted);
3667 
3668     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3669     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3670 
3671     // Undo and check.
3672     pUndo->Undo();
3673     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3674     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A8)", "Wrong formula!");
3675 
3676     // Delete rows 7:9 (overlaps on the bottom).
3677     bDeleted = rFunc.DeleteCells(ScRange(0,6,0,m_pDoc->MaxCol(),8,0), &aMark, DelCellCmd::CellsUp, true);
3678     CPPUNIT_ASSERT(bDeleted);
3679 
3680     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3681     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A6)", "Wrong formula!");
3682 
3683     // Undo and check again.
3684     pUndo->Undo();
3685     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3686     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A8)", "Wrong formula!");
3687 
3688     m_pDoc->DeleteTab(0);
3689 }
3690 
testFormulaRefUpdateName()3691 void TestFormula::testFormulaRefUpdateName()
3692 {
3693     m_pDoc->InsertTab(0, "Formula");
3694 
3695     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3696 
3697     // Fill C2:C5 with values.
3698     m_pDoc->SetValue(ScAddress(2,1,0), 1);
3699     m_pDoc->SetValue(ScAddress(2,2,0), 2);
3700     m_pDoc->SetValue(ScAddress(2,3,0), 3);
3701     m_pDoc->SetValue(ScAddress(2,4,0), 4);
3702 
3703     // Add a named expression that references the immediate left cell.
3704     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
3705     CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
3706     ScRangeData* pName = new ScRangeData(
3707         *m_pDoc, "ToLeft", "RC[-1]", ScAddress(2,1,0),
3708         ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
3709 
3710     bool bInserted = pGlobalNames->insert(pName);
3711     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3712 
3713     // Insert formulas in D2:D5 using the named expression.
3714     m_pDoc->SetString(ScAddress(3,1,0), "=ToLeft");
3715     m_pDoc->SetString(ScAddress(3,2,0), "=ToLeft");
3716     m_pDoc->SetString(ScAddress(3,3,0), "=ToLeft");
3717     m_pDoc->SetString(ScAddress(3,4,0), "=ToLeft");
3718 
3719     // Make sure the results are correct.
3720     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3721     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3722     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3723     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3724 
3725     // Push cells in column C down by one cell.
3726     m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
3727 
3728     // Make sure the results change accordingly.
3729     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(3,1,0));
3730     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,2,0));
3731     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,3,0));
3732     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,4,0));
3733 
3734     // Move cells back.
3735     m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
3736 
3737     // Make sure the results are back as well.
3738     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3739     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3740     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3741     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3742 
3743     // Fill B10:B12 with values.
3744     m_pDoc->SetValue(ScAddress(1,9,0), 10);
3745     m_pDoc->SetValue(ScAddress(1,10,0), 11);
3746     m_pDoc->SetValue(ScAddress(1,11,0), 12);
3747 
3748     // Insert a new named expression that references these values as absolute range.
3749     pName = new ScRangeData(
3750         *m_pDoc, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3751     bInserted = pGlobalNames->insert(pName);
3752     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3753 
3754     // Set formula at C8 that references this named expression.
3755     m_pDoc->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
3756     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3757 
3758     // Shift B10:B12 to right by 2 columns.
3759     m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
3760 
3761     // This should shift the absolute range B10:B12 that MyRange references.
3762     pName = pGlobalNames->findByUpperName("MYRANGE");
3763     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3764     OUString aExpr;
3765     pName->GetSymbol(aExpr);
3766     CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr);
3767 
3768     // This move shouldn't affect the value of C8.
3769     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,7,0));
3770     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3771     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3772 
3773     // Update the value of D10 and make sure C8 gets updated.
3774     m_pDoc->SetValue(ScAddress(3,9,0), 20);
3775     CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3776 
3777     // Insert a new sheet before the current.
3778     m_pDoc->InsertTab(0, "New");
3779     OUString aName;
3780     m_pDoc->GetName(1, aName);
3781     CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3782 
3783     pName = pGlobalNames->findByUpperName("MYRANGE");
3784     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3785 
3786     m_pDoc->SetValue(ScAddress(3,9,1), 10);
3787     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
3788 
3789     // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
3790     m_pDoc->DeleteTab(0);
3791 
3792     aName.clear();
3793     m_pDoc->GetName(0, aName);
3794     CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3795 
3796     pName = pGlobalNames->findByUpperName("MYRANGE");
3797     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3798 
3799     m_pDoc->SetValue(ScAddress(3,9,0), 11);
3800     CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3801 
3802     // Clear all and start over.
3803     clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
3804     pGlobalNames->clear();
3805 
3806     pName = new ScRangeData(
3807         *m_pDoc, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3808     bInserted = pGlobalNames->insert(pName);
3809     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3810     pName->GetSymbol(aExpr);
3811     CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3812 
3813     // Insert range of cells to shift right. The range partially overlaps the named range.
3814     m_pDoc->InsertCol(ScRange(2,4,0,3,8,0));
3815 
3816     // This should not alter the range.
3817     pName->GetSymbol(aExpr);
3818     CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3819 
3820     m_pDoc->DeleteTab(0);
3821 }
3822 
testFormulaRefUpdateNameMove()3823 void TestFormula::testFormulaRefUpdateNameMove()
3824 {
3825     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3826 
3827     m_pDoc->InsertTab(0, "Test");
3828 
3829     // Set values to B2:B4.
3830     m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
3831     m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
3832     m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
3833 
3834     // Set named range for B2:B4.
3835     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
3836     CPPUNIT_ASSERT(bInserted);
3837 
3838     // Set formula in A10.
3839     m_pDoc->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
3840     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3841 
3842     ScRangeData* pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3843     CPPUNIT_ASSERT(pData);
3844     OUString aSymbol;
3845     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3846     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3847 
3848     // Move B2:B4 to D3.
3849     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3850     bool bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3851     CPPUNIT_ASSERT(bMoved);
3852 
3853     // The named range should have moved as well.
3854     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3855     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3856 
3857     // The value of A10 should remain unchanged.
3858     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3859 
3860     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3861     CPPUNIT_ASSERT(pUndoMgr);
3862 
3863     // Undo and check.
3864     pUndoMgr->Undo();
3865 
3866     pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3867     CPPUNIT_ASSERT(pData);
3868     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3869     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3870     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3871 
3872     // Redo and check.
3873     pUndoMgr->Redo();
3874 
3875     pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3876     CPPUNIT_ASSERT(pData);
3877     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3878     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3879     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3880 
3881     // Undo again to bring it back to the initial condition, and clear the undo buffer.
3882     pUndoMgr->Undo();
3883     pUndoMgr->Clear();
3884 
3885     // Add an identical formula to A11 and make a formula group over A10:A11.
3886     m_pDoc->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
3887     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,0));
3888     CPPUNIT_ASSERT(pFC);
3889     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
3890     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
3891 
3892     // Move B2:B4 to D3 again.
3893     bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3894     CPPUNIT_ASSERT(bMoved);
3895 
3896     // Values of A10 and A11 should remain the same.
3897     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3898     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
3899 
3900     // Clear and start over.
3901     clearSheet(m_pDoc, 0);
3902     m_pDoc->GetRangeName()->clear();
3903 
3904     // Set value to B2.
3905     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3906 
3907     // Define B2 as 'MyCell'.
3908     bInserted = m_pDoc->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
3909     CPPUNIT_ASSERT(bInserted);
3910 
3911     // Set formula to B3 that references B2 via MyCell.
3912     m_pDoc->SetString(ScAddress(1,2,0), "=MyCell*2");
3913     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3914 
3915     // Move B2 to D2.
3916     bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,1,0), ScAddress(3,1,0), true, true, false, true);
3917     CPPUNIT_ASSERT(bMoved);
3918 
3919     // Value in B3 should remain unchanged.
3920     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3921 
3922     m_pDoc->DeleteTab(0);
3923 }
3924 
testFormulaRefUpdateNameExpandRef()3925 void TestFormula::testFormulaRefUpdateNameExpandRef()
3926 {
3927     setExpandRefs(true);
3928 
3929     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3930 
3931     m_pDoc->InsertTab(0, "Test");
3932 
3933     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
3934     CPPUNIT_ASSERT(bInserted);
3935 
3936     // Set values to A1:A3.
3937     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3938     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
3939     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
3940 
3941     m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
3942     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
3943 
3944     // Insert a new row at row 4, which should expand the named range to A1:A4.
3945     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3946     ScMarkData aMark(m_pDoc->GetSheetLimits());
3947     aMark.SelectOneTable(0);
3948     rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
3949     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3950     CPPUNIT_ASSERT(pName);
3951     OUString aSymbol;
3952     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3953     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3954 
3955     // Make sure the listening area has been expanded as well.  Note the
3956     // formula cell has been pushed downward by one cell.
3957     m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
3958     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3959 
3960     // Insert a new column at column 2, which should not expand the named
3961     // range as it is only one column wide.
3962     rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
3963     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3964     CPPUNIT_ASSERT(pName);
3965     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3966     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3967 
3968     // Make sure the referenced area has not changed.
3969     m_pDoc->SetValue(ScAddress(0,3,0), 2.0);
3970     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3971     m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
3972     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3973 
3974     // Clear the document and start over.
3975     m_pDoc->GetRangeName()->clear();
3976     clearSheet(m_pDoc, 0);
3977 
3978     // Set values to B4:B6.
3979     m_pDoc->SetValue(ScAddress(1,3,0), 1.0);
3980     m_pDoc->SetValue(ScAddress(1,4,0), 2.0);
3981     m_pDoc->SetValue(ScAddress(1,5,0), 3.0);
3982 
3983     bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
3984     CPPUNIT_ASSERT(bInserted);
3985 
3986     // Set formula to A1.
3987     m_pDoc->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
3988     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,0,0));
3989 
3990     // Insert rows over 3:5 which should expand the range by 3 rows.
3991     rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),4,0), &aMark, INS_INSROWS_BEFORE, false, true);
3992 
3993     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3994     CPPUNIT_ASSERT(pName);
3995 
3996     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3997     CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
3998 
3999     // Clear the document and start over.
4000     m_pDoc->GetRangeName()->clear();
4001     clearSheet(m_pDoc, 0);
4002 
4003     // Set values to A1:A3.
4004     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4005     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
4006     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
4007 
4008     // Name A1:A3 'MyData'.
4009     bInserted = m_pDoc->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
4010     CPPUNIT_ASSERT(bInserted);
4011 
4012     // Set formulas to C1:C2 and E1.
4013     m_pDoc->SetString(ScAddress(2,0,0), "=SUM(MyData)");
4014     m_pDoc->SetString(ScAddress(2,1,0), "=SUM(MyData)");
4015     m_pDoc->SetString(ScAddress(4,0,0), "=SUM(MyData)");
4016 
4017     // C1:C2 should be shared.
4018     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
4019     CPPUNIT_ASSERT(pFC);
4020     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
4021     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
4022 
4023     // E1 should not be shared.
4024     pFC = m_pDoc->GetFormulaCell(ScAddress(4,0,0));
4025     CPPUNIT_ASSERT(pFC);
4026     CPPUNIT_ASSERT(!pFC->IsShared());
4027 
4028     // Check the results.
4029     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4030     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4031     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(4,0,0)));
4032 
4033     // Insert a new row at row 3.  This should expand MyData to A1:A4.
4034     rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, INS_INSROWS_BEFORE, false, true);
4035 
4036     // Set new value to A3.
4037     m_pDoc->SetValue(ScAddress(0,2,0), 4.0);
4038 
4039     // Check the results again.
4040     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4041     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4042     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(4,0,0)));
4043 
4044     m_pDoc->DeleteTab(0);
4045 }
4046 
testFormulaRefUpdateNameExpandRef2()4047 void TestFormula::testFormulaRefUpdateNameExpandRef2()
4048 {
4049     setExpandRefs(true);
4050 
4051     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4052 
4053     m_pDoc->InsertTab(0, "Test");
4054 
4055     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$B$3");
4056     CPPUNIT_ASSERT(bInserted);
4057 
4058     // Insert a new row at row 4, which should expand the named range to A1:A4.
4059     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4060     ScMarkData aMark(m_pDoc->GetSheetLimits());
4061     aMark.SelectOneTable(0);
4062 
4063     // Insert a new column at column 3, which should expand the named
4064     rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
4065     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4066     CPPUNIT_ASSERT(pName);
4067     OUString aSymbol;
4068     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
4069     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$C$3"), aSymbol);
4070 
4071     m_pDoc->DeleteTab(0);
4072 }
4073 
testFormulaRefUpdateNameDeleteRow()4074 void TestFormula::testFormulaRefUpdateNameDeleteRow()
4075 {
4076     m_pDoc->InsertTab(0, "Test");
4077 
4078     // Insert a new name 'MyRange' to reference B2:B4.
4079     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$2:$B$4");
4080     CPPUNIT_ASSERT(bInserted);
4081 
4082     const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4083     CPPUNIT_ASSERT(pName);
4084 
4085     sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4086     const ScTokenArray* pCode = pName->GetCode();
4087     OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4088     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4089 
4090     // Insert a new name 'MyAddress' to reference $B$3. Note absolute row.
4091     bInserted = m_pDoc->InsertNewRangeName("MyAddress", ScAddress(0,0,0), "$B$3");
4092     CPPUNIT_ASSERT(bInserted);
4093 
4094     const ScRangeData* pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
4095     CPPUNIT_ASSERT(pName2);
4096 
4097     sc::TokenStringContext aCxt2(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4098     const ScTokenArray* pCode2 = pName2->GetCode();
4099     OUString aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4100     CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
4101 
4102     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4103 
4104     // Delete row 3.
4105     ScMarkData aMark(m_pDoc->GetSheetLimits());
4106     aMark.SelectOneTable(0);
4107     rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4108 
4109     // The reference in the 'MyRange' name should get updated to B2:B3.
4110     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4111     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
4112 
4113     // The reference in the 'MyAddress' name should get updated to $B$#REF!.
4114     aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4115     CPPUNIT_ASSERT_EQUAL(OUString("$B$#REF!"), aExpr2);
4116 
4117     // Delete row 3 again.
4118     rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4119     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4120     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
4121 
4122     // Undo and check.
4123     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4124     CPPUNIT_ASSERT(pUndoMgr);
4125 
4126     pUndoMgr->Undo();
4127 
4128     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4129     CPPUNIT_ASSERT(pName);
4130     pCode = pName->GetCode();
4131 
4132     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4133     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
4134 
4135     // Undo again and check.
4136     pUndoMgr->Undo();
4137 
4138     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4139     CPPUNIT_ASSERT(pName);
4140     pCode = pName->GetCode();
4141 
4142     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4143     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4144 
4145     // Delete row 2-3.
4146     rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4147 
4148     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4149     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
4150 
4151     // Undo and check.
4152     pUndoMgr->Undo();
4153 
4154     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4155     CPPUNIT_ASSERT(pName);
4156     pCode = pName->GetCode();
4157 
4158     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4159     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4160 
4161     pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
4162     CPPUNIT_ASSERT(pName2);
4163     pCode2 = pName2->GetCode();
4164 
4165     aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4166     CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
4167 
4168     m_pDoc->InsertTab(1, "test2");
4169 
4170     ScMarkData aMark2(m_pDoc->GetSheetLimits());
4171     aMark2.SelectOneTable(1);
4172     rFunc.DeleteCells(ScRange(0,2,1,m_pDoc->MaxCol(),2,1), &aMark2, DelCellCmd::CellsUp, true);
4173 
4174     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4175     CPPUNIT_ASSERT(pName);
4176     pCode = pName->GetCode();
4177 
4178     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4179     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4180 
4181     pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
4182     CPPUNIT_ASSERT(pName2);
4183     pCode2 = pName2->GetCode();
4184 
4185     // Deleting a range the 'MyAddress' name points into due to its implicit
4186     // relative sheet reference to the sheet where used does not invalidate
4187     // the named expression because when updating the sheet reference is
4188     // relative to its base position on sheet 0 (same for the 'MyRange' range,
4189     // which is the reason why it is not updated either).
4190     // This is a tad confusing...
4191     aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4192     CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
4193 
4194     m_pDoc->DeleteTab(1);
4195     m_pDoc->DeleteTab(0);
4196 }
4197 
testFormulaRefUpdateNameCopySheet()4198 void TestFormula::testFormulaRefUpdateNameCopySheet()
4199 {
4200     m_pDoc->InsertTab(0, "Test");
4201     m_pDoc->InsertTab(1, "Test2");
4202 
4203     bool bInserted = m_pDoc->InsertNewRangeName("RED", ScAddress(0,0,0), "$Test.$B$2");
4204     CPPUNIT_ASSERT(bInserted);
4205     bInserted = m_pDoc->InsertNewRangeName("BLUE", ScAddress(0,0,0), "$Test.$B$3");
4206     CPPUNIT_ASSERT(bInserted);
4207     m_pDoc->SetValue(1, 1, 0, 1);
4208     m_pDoc->SetValue(1, 2, 0, 2);
4209 
4210     // insert formula into Test2 that is =RED+BLUE
4211     m_pDoc->SetString(ScAddress(2,2,1), "=RED+BLUE");
4212 
4213     double nVal = m_pDoc->GetValue(2, 2, 1);
4214     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
4215     m_pDoc->CopyTab(1, 0);
4216 
4217     nVal = m_pDoc->GetValue(2, 2, 2);
4218     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
4219 
4220     nVal = m_pDoc->GetValue(2, 2, 0);
4221     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
4222 
4223     m_pDoc->SetValue(1, 1, 1, 3);
4224 
4225     nVal = m_pDoc->GetValue(2, 2, 2);
4226     CPPUNIT_ASSERT_EQUAL(5.0, nVal);
4227 
4228     nVal = m_pDoc->GetValue(2, 2, 0);
4229     CPPUNIT_ASSERT_EQUAL(5.0, nVal);
4230 
4231     m_pDoc->DeleteTab(2);
4232     m_pDoc->DeleteTab(1);
4233     m_pDoc->DeleteTab(0);
4234 
4235     m_pDoc->InsertTab(0, "Test1");
4236     // Global name referencing sheet Test1.
4237     bInserted = m_pDoc->InsertNewRangeName("sheetnumber", ScAddress(0,0,0), "$Test1.$A$1");
4238     CPPUNIT_ASSERT(bInserted);
4239     m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
4240     m_pDoc->SetString(ScAddress(1,0,0), "=sheetnumber");
4241     nVal = m_pDoc->GetValue(1,0,0);
4242     CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet number should be 1", 1.0, nVal);
4243 
4244     // Copy sheet after.
4245     m_pDoc->CopyTab(0, 1);
4246     nVal = m_pDoc->GetValue(1,0,1);
4247     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 2", 2.0, nVal);
4248     nVal = m_pDoc->GetValue(1,0,0);
4249     CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 1", 1.0, nVal);
4250     const ScRangeData* pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
4251     CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
4252 
4253     // Copy sheet before, shifting following now two sheets.
4254     m_pDoc->CopyTab(0, 0);
4255     nVal = m_pDoc->GetValue(1,0,0);
4256     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
4257     pName = m_pDoc->GetRangeName(0)->findByUpperName("SHEETNUMBER");
4258     CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
4259     nVal = m_pDoc->GetValue(1,0,1);
4260     CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 2", 2.0, nVal);
4261     pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
4262     CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should not exist", !pName);
4263     nVal = m_pDoc->GetValue(1,0,2);
4264     CPPUNIT_ASSERT_EQUAL_MESSAGE("Old sheet number should be 3", 3.0, nVal);
4265     pName = m_pDoc->GetRangeName(2)->findByUpperName("SHEETNUMBER");
4266     CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should exist", pName);
4267 
4268     m_pDoc->DeleteTab(2);
4269     m_pDoc->DeleteTab(1);
4270     m_pDoc->DeleteTab(0);
4271 
4272     m_pDoc->InsertTab(0, "Test2");
4273     // Local name referencing sheet Test2.
4274     bInserted = m_pDoc->GetRangeName(0)->insert( new ScRangeData( *m_pDoc, "localname", "$Test2.$A$1"));
4275     CPPUNIT_ASSERT(bInserted);
4276     m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
4277     m_pDoc->SetString(ScAddress(1,0,0), "=localname");
4278     nVal = m_pDoc->GetValue(1,0,0);
4279     CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 1", 1.0, nVal);
4280 
4281     // Insert sheet before and shift sheet with local name.
4282     m_pDoc->InsertTab(0, "Test1");
4283     pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
4284     CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
4285     nVal = m_pDoc->GetValue(1,0,1);
4286     CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 2", 2.0, nVal);
4287 
4288     // Copy sheet before, shifting following now two sheets.
4289     m_pDoc->CopyTab(1, 0);
4290     pName = m_pDoc->GetRangeName(0)->findByUpperName("LOCALNAME");
4291     CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
4292     nVal = m_pDoc->GetValue(1,0,0);
4293     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
4294     pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
4295     CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should not exist", !pName);
4296     pName = m_pDoc->GetRangeName(2)->findByUpperName("LOCALNAME");
4297     CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
4298     nVal = m_pDoc->GetValue(1,0,2);
4299     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 3", 3.0, nVal);
4300 
4301     m_pDoc->DeleteTab(2);
4302     m_pDoc->DeleteTab(1);
4303     m_pDoc->DeleteTab(0);
4304     m_pDoc->SetRangeName(nullptr);
4305 
4306     // Test nested names during copying sheet.
4307 
4308     m_pDoc->InsertTab(0, "Test2");
4309     ScAddress aPos(0,0,0);
4310     bInserted = m_pDoc->InsertNewRangeName( "global", aPos, "$Test2.$A$1");
4311     CPPUNIT_ASSERT(bInserted);
4312     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local", aPos, "$Test2.$A$2");
4313     CPPUNIT_ASSERT(bInserted);
4314     bInserted = m_pDoc->InsertNewRangeName( "global_global", aPos, "global*100");
4315     CPPUNIT_ASSERT(bInserted);
4316     bInserted = m_pDoc->InsertNewRangeName( "global_local", aPos, "local*1000");
4317     CPPUNIT_ASSERT(bInserted);
4318     bInserted = m_pDoc->InsertNewRangeName( "global_unused", aPos, "$Test2.$A$1");
4319     CPPUNIT_ASSERT(bInserted);
4320     bInserted = m_pDoc->InsertNewRangeName( "global_unused_noref", aPos, "42");
4321     CPPUNIT_ASSERT(bInserted);
4322     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_global", aPos, "global*10000");
4323     CPPUNIT_ASSERT(bInserted);
4324     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_local", aPos, "local*100000");
4325     CPPUNIT_ASSERT(bInserted);
4326     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused", aPos, "$Test2.$A$2");
4327     CPPUNIT_ASSERT(bInserted);
4328     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused_noref", aPos, "23");
4329     CPPUNIT_ASSERT(bInserted);
4330 
4331     m_pDoc->SetString(aPos, "=SHEET()");
4332     aPos.IncRow();
4333     m_pDoc->SetString(aPos, "=A1*10+SHEET()");
4334     aPos.IncRow();
4335     m_pDoc->SetString(aPos, "=global_global");
4336     aPos.IncRow();
4337     m_pDoc->SetString(aPos, "=global_local");
4338     aPos.IncRow();
4339     m_pDoc->SetString(aPos, "=local_global");
4340     aPos.IncRow();
4341     m_pDoc->SetString(aPos, "=local_local");
4342 
4343     testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, false);
4344 
4345     // Copy sheet after.
4346     m_pDoc->CopyTab(0, 1);
4347     testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, false);
4348     testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 1, true);
4349 
4350     // Copy sheet before, shifting following now two sheets.
4351     m_pDoc->CopyTab(1, 0);
4352     testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, true);
4353     testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 1, false);
4354     testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 2, true);
4355 
4356     m_pDoc->DeleteTab(2);
4357     m_pDoc->DeleteTab(1);
4358     m_pDoc->DeleteTab(0);
4359 }
4360 
testFormulaRefUpdateSheetLocalMove()4361 void TestFormula::testFormulaRefUpdateSheetLocalMove()
4362 {
4363     SCTAB nSheet1 = 0;
4364     SCTAB nSheet2 = 1;
4365     m_pDoc->InsertTab( nSheet1, "Sheet1");
4366     m_pDoc->InsertTab( nSheet2, "Sheet2");
4367 
4368     ScAddress aPos(0,0,nSheet1);
4369     bool bOk;
4370     bOk = m_pDoc->InsertNewRangeName( 0, "MyCell", aPos, "$Sheet1.$B$2");
4371     CPPUNIT_ASSERT(bOk);
4372     aPos.SetTab(nSheet2);
4373     bOk = m_pDoc->InsertNewRangeName( 1, "MyCell", aPos, "$Sheet2.$B$2");
4374     CPPUNIT_ASSERT(bOk);
4375 
4376     aPos.SetTab(nSheet1);
4377     aPos.IncCol();
4378     m_pDoc->SetString( aPos, "x");
4379     aPos.IncRow();
4380     m_pDoc->SetString( aPos, "1.0");
4381     aPos.IncRow();
4382     m_pDoc->SetString( aPos, "=MyCell");
4383     CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet1.B3", 1.0, m_pDoc->GetValue(aPos));
4384 
4385     aPos.SetTab(nSheet2);
4386     aPos.SetRow(1);
4387     m_pDoc->SetString( aPos, "2.0");
4388     aPos.IncRow();
4389     m_pDoc->SetString( aPos, "=MyCell");
4390     CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet2.B3", 2.0, m_pDoc->GetValue(aPos));
4391 
4392     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4393     OUString aFormula;
4394 
4395     // Move Sheet1.B1 ("x") to Sheet2.B1
4396     bOk = rFunc.MoveBlock( ScRange(1,0,nSheet1,1,0,nSheet1), ScAddress(1,0,nSheet2), true, false, false, false);
4397     CPPUNIT_ASSERT(bOk);
4398     // Results not changed.
4399     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4400     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet2.B3", 2.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4401     // Formulas not changed.
4402     m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4403     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet1.B3", OUString("=MyCell"), aFormula);
4404     m_pDoc->GetFormula( 1,2,nSheet2, aFormula);
4405     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet2.B3", OUString("=MyCell"), aFormula);
4406 
4407     // Move Sheet2.B2 ("2.0") to Sheet1.C2
4408     bOk = rFunc.MoveBlock( ScRange(1,1,nSheet2,1,1,nSheet2), ScAddress(2,1,nSheet1), true, false, false, false);
4409     CPPUNIT_ASSERT(bOk);
4410     // Results not changed.
4411     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4412     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2.B3", 2.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4413     // Formulas not changed.
4414     m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4415     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet1.B3", OUString("=MyCell"), aFormula);
4416     m_pDoc->GetFormula( 1,2,nSheet2, aFormula);
4417     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2.B3", OUString("=MyCell"), aFormula);
4418 
4419     ScRangeData* pName;
4420 
4421     // Check that the sheet-local named reference points to the moved cell, now
4422     // Sheet1.C2
4423     pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4424     CPPUNIT_ASSERT(pName);
4425     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4426     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4427 
4428     // Move Sheet2.B3 ("=MyCell") to Sheet1.C3
4429     bOk = rFunc.MoveBlock( ScRange(1,2,nSheet2,1,2,nSheet2), ScAddress(2,2,nSheet1), true, false, false, false);
4430     CPPUNIT_ASSERT(bOk);
4431     // Results changed.
4432     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4433     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet2.B3", 0.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4434     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4435     // One formula identical, one adjusted.
4436     m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4437     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.B3", OUString("=MyCell"), aFormula);
4438     m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4439     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4440 
4441     // Check that the sheet-local named reference in Sheet1 still points to the
4442     // original cell Sheet1.B2
4443     pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4444     CPPUNIT_ASSERT(pName);
4445     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4446     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4447 
4448     // Check that the sheet-local named reference in Sheet2 still points to the
4449     // moved cell, now Sheet1.C2
4450     pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4451     CPPUNIT_ASSERT(pName);
4452     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4453     CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4454 
4455     // Insert sheet before the others.
4456     m_pDoc->InsertTab(0, "Sheet0");
4457     ++nSheet1;
4458     ++nSheet2;
4459 
4460     // Nothing changed.
4461     CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4462     CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4463     m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4464     CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.B3", OUString("=MyCell"), aFormula);
4465     m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4466     CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4467     pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4468     CPPUNIT_ASSERT(pName);
4469     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4470     CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4471     pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4472     CPPUNIT_ASSERT(pName);
4473     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4474     CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4475 
4476     // Delete sheet before the others.
4477     m_pDoc->DeleteTab(0);
4478     --nSheet1;
4479     --nSheet2;
4480 
4481     // Nothing changed.
4482     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4483     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4484     m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4485     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.B3", OUString("=MyCell"), aFormula);
4486     m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4487     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4488     pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4489     CPPUNIT_ASSERT(pName);
4490     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4491     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4492     pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4493     CPPUNIT_ASSERT(pName);
4494     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4495     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4496 
4497     // Delete last sheet with sheet-local name.
4498     m_pDoc->DeleteTab(nSheet2);
4499 
4500     // XXX we *could* analyze whether the expression points to a different
4501     // sheet and then move the name to a remaining sheet. If so, adapt this
4502     // test.
4503     // Nothing changed except the sheet-local name and its use.
4504     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4505     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", 0.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4506     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", OUString("#NAME?"), m_pDoc->GetString(ScAddress(2,2,nSheet1)));
4507     m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4508     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.B3", OUString("=MyCell"), aFormula);
4509     m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4510     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", OUString("=#NAME?"), aFormula);
4511     pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4512     CPPUNIT_ASSERT(pName);
4513     pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4514     CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4515     CPPUNIT_ASSERT(!m_pDoc->GetRangeName(nSheet2));
4516     nSheet2 = -1;
4517 
4518     m_pDoc->DeleteTab(0);
4519 }
4520 
testFormulaRefUpdateNameDelete()4521 void TestFormula::testFormulaRefUpdateNameDelete()
4522 {
4523     m_pDoc->InsertTab(0, "Test");
4524 
4525     // Insert a new name 'MyRange' to reference B1
4526     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$1");
4527     CPPUNIT_ASSERT(bInserted);
4528 
4529     const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4530     CPPUNIT_ASSERT(pName);
4531 
4532     m_pDoc->DeleteCol(1, 0, 3, 0, 0, 1);
4533     const ScTokenArray* pCode = pName->GetCode();
4534     sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4535     OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4536     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$1"), aExpr);
4537 
4538     m_pDoc->DeleteTab(0);
4539 }
4540 
testFormulaRefUpdateValidity()4541 void TestFormula::testFormulaRefUpdateValidity()
4542 {
4543     struct {
4544 
4545         bool checkList( std::vector<ScTypedStrData>& rList )
4546         {
4547             double aExpected[] = { 1.0, 2.0, 3.0 }; // must be sorted.
4548             size_t nCheckSize = SAL_N_ELEMENTS(aExpected);
4549 
4550             if (rList.size() != nCheckSize)
4551             {
4552                 cerr << "List size is not what is expected." << endl;
4553                 return false;
4554             }
4555 
4556             std::sort(rList.begin(), rList.end(), ScTypedStrData::LessCaseSensitive());
4557 
4558             for (size_t i = 0; i < nCheckSize; ++i)
4559             {
4560                 if (aExpected[i] != rList[i].GetValue())
4561                 {
4562                     cerr << "Incorrect value at position " << i
4563                         << ": expected=" << aExpected[i] << ", actual=" << rList[i].GetValue() << endl;
4564                     return false;
4565                 }
4566             }
4567 
4568             return true;
4569         }
4570 
4571     } aCheck;
4572 
4573     setExpandRefs(false);
4574     setCalcAsShown(m_pDoc, true);
4575 
4576     m_pDoc->InsertTab(0, "Formula");
4577 
4578     // Set values in C2:C4.
4579     m_pDoc->SetValue(ScAddress(2,1,0), 1.0);
4580     m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
4581     m_pDoc->SetValue(ScAddress(2,3,0), 3.0);
4582 
4583     // Set validity in A2.
4584     ScValidationData aData(
4585         SC_VALID_LIST, ScConditionMode::Equal, "C2:C4", "", *m_pDoc, ScAddress(0,1,0), "", "",
4586         m_pDoc->GetGrammar(), m_pDoc->GetGrammar());
4587 
4588     sal_uLong nIndex = m_pDoc->AddValidationEntry(aData);
4589     SfxUInt32Item aItem(ATTR_VALIDDATA, nIndex);
4590 
4591     ScPatternAttr aNewAttrs(
4592         std::make_unique<SfxItemSet>(*m_pDoc->GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{}));
4593     aNewAttrs.GetItemSet().Put(aItem);
4594 
4595     m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
4596 
4597     const ScValidationData* pData = m_pDoc->GetValidationEntry(nIndex);
4598     CPPUNIT_ASSERT(pData);
4599 
4600     // Make sure the list is correct.
4601     std::vector<ScTypedStrData> aList;
4602     pData->FillSelectionList(aList, ScAddress(0,1,0));
4603     bool bGood = aCheck.checkList(aList);
4604     CPPUNIT_ASSERT_MESSAGE("Initial list is incorrect.", bGood);
4605 
4606     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4607     ScMarkData aMark(m_pDoc->GetSheetLimits());
4608     aMark.SelectOneTable(0);
4609 
4610     // Insert a new column at Column B, to move the list from C2:C4 to D2:D4.
4611     bool bInserted = rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, true, true);
4612     CPPUNIT_ASSERT_MESSAGE("Column insertion failed.", bInserted);
4613     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4614     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4615     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,3,0)));
4616 
4617     // Check the list values again.
4618     aList.clear();
4619     pData->FillSelectionList(aList, ScAddress(0,1,0));
4620     bGood = aCheck.checkList(aList);
4621     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after column insertion.", bGood);
4622 
4623     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4624     CPPUNIT_ASSERT(pUndoMgr);
4625 
4626     // Undo and check the list content again.  The list moves back to C2:C4 after the undo.
4627     pUndoMgr->Undo();
4628     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4629     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4630     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4631 
4632     aList.clear();
4633     pData->FillSelectionList(aList, ScAddress(0,1,0));
4634     bGood = aCheck.checkList(aList);
4635     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of column insertion.", bGood);
4636 
4637     // Move C2:C4 to E5:E7.
4638     bool bMoved = rFunc.MoveBlock(ScRange(2,1,0,2,3,0), ScAddress(4,4,0), false, true, false, true);
4639     CPPUNIT_ASSERT(bMoved);
4640     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4,4,0)));
4641     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,5,0)));
4642     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(4,6,0)));
4643 
4644     // Check the list again after the move.
4645     aList.clear();
4646     pData->FillSelectionList(aList, ScAddress(0,1,0));
4647     bGood = aCheck.checkList(aList);
4648     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after moving C2:C4 to E5:E7.", bGood);
4649 
4650     // Undo the move and check.  The list should be back to C2:C4.
4651     pUndoMgr->Undo();
4652     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4653     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4654     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4655 
4656     aList.clear();
4657     pData->FillSelectionList(aList, ScAddress(0,1,0));
4658     bGood = aCheck.checkList(aList);
4659     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of the move.", bGood);
4660 
4661     m_pDoc->DeleteTab(0);
4662 }
4663 
testTokenArrayRefUpdateMove()4664 void TestFormula::testTokenArrayRefUpdateMove()
4665 {
4666     m_pDoc->InsertTab(0, "Sheet1");
4667     m_pDoc->InsertTab(1, "Sheet2");
4668 
4669     ScAddress aPos(0,0,0); // A1
4670 
4671     sc::TokenStringContext aCxt(*m_pDoc, m_pDoc->GetGrammar());
4672 
4673     // Emulate cell movement from Sheet1.C3 to Sheet2.C3.
4674     sc::RefUpdateContext aRefCxt(*m_pDoc);
4675     aRefCxt.meMode = URM_MOVE;
4676     aRefCxt.maRange = ScAddress(2,2,1); // C3 on Sheet2.
4677     aRefCxt.mnTabDelta = -1;
4678 
4679     std::vector<OUString> aTests = {
4680         "B1*C1",
4681         "SUM(B1:C1)",
4682         "$Sheet1.B1",
4683         "SUM(Sheet1.B1:Sheet2.B1)"
4684     };
4685 
4686     // Since C3 is not referenced in any of the above formulas, moving C3 from
4687     // Sheet1 to Sheet2 should NOT change the displayed formula string at all.
4688 
4689     for (const OUString& aTest : aTests)
4690     {
4691         ScCompiler aComp(*m_pDoc, aPos, m_pDoc->GetGrammar());
4692         std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aTest));
4693 
4694         OUString aStr = pArray->CreateString(aCxt, aPos);
4695 
4696         CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4697 
4698         // This formula cell isn't moving its position. The displayed formula
4699         // string should not change.
4700         pArray->AdjustReferenceOnMove(aRefCxt, aPos, aPos);
4701 
4702         aStr = pArray->CreateString(aCxt, aPos);
4703         CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4704     }
4705 
4706     m_pDoc->DeleteTab(1);
4707     m_pDoc->DeleteTab(0);
4708 }
4709 
testMultipleOperations()4710 void TestFormula::testMultipleOperations()
4711 {
4712     m_pDoc->InsertTab(0, "MultiOp");
4713 
4714     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4715 
4716     // Insert the reference formula at top row.
4717     m_pDoc->SetValue(ScAddress(0,0,0), 1);
4718     m_pDoc->SetString(ScAddress(1,0,0), "=A1*10");
4719     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4720 
4721     // Insert variable inputs in A3:A5.
4722     m_pDoc->SetValue(ScAddress(0,2,0), 2);
4723     m_pDoc->SetValue(ScAddress(0,3,0), 3);
4724     m_pDoc->SetValue(ScAddress(0,4,0), 4);
4725 
4726     // Set multiple operations range.
4727     ScTabOpParam aParam;
4728     aParam.aRefFormulaCell = ScRefAddress(1,0,0);
4729     aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4730     aParam.aRefColCell = ScRefAddress(0,0,0);
4731     ScMarkData aMark(m_pDoc->GetSheetLimits());
4732     aMark.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
4733     m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4734     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(1,2,0));
4735     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,3,0));
4736     CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,4,0));
4737 
4738     // Clear A3:B5.
4739     clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
4740 
4741     // This time, use indirect reference formula cell.
4742     m_pDoc->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
4743     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4744 
4745     // Insert variable inputs in A3:A5.
4746     m_pDoc->SetValue(ScAddress(0,2,0), 3);
4747     m_pDoc->SetValue(ScAddress(0,3,0), 4);
4748     m_pDoc->SetValue(ScAddress(0,4,0), 5);
4749 
4750     // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
4751     aParam.aRefFormulaCell.Set(2,0,0,false,false,false);
4752     aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4753     m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4754     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,2,0));
4755     CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,3,0));
4756     CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(1,4,0));
4757 
4758     m_pDoc->DeleteTab(0);
4759 }
4760 
testFuncCOLUMN()4761 void TestFormula::testFuncCOLUMN()
4762 {
4763     m_pDoc->InsertTab(0, "Formula");
4764     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4765 
4766     m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
4767     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4768 
4769     m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4770     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4771 
4772     // Move the formula cell with COLUMN() function to change its value.
4773     m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4774     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(6,10,0)));
4775 
4776     // The cell that references the moved cell should update its value as well.
4777     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4778 
4779     // Move the column in the other direction.
4780     m_pDoc->DeleteCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4781 
4782     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4783 
4784     // The cell that references the moved cell should update its value as well.
4785     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4786 
4787     m_pDoc->DeleteTab(0);
4788 }
4789 
testFuncCOUNT()4790 void TestFormula::testFuncCOUNT()
4791 {
4792     m_pDoc->InsertTab(0, "Formula");
4793     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4794 
4795     m_pDoc->SetValue(ScAddress(0,0,0), 2); // A1
4796     m_pDoc->SetValue(ScAddress(0,1,0), 4); // A2
4797     m_pDoc->SetValue(ScAddress(0,2,0), 6); // A3
4798 
4799     ScAddress aPos(1,0,0);
4800     m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
4801     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
4802 
4803     aPos.IncRow();
4804     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
4805     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
4806 
4807     aPos.IncRow();
4808     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
4809     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
4810 
4811     aPos.IncRow();
4812     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
4813     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
4814 
4815     // Matrix in C1.
4816     ScMarkData aMark(m_pDoc->GetSheetLimits());
4817     aMark.SelectOneTable(0);
4818     m_pDoc->InsertMatrixFormula(2, 0, 2, 0, aMark, "=COUNT(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4819     // Check that the #VALUE! error of "a" not found in "b" is not counted.
4820     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4821 
4822     // Matrix in C3.
4823     m_pDoc->InsertMatrixFormula(2, 2, 2, 2, aMark, "=COUNTA(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4824     // Check that the #VALUE! error of "a" not found in "b" is counted.
4825     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4826 
4827     m_pDoc->DeleteTab(0);
4828 }
4829 
testFuncCOUNTBLANK()4830 void TestFormula::testFuncCOUNTBLANK()
4831 {
4832     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4833     m_pDoc->InsertTab(0, "Formula");
4834 
4835     std::vector<std::vector<const char*>> aData = {
4836         { "1", nullptr, "=B1", "=\"\"" },
4837         { "2", nullptr, "=B2", "=\"\"" },
4838         { "A", nullptr, "=B3", "=\"\"" },
4839         { "B", nullptr, "=B4", "=D3" },
4840         {   nullptr, nullptr, "=B5", "=D4" },
4841         { "=COUNTBLANK(A1:A5)", "=COUNTBLANK(B1:B5)", "=COUNTBLANK(C1:C5)", "=COUNTBLANK(D1:D5)" }
4842     };
4843 
4844     ScAddress aPos(0,0,0);
4845     ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
4846     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4847 
4848     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4849     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,5,0)));
4850     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,5,0)));
4851     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(3,5,0)));
4852 
4853     // Test single cell reference cases.
4854 
4855     clearSheet(m_pDoc, 0);
4856 
4857     std::vector<std::vector<const char*>> aData2 = {
4858         { "1",     "=COUNTBLANK(A1)" },
4859         { "A",     "=COUNTBLANK(A2)" },
4860         {   nullptr,     "=COUNTBLANK(A3)" },
4861         { "=\"\"", "=COUNTBLANK(A4)" },
4862         { "=A4"  , "=COUNTBLANK(A5)" },
4863     };
4864 
4865     aRange = insertRangeData(m_pDoc, aPos, aData2);
4866     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4867 
4868     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4869     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4870     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4871     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
4872     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
4873 
4874     m_pDoc->DeleteTab(0);
4875 }
4876 
testFuncROW()4877 void TestFormula::testFuncROW()
4878 {
4879     m_pDoc->InsertTab(0, "Formula");
4880     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4881 
4882     m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
4883     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4884 
4885     m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4886     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4887 
4888     // Insert 2 new rows at row 4.
4889     m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4890     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(5,12,0)));
4891 
4892     // The cell that references the moved cell should update its value as well.
4893     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4894 
4895     // Delete 2 rows to move it back.
4896     m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4897 
4898     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4899 
4900     // The cell that references the moved cell should update its value as well.
4901     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4902 
4903     // Clear sheet and start over.
4904     clearSheet(m_pDoc, 0);
4905 
4906     m_pDoc->SetString(ScAddress(0,1,0), "=ROW(A5)");
4907     m_pDoc->SetString(ScAddress(1,1,0), "=ROW(B5)");
4908     m_pDoc->SetString(ScAddress(1,2,0), "=ROW(B6)");
4909     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4910     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4911     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4912 
4913     // B2:B3 should be shared.
4914     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
4915     CPPUNIT_ASSERT(pFC);
4916     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
4917     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
4918 
4919     // Insert a new row at row 4.
4920     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4921     ScMarkData aMark(m_pDoc->GetSheetLimits());
4922     aMark.SelectOneTable(0);
4923     rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
4924     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "ROW(A6)", "Wrong formula!");
4925     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "ROW(B6)", "Wrong formula!");
4926     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "ROW(B7)", "Wrong formula!");
4927 
4928     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4929     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4930     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4931 
4932     m_pDoc->DeleteTab(0);
4933 }
4934 
testFuncSUM()4935 void TestFormula::testFuncSUM()
4936 {
4937     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4938                             m_pDoc->InsertTab (0, "foo"));
4939 
4940     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
4941 
4942     // Single argument case.
4943     m_pDoc->SetValue(ScAddress(0,0,0), 1);
4944     m_pDoc->SetValue(ScAddress(0,1,0), 1);
4945     m_pDoc->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
4946     m_pDoc->CalcAll();
4947     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4948 
4949     // Multiple argument case.
4950     m_pDoc->SetValue(ScAddress(0,0,0), 1);
4951     m_pDoc->SetValue(ScAddress(0,1,0), 22);
4952     m_pDoc->SetValue(ScAddress(0,2,0), 4);
4953     m_pDoc->SetValue(ScAddress(0,3,0), 5);
4954     m_pDoc->SetValue(ScAddress(0,4,0), 6);
4955 
4956     m_pDoc->SetValue(ScAddress(1,0,0), 3);
4957     m_pDoc->SetValue(ScAddress(1,1,0), 4);
4958     m_pDoc->SetValue(ScAddress(1,2,0), 5);
4959     m_pDoc->SetValue(ScAddress(1,3,0), 6);
4960     m_pDoc->SetValue(ScAddress(1,4,0), 7);
4961 
4962     m_pDoc->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
4963     m_pDoc->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
4964     m_pDoc->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
4965     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(ScAddress(3,0,0)));
4966     CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4967     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4968 
4969     // Clear and start over.
4970     clearRange(m_pDoc, ScRange(0,0,0,3,m_pDoc->MaxRow(),0));
4971 
4972     // SUM needs to take the first error in case the range contains an error.
4973     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4974     m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
4975     m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
4976     m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
4977     CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4978 
4979     // Set #DIV/0! error to A3. A4 should also inherit this error.
4980     m_pDoc->SetString(ScAddress(0,2,0), "=1/0");
4981     FormulaError nErr = m_pDoc->GetErrCode(ScAddress(0,2,0));
4982     CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell should have a division by zero error.",
4983                            int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4984     nErr = m_pDoc->GetErrCode(ScAddress(0,3,0));
4985     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM should have also inherited a div-by-zero error.",
4986                            int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4987 
4988     // Set #NA! to A2. A4 should now inherit this error.
4989     m_pDoc->SetString(ScAddress(0,1,0), "=NA()");
4990     nErr = m_pDoc->GetErrCode(ScAddress(0,1,0));
4991     CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr != FormulaError::NONE);
4992     CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should have inherited the same error as A2.",
4993                            static_cast<int>(nErr), static_cast<int>(m_pDoc->GetErrCode(ScAddress(0,3,0))));
4994 
4995     m_pDoc->DeleteTab(0);
4996 }
4997 
testFuncPRODUCT()4998 void TestFormula::testFuncPRODUCT()
4999 {
5000     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5001 
5002     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab(0, "foo"));
5003 
5004     ScAddress aPos(3, 0, 0);
5005     m_pDoc->SetValue(0, 0, 0, 3.0); // A1
5006     m_pDoc->SetString(aPos, "=PRODUCT(A1)");
5007     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 3.0, m_pDoc->GetValue(aPos));
5008     m_pDoc->SetValue(0, 0, 0, -3.0); // A1
5009     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -3.0, m_pDoc->GetValue(aPos));
5010     m_pDoc->SetString(aPos, "=PRODUCT(B1)");
5011     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos));
5012     m_pDoc->SetValue(1, 0, 0, 10.0); // B1
5013     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 10.0, m_pDoc->GetValue(aPos));
5014 
5015     m_pDoc->SetString(aPos, "=PRODUCT(A1:C3)");
5016     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -30.0, m_pDoc->GetValue(aPos));
5017     m_pDoc->SetValue(1, 1, 0, -1.0); // B2
5018     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 30.0, m_pDoc->GetValue(aPos));
5019     m_pDoc->SetValue(2, 0, 0, 4.0); // C1
5020     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 120.0, m_pDoc->GetValue(aPos));
5021     m_pDoc->SetValue(0, 1, 0, -2.0); // A2
5022     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -240.0, m_pDoc->GetValue(aPos));
5023     m_pDoc->SetValue(2, 1, 0, 8.0); // C2
5024     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -1920.0, m_pDoc->GetValue(aPos));
5025     m_pDoc->SetValue(0, 2, 0, 0.2); // A3
5026     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -384.0, m_pDoc->GetValue(aPos), 10e-4);
5027     m_pDoc->SetValue(1, 2, 0, -0.25); // B3
5028     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 96.0, m_pDoc->GetValue(aPos), 10e-4);
5029     m_pDoc->SetValue(2, 2, 0, -0.125); // C3
5030     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -12.0, m_pDoc->GetValue(aPos), 10e-4);
5031     m_pDoc->SetValue(2, 2, 0, 0.0); // C3
5032     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos), 10e-4);
5033 
5034     m_pDoc->SetString(aPos, "=PRODUCT({2;3;4})");
5035     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 24.0, m_pDoc->GetValue(aPos));
5036     m_pDoc->SetString(aPos, "=PRODUCT({2;-2;2})");
5037     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -8.0, m_pDoc->GetValue(aPos));
5038     m_pDoc->SetString(aPos, "=PRODUCT({8;0.125;-1})");
5039     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -1.0, m_pDoc->GetValue(aPos));
5040 
5041     m_pDoc->SetString(aPos, "=PRODUCT({2;3};{4;5})");
5042     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 120.0, m_pDoc->GetValue(aPos));
5043     m_pDoc->SetString(aPos, "=PRODUCT({10;-8};{3;-1};{15;30};{7})");
5044     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 756000.0, m_pDoc->GetValue(aPos));
5045     m_pDoc->SetString(aPos, "=PRODUCT({10;-0.1;8};{0.125;4;0.25;2};{0.5};{1};{-1})");
5046     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 1.0, m_pDoc->GetValue(aPos));
5047 
5048     m_pDoc->DeleteTab(0);
5049 }
5050 
testFuncSUMPRODUCT()5051 void TestFormula::testFuncSUMPRODUCT()
5052 {
5053     m_pDoc->InsertTab(0, "Test");
5054 
5055     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5056 
5057     ScAddress aPos(0,0,0);
5058     m_pDoc->SetString(aPos, "=SUMPRODUCT(B1:B3;C1:C3)");
5059     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
5060     m_pDoc->SetValue(ScAddress(2,0,0),  1.0); // C1
5061     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
5062     m_pDoc->SetValue(ScAddress(1,0,0),  1.0); // B1
5063     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
5064     m_pDoc->SetValue(ScAddress(1,1,0),  2.0); // B2
5065     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
5066     m_pDoc->SetValue(ScAddress(2,1,0),  3.0); // C2
5067     CPPUNIT_ASSERT_EQUAL(7.0,  m_pDoc->GetValue(aPos));
5068     m_pDoc->SetValue(ScAddress(2,2,0), -2.0); // C3
5069     CPPUNIT_ASSERT_EQUAL(7.0,  m_pDoc->GetValue(aPos));
5070     m_pDoc->SetValue(ScAddress(1,2,0),  5.0); // B3
5071     CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc->GetValue(aPos));
5072 
5073     // Force an error in C2 and test ForcedArray matrix error propagation.
5074     m_pDoc->SetString( 2, 1, 0, "=1/0");
5075     FormulaError nError = m_pDoc->GetErrCode(aPos);
5076     CPPUNIT_ASSERT_MESSAGE("Formula result should be a propagated error", nError != FormulaError::NONE);
5077 
5078     // Test ForceArray propagation of SUMPRODUCT parameters to ABS and + operator.
5079     // => ABS({-3,4})*({-3,4}+{-3,4}) => {3,4}*{-6,8} => {-18,32} => 14
5080     m_pDoc->SetValue(ScAddress(4,0,0), -3.0); // E1
5081     m_pDoc->SetValue(ScAddress(4,1,0),  4.0); // E2
5082     // Non-intersecting formula in F3.
5083     m_pDoc->SetString(ScAddress(5,2,0), "=SUMPRODUCT(ABS(E1:E2);E1:E2+E1:E2)");
5084     CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(ScAddress(5,2,0)));
5085 
5086     m_pDoc->DeleteTab(0);
5087 }
5088 
testFuncSUMXMY2()5089 void TestFormula::testFuncSUMXMY2()
5090 {
5091     m_pDoc->InsertTab(0, "Test SumXMY2");
5092 
5093     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5094 
5095     ScAddress aPos(0,0,0);
5096     m_pDoc->SetString(aPos, "=SUMXMY2(B1:B3;C1:C3)");
5097     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
5098     m_pDoc->SetValue(ScAddress(1,0,0),  1.0); // B1
5099     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
5100     m_pDoc->SetValue(ScAddress(1,1,0),  2.0); // B2
5101     CPPUNIT_ASSERT_EQUAL(5.0,  m_pDoc->GetValue(aPos));
5102     m_pDoc->SetValue(ScAddress(1,2,0),  3.0); // B3
5103     CPPUNIT_ASSERT_EQUAL(14.0,  m_pDoc->GetValue(aPos));
5104     m_pDoc->SetValue(ScAddress(2,0,0), -1.0); // C1
5105     CPPUNIT_ASSERT_EQUAL(17.0,  m_pDoc->GetValue(aPos));
5106     m_pDoc->SetValue(ScAddress(2,1,0),  3.0); // C2
5107     CPPUNIT_ASSERT_EQUAL(14.0,  m_pDoc->GetValue(aPos));
5108     m_pDoc->SetValue(ScAddress(2,2,0),  1.0); // C3
5109     CPPUNIT_ASSERT_EQUAL(9.0,  m_pDoc->GetValue(aPos));
5110 
5111     double result = 0.0;
5112     m_pDoc->SetString(0, 4, 0, "=SUMXMY2({2;3;4};{4;3;2})");
5113     m_pDoc->GetValue(0, 4, 0, result);
5114     ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of SUMXMY2 with inline arrays failed", 8.0, result);
5115 
5116     m_pDoc->DeleteTab(0);
5117 }
5118 
testFuncMIN()5119 void TestFormula::testFuncMIN()
5120 {
5121     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5122     m_pDoc->InsertTab(0, "Formula");
5123 
5124     // A1:A2
5125     m_pDoc->SetString(ScAddress(0,0,0), "a");
5126     m_pDoc->SetString(ScAddress(0,1,0), "b");
5127 
5128     // B1:B2
5129     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
5130     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
5131 
5132     // Matrix in C1:C2.
5133     ScMarkData aMark(m_pDoc->GetSheetLimits());
5134     aMark.SelectOneTable(0);
5135     m_pDoc->InsertMatrixFormula(2, 0, 2, 1, aMark, "=MIN(IF(A1:A2=\"c\";B1:B2))");
5136 
5137     // Formula cell in C1:C2 should be a 1x2 matrix array.
5138     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
5139     CPPUNIT_ASSERT(pFC);
5140     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be an array.", ScMatrixMode::Formula, pFC->GetMatrixFlag());
5141 
5142     SCCOL nCols;
5143     SCROW nRows;
5144     pFC->GetMatColsRows(nCols, nRows);
5145     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
5146     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRows);
5147 
5148     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C1 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,0,0))));
5149     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C2 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,1,0))));
5150 
5151     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5152     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
5153 
5154     // Inline array input (A4).
5155     m_pDoc->SetString(ScAddress(0,3,0), "=MIN({-2;4;3})");
5156     CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
5157 
5158     // Add more values to B3:B4.
5159     m_pDoc->SetValue(ScAddress(1,2,0),  20.0);
5160     m_pDoc->SetValue(ScAddress(1,3,0), -20.0);
5161 
5162     // Get the MIN of B1:B4.
5163     m_pDoc->SetString(ScAddress(2,4,0), "=MIN(B1:B4)");
5164     CPPUNIT_ASSERT_EQUAL(-20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
5165 
5166     m_pDoc->DeleteTab(0);
5167 }
5168 
testFuncN()5169 void TestFormula::testFuncN()
5170 {
5171     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5172                             m_pDoc->InsertTab (0, "foo"));
5173 
5174     double result;
5175 
5176     // Clear the area first.
5177     clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
5178 
5179     // Put values to reference.
5180     double val = 0;
5181     m_pDoc->SetValue(0, 0, 0, val);
5182     m_pDoc->SetString(0, 2, 0, "Text");
5183     val = 1;
5184     m_pDoc->SetValue(0, 3, 0, val);
5185     val = -1;
5186     m_pDoc->SetValue(0, 4, 0, val);
5187     val = 12.3;
5188     m_pDoc->SetValue(0, 5, 0, val);
5189     m_pDoc->SetString(0, 6, 0, "'12.3");
5190 
5191     // Cell references
5192     m_pDoc->SetString(1, 0, 0, "=N(A1)");
5193     m_pDoc->SetString(1, 1, 0, "=N(A2)");
5194     m_pDoc->SetString(1, 2, 0, "=N(A3)");
5195     m_pDoc->SetString(1, 3, 0, "=N(A4)");
5196     m_pDoc->SetString(1, 4, 0, "=N(A5)");
5197     m_pDoc->SetString(1, 5, 0, "=N(A6)");
5198     m_pDoc->SetString(1, 6, 0, "=N(A9)");
5199 
5200     // In-line values
5201     m_pDoc->SetString(1, 7, 0, "=N(0)");
5202     m_pDoc->SetString(1, 8, 0, "=N(1)");
5203     m_pDoc->SetString(1, 9, 0, "=N(-1)");
5204     m_pDoc->SetString(1, 10, 0, "=N(123)");
5205     m_pDoc->SetString(1, 11, 0, "=N(\"\")");
5206     m_pDoc->SetString(1, 12, 0, "=N(\"12\")");
5207     m_pDoc->SetString(1, 13, 0, "=N(\"foo\")");
5208 
5209     // Range references
5210     m_pDoc->SetString(2, 2, 0, "=N(A1:A8)");
5211     m_pDoc->SetString(2, 3, 0, "=N(A1:A8)");
5212     m_pDoc->SetString(2, 4, 0, "=N(A1:A8)");
5213     m_pDoc->SetString(2, 5, 0, "=N(A1:A8)");
5214 
5215     // Calculate and check the results.
5216     m_pDoc->CalcAll();
5217     double checks1[] = {
5218         0, 0,  0,    1, -1, 12.3, 0, // cell reference
5219         0, 1, -1, 123,  0,    0, 0   // in-line values
5220     };
5221     for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
5222     {
5223         m_pDoc->GetValue(1, i, 0, result);
5224         bool bGood = result == checks1[i];
5225         if (!bGood)
5226         {
5227             cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
5228             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
5229         }
5230     }
5231     double checks2[] = {
5232         0, 1, -1, 12.3               // range references
5233     };
5234     for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
5235     {
5236         m_pDoc->GetValue(1, i+2, 0, result);
5237         bool bGood = result == checks2[i];
5238         if (!bGood)
5239         {
5240             cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
5241             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
5242         }
5243     }
5244 
5245     m_pDoc->DeleteTab(0);
5246 }
5247 
testFuncCOUNTIF()5248 void TestFormula::testFuncCOUNTIF()
5249 {
5250     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5251 
5252     // COUNTIF (test case adopted from OOo i#36381)
5253 
5254     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5255                             m_pDoc->InsertTab (0, "foo"));
5256 
5257     // Empty A1:A39 first.
5258     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5259 
5260     // Raw data (rows 1 through 9)
5261     const char* aData[] = {
5262         "1999",
5263         "2000",
5264         "0",
5265         "0",
5266         "0",
5267         "2002",
5268         "2001",
5269         "X",
5270         "2002"
5271     };
5272 
5273     SCROW nRows = SAL_N_ELEMENTS(aData);
5274     for (SCROW i = 0; i < nRows; ++i)
5275         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5276 
5277     printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
5278 
5279     // formulas and results
5280     static const struct {
5281         const char* pFormula; double fResult;
5282     } aChecks[] = {
5283         { "=COUNTIF(A1:A12;1999)",       1 },
5284         { "=COUNTIF(A1:A12;2002)",       2 },
5285         { "=COUNTIF(A1:A12;1998)",       0 },
5286         { "=COUNTIF(A1:A12;\">=1999\")", 5 },
5287         { "=COUNTIF(A1:A12;\">1999\")",  4 },
5288         { "=COUNTIF(A1:A12;\"<2001\")",  5 },
5289         { "=COUNTIF(A1:A12;\">0\")",     5 },
5290         { "=COUNTIF(A1:A12;\">=0\")",    8 },
5291         { "=COUNTIF(A1:A12;0)",          3 },
5292         { "=COUNTIF(A1:A12;\"X\")",      1 },
5293         { "=COUNTIF(A1:A12;)",           3 }
5294     };
5295 
5296     nRows = SAL_N_ELEMENTS(aChecks);
5297     for (SCROW i = 0; i < nRows; ++i)
5298     {
5299         SCROW nRow = 20 + i;
5300         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5301     }
5302 
5303     for (SCROW i = 0; i < nRows; ++i)
5304     {
5305         double result;
5306         SCROW nRow = 20 + i;
5307         m_pDoc->GetValue(0, nRow, 0, result);
5308         bool bGood = result == aChecks[i].fResult;
5309         if (!bGood)
5310         {
5311             cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
5312                 << "  expected=" << aChecks[i].fResult << "  actual=" << result << endl;
5313             CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
5314         }
5315     }
5316 
5317     // Don't count empty strings when searching for a number.
5318 
5319     // Clear A1:A2.
5320     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
5321 
5322     m_pDoc->SetString(0, 0, 0, "=\"\"");
5323     m_pDoc->SetString(0, 1, 0, "=COUNTIF(A1;1)");
5324 
5325     double result = m_pDoc->GetValue(0, 1, 0);
5326     ASSERT_DOUBLES_EQUAL_MESSAGE("We shouldn't count empty string as valid number.", 0.0, result);
5327 
5328     // Another test case adopted from fdo#77039.
5329     clearSheet(m_pDoc, 0);
5330 
5331     // Set formula cells with blank results in A1:A4.
5332     for (SCROW i = 0; i <=3; ++i)
5333         m_pDoc->SetString(ScAddress(0,i,0), "=\"\"");
5334 
5335     // Insert formula into A5 to count all cells with empty strings.
5336     m_pDoc->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
5337 
5338     // We should correctly count with empty string key.
5339     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,4,0)));
5340 
5341     // Another test case adopted from tdf#99291, empty array elements should
5342     // not match empty cells, but cells with 0.
5343     clearSheet(m_pDoc, 0);
5344     ScMarkData aMark(m_pDoc->GetSheetLimits());
5345     aMark.SelectOneTable(0);
5346     m_pDoc->InsertMatrixFormula(0,0, 0,1, aMark, "=COUNTIF(B1:B5;C1:C2)");
5347     // As we will be testing for 0.0 values, check that formulas are actually present.
5348     OUString aFormula;
5349     m_pDoc->GetFormula(0,0,0, aFormula);
5350     CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
5351     m_pDoc->GetFormula(0,1,0, aFormula);
5352     CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
5353     // The 0.0 results expected.
5354     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5355     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5356     // 0.0 in B2, 1.0 in B3 and B4
5357     m_pDoc->SetValue( ScAddress(1,1,0), 0.0);
5358     m_pDoc->SetValue( ScAddress(1,2,0), 1.0);
5359     m_pDoc->SetValue( ScAddress(1,3,0), 1.0);
5360     // Matched by 0.0 produced by empty cell in array, and 1.0 in C2.
5361     m_pDoc->SetValue( ScAddress(2,1,0), 1.0);
5362     CPPUNIT_ASSERT_EQUAL_MESSAGE("One cell with 0.0",  1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5363     CPPUNIT_ASSERT_EQUAL_MESSAGE("Two cells with 1.0", 2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5364 
5365     m_pDoc->DeleteTab(0);
5366 }
5367 
testFuncIF()5368 void TestFormula::testFuncIF()
5369 {
5370     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5371 
5372     m_pDoc->InsertTab(0, "Formula");
5373 
5374     m_pDoc->SetString(ScAddress(0,0,0), "=IF(B1=2;\"two\";\"not two\")");
5375     CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
5376     m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
5377     CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
5378     m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
5379     CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
5380 
5381     // Test nested IF in array/matrix if the nested IF condition is a scalar.
5382     ScMarkData aMark(m_pDoc->GetSheetLimits());
5383     aMark.SelectOneTable(0);
5384     m_pDoc->InsertMatrixFormula(0,2, 1,2, aMark, "=IF({1;0};IF(1;23);42)");
5385     // Results must be 23 and 42.
5386     CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0)));
5387     CPPUNIT_ASSERT_EQUAL(42.0, m_pDoc->GetValue(ScAddress(1,2,0)));
5388 
5389     // Test nested IF in array/matrix if nested IF conditions are range
5390     // references, data in A5:C8, matrix formula in D4 so there is no
5391     // implicit intersection between formula and ranges.
5392     {
5393         std::vector<std::vector<const char*>> aData = {
5394             { "1", "1", "16" },
5395             { "0", "1", "32" },
5396             { "1", "0", "64" },
5397             { "0", "0", "128" }
5398         };
5399         ScAddress aPos(0,4,0);
5400         ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
5401         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
5402     }
5403     m_pDoc->InsertMatrixFormula(3,3, 3,3, aMark, "=SUM(IF(A5:A8;IF(B5:B8;C5:C8;0);0))");
5404     // Result must be 16, only the first row matches all criteria.
5405     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(3,3,0)));
5406 
5407     // A11:B11
5408     // Test nested IF in array/matrix if the nested IF has no Else path.
5409     m_pDoc->InsertMatrixFormula(0,10, 1,10, aMark, "=IF(IF({1;0};12);34;56)");
5410     // Results must be 34 and 56.
5411     CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(0,10,0)));
5412     CPPUNIT_ASSERT_EQUAL(56.0, m_pDoc->GetValue(ScAddress(1,10,0)));
5413 
5414     m_pDoc->DeleteTab(0);
5415 }
5416 
testFuncCHOOSE()5417 void TestFormula::testFuncCHOOSE()
5418 {
5419     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5420 
5421     m_pDoc->InsertTab(0, "Formula");
5422 
5423     m_pDoc->SetString(ScAddress(0,0,0), "=CHOOSE(B1;\"one\";\"two\";\"three\")");
5424     FormulaError nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
5425     CPPUNIT_ASSERT_MESSAGE("Formula result should be an error since B1 is still empty.", nError != FormulaError::NONE);
5426     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
5427     CPPUNIT_ASSERT_EQUAL(OUString("one"), m_pDoc->GetString(ScAddress(0,0,0)));
5428     m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
5429     CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
5430     m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
5431     CPPUNIT_ASSERT_EQUAL(OUString("three"), m_pDoc->GetString(ScAddress(0,0,0)));
5432     m_pDoc->SetValue(ScAddress(1,0,0), 4.0);
5433     nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
5434     CPPUNIT_ASSERT_MESSAGE("Formula result should be an error due to out-of-bound input..", nError != FormulaError::NONE);
5435 
5436     m_pDoc->DeleteTab(0);
5437 }
5438 
testFuncIFERROR()5439 void TestFormula::testFuncIFERROR()
5440 {
5441     // IFERROR/IFNA (fdo#56124)
5442 
5443     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5444                             m_pDoc->InsertTab (0, "foo"));
5445 
5446     // Empty A1:A39 first.
5447     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5448 
5449     // Raw data (rows 1 through 12)
5450     const char* aData[] = {
5451         "1",
5452         "e",
5453         "=SQRT(4)",
5454         "=SQRT(-2)",
5455         "=A4",
5456         "=1/0",
5457         "=NA()",
5458         "bar",
5459         "4",
5460         "gee",
5461         "=1/0",
5462         "23"
5463     };
5464 
5465     SCROW nRows = SAL_N_ELEMENTS(aData);
5466     for (SCROW i = 0; i < nRows; ++i)
5467         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5468 
5469     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
5470 
5471     // formulas and results
5472     static const struct {
5473         const char* pFormula; const char* pResult;
5474     } aChecks[] = {
5475         { "=IFERROR(A1;9)",                         "1" },
5476         { "=IFERROR(A2;9)",                         "e" },
5477         { "=IFERROR(A3;9)",                         "2" },
5478         { "=IFERROR(A4;-7)",                       "-7" },
5479         { "=IFERROR(A5;-7)",                       "-7" },
5480         { "=IFERROR(A6;-7)",                       "-7" },
5481         { "=IFERROR(A7;-7)",                       "-7" },
5482         { "=IFNA(A6;9)",                      "#DIV/0!" },
5483         { "=IFNA(A7;-7)",                          "-7" },
5484         { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)",    "4" },
5485         { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
5486         { "{=IFERROR(3*A11:A12;1998)}[0]",       "1998" },  // um... this is not the correct way to insert a
5487         { "{=IFERROR(3*A11:A12;1998)}[1]",         "69" }   // matrix formula, just a place holder, see below
5488     };
5489 
5490     nRows = SAL_N_ELEMENTS(aChecks);
5491     for (SCROW i = 0; i < nRows-2; ++i)
5492     {
5493         SCROW nRow = 20 + i;
5494         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5495     }
5496 
5497     // Create a matrix range in last two rows of the range above, actual data
5498     // of the placeholders.
5499     ScMarkData aMark(m_pDoc->GetSheetLimits());
5500     aMark.SelectOneTable(0);
5501     m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)");
5502 
5503     m_pDoc->CalcAll();
5504 
5505     for (SCROW i = 0; i < nRows; ++i)
5506     {
5507         SCROW nRow = 20 + i;
5508         OUString aResult = m_pDoc->GetString(0, nRow, 0);
5509         CPPUNIT_ASSERT_EQUAL_MESSAGE(
5510             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
5511     }
5512 
5513     const SCCOL nCols = 3;
5514     std::vector<std::vector<const char*>> aData2 = {
5515         { "1", "2",    "3" },
5516         { "4", "=1/0", "6" },
5517         { "7", "8",    "9" }
5518     };
5519     const char* aCheck2[][nCols] = {
5520         { "1", "2",    "3" },
5521         { "4", "Error","6" },
5522         { "7", "8",    "9" }
5523     };
5524 
5525     // Data in C1:E3
5526     ScAddress aPos(2,0,0);
5527     ScRange aRange = insertRangeData(m_pDoc, aPos, aData2);
5528     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
5529 
5530     // Array formula in F4:H6
5531     const SCROW nElems2 = SAL_N_ELEMENTS(aCheck2);
5532     const SCCOL nStartCol = aPos.Col() + nCols;
5533     const SCROW nStartRow = aPos.Row() + nElems2;
5534     m_pDoc->InsertMatrixFormula( nStartCol, nStartRow, nStartCol+nCols-1, nStartRow+nElems2-1, aMark,
5535             "=IFERROR(C1:E3;\"Error\")");
5536 
5537     m_pDoc->CalcAll();
5538 
5539     for (SCCOL nCol = nStartCol; nCol < nStartCol + nCols; ++nCol)
5540     {
5541         for (SCROW nRow = nStartRow; nRow < nStartRow + nElems2; ++nRow)
5542         {
5543             OUString aResult = m_pDoc->GetString( nCol, nRow, 0);
5544             CPPUNIT_ASSERT_EQUAL_MESSAGE( "IFERROR array result",
5545                     OUString::createFromAscii( aCheck2[nRow-nStartRow][nCol-nStartCol]), aResult);
5546         }
5547     }
5548 
5549     m_pDoc->DeleteTab(0);
5550 }
5551 
testFuncSHEET()5552 void TestFormula::testFuncSHEET()
5553 {
5554     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5555                             m_pDoc->InsertTab (SC_TAB_APPEND, "test1"));
5556 
5557     m_pDoc->SetString(0, 0, 0, "=SHEETS()");
5558     m_pDoc->CalcFormulaTree(false, false);
5559     double original;
5560     m_pDoc->GetValue(0, 0, 0, original);
5561 
5562     CPPUNIT_ASSERT_EQUAL_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
5563                            static_cast<SCTAB>(original), m_pDoc->GetTableCount());
5564 
5565     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5566                             m_pDoc->InsertTab (SC_TAB_APPEND, "test2"));
5567 
5568     double modified;
5569     m_pDoc->GetValue(0, 0, 0, modified);
5570     ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
5571                            1.0, modified - original);
5572 
5573     SCTAB nTabCount = m_pDoc->GetTableCount();
5574     m_pDoc->DeleteTab(--nTabCount);
5575 
5576     m_pDoc->GetValue(0, 0, 0, modified);
5577     ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
5578                            0.0, modified - original);
5579 
5580     m_pDoc->DeleteTab(--nTabCount);
5581 }
5582 
testFuncNOW()5583 void TestFormula::testFuncNOW()
5584 {
5585     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5586                             m_pDoc->InsertTab (0, "foo"));
5587 
5588     double val = 1;
5589     m_pDoc->SetValue(0, 0, 0, val);
5590     m_pDoc->SetString(0, 1, 0, "=IF(A1>0;NOW();0");
5591     double now1;
5592     m_pDoc->GetValue(0, 1, 0, now1);
5593     CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
5594 
5595     val = 0;
5596     m_pDoc->SetValue(0, 0, 0, val);
5597     m_pDoc->CalcFormulaTree(false, false);
5598     double zero;
5599     m_pDoc->GetValue(0, 1, 0, zero);
5600     ASSERT_DOUBLES_EQUAL_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", 0.0, zero);
5601 
5602     val = 1;
5603     m_pDoc->SetValue(0, 0, 0, val);
5604     m_pDoc->CalcFormulaTree(false, false);
5605     double now2;
5606     m_pDoc->GetValue(0, 1, 0, now2);
5607     CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
5608 
5609     m_pDoc->DeleteTab(0);
5610 }
5611 
testFuncNUMBERVALUE()5612 void TestFormula::testFuncNUMBERVALUE()
5613 {
5614     // NUMBERVALUE fdo#57180
5615 
5616     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5617                             m_pDoc->InsertTab (0, "foo"));
5618 
5619     // Empty A1:A39 first.
5620     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5621 
5622     // Raw data (rows 1 through 6)
5623     const char* aData[] = {
5624         "1ag9a9b9",
5625         "1ag34 5g g6  78b9%%",
5626         "1 234d56E-2",
5627         "d4",
5628         "54.4",
5629         "1a2b3e1%"
5630     };
5631 
5632     SCROW nRows = SAL_N_ELEMENTS(aData);
5633     for (SCROW i = 0; i < nRows; ++i)
5634         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5635 
5636     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
5637 
5638     // formulas and results
5639     static const struct {
5640         const char* pFormula; const char* pResult;
5641     } aChecks[] = {
5642         { "=NUMBERVALUE(A1;\"b\";\"ag\")",  "199.9" },
5643         { "=NUMBERVALUE(A2;\"b\";\"ag\")",  "134.56789" },
5644         { "=NUMBERVALUE(A2;\"b\";\"g\")",   "#VALUE!" },
5645         { "=NUMBERVALUE(A3;\"d\")",         "12.3456" },
5646         { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
5647         { "=NUMBERVALUE(A4;)",              "Err:502" },
5648         { "=NUMBERVALUE(A5;)",              "Err:502" },
5649         { "=NUMBERVALUE(A6;\"b\";\"a\")",   "1.23" }
5650     };
5651 
5652     nRows = SAL_N_ELEMENTS(aChecks);
5653     for (SCROW i = 0; i < nRows; ++i)
5654     {
5655         SCROW nRow = 20 + i;
5656         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5657     }
5658     m_pDoc->CalcAll();
5659 
5660     for (SCROW i = 0; i < nRows; ++i)
5661     {
5662         SCROW nRow = 20 + i;
5663         OUString aResult = m_pDoc->GetString(0, nRow, 0);
5664         CPPUNIT_ASSERT_EQUAL_MESSAGE(
5665             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
5666     }
5667 
5668     m_pDoc->DeleteTab(0);
5669 }
5670 
testFuncLEN()5671 void TestFormula::testFuncLEN()
5672 {
5673     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5674 
5675     m_pDoc->InsertTab(0, "Formula");
5676 
5677     // Leave A1:A3 empty, and insert an array of LEN in B1:B3 that references
5678     // these empty cells.
5679 
5680     ScMarkData aMark(m_pDoc->GetSheetLimits());
5681     aMark.SelectOneTable(0);
5682     m_pDoc->InsertMatrixFormula(1, 0, 1, 2, aMark, "=LEN(A1:A3)");
5683 
5684     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
5685     CPPUNIT_ASSERT(pFC);
5686     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be a matrix origin.",
5687                            ScMatrixMode::Formula, pFC->GetMatrixFlag());
5688 
5689     // This should be a 1x3 matrix.
5690     SCCOL nCols = -1;
5691     SCROW nRows = -1;
5692     pFC->GetMatColsRows(nCols, nRows);
5693     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
5694     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRows);
5695 
5696     // LEN value should be 0 for an empty cell.
5697     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5698     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
5699     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,2,0)));
5700 
5701     m_pDoc->DeleteTab(0);
5702 }
5703 
testFuncLOOKUP()5704 void TestFormula::testFuncLOOKUP()
5705 {
5706     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5707 
5708     m_pDoc->InsertTab(0, "Test");
5709 
5710     // Raw data
5711     const char* aData[][2] = {
5712         { "=CONCATENATE(\"A\")", "1" },
5713         { "=CONCATENATE(\"B\")", "2" },
5714         { "=CONCATENATE(\"C\")", "3" },
5715         { nullptr, nullptr } // terminator
5716     };
5717 
5718     // Insert raw data into A1:B3.
5719     for (SCROW i = 0; aData[i][0]; ++i)
5720     {
5721         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5722         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5723     }
5724 
5725     const char* aData2[][2] = {
5726         { "A", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5727         { "B", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5728         { "C", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5729         { nullptr, nullptr } // terminator
5730     };
5731 
5732     // Insert check formulas into A5:B7.
5733     for (SCROW i = 0; aData2[i][0]; ++i)
5734     {
5735         m_pDoc->SetString(0, i+4, 0, OUString::createFromAscii(aData2[i][0]));
5736         m_pDoc->SetString(1, i+4, 0, OUString::createFromAscii(aData2[i][1]));
5737     }
5738 
5739     printRange(m_pDoc, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
5740 
5741     // Values for B5:B7 should be 1, 2, and 3.
5742     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,4,0))));
5743     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,5,0))));
5744     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,6,0))));
5745 
5746     ASSERT_DOUBLES_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
5747     ASSERT_DOUBLES_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,5,0)));
5748     ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,6,0)));
5749 
5750     m_pDoc->DeleteTab(0);
5751 }
5752 
testFuncLOOKUParrayWithError()5753 void TestFormula::testFuncLOOKUParrayWithError()
5754 {
5755     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5756     m_pDoc->InsertTab(0, "Test");
5757 
5758     std::vector<std::vector<const char*>> aData = {
5759         { "x", "y", "z" },
5760         { "a", "b", "c" }
5761     };
5762     insertRangeData(m_pDoc, ScAddress(2,1,0), aData);               // C2:E3
5763     m_pDoc->SetString(0,0,0, "=LOOKUP(2;1/(C2:E2<>\"\");C3:E3)");   // A1
5764 
5765     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last column.", OUString("c"), m_pDoc->GetString(0,0,0));
5766     m_pDoc->SetString(4,1,0, "");                                   // E2
5767     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for second last column.", OUString("b"), m_pDoc->GetString(0,0,0));
5768 
5769     m_pDoc->SetString(6,1,0, "one");                                // G2
5770     m_pDoc->SetString(6,5,0, "two");                                // G6
5771     // Creates an interim array {1,#DIV/0!,#DIV/0!,#DIV/0!,1,#DIV/0!,#DIV/0!,#DIV/0!}
5772     m_pDoc->SetString(7,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(G2:G9)));G2:G9)"); // H9
5773     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last row.", OUString("two"), m_pDoc->GetString(7,8,0));
5774 
5775     // Lookup on empty range.
5776     m_pDoc->SetString(9,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(I2:I9)));I2:I9)"); // J9
5777     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find no match.", OUString("#N/A"), m_pDoc->GetString(9,8,0));
5778 
5779     m_pDoc->DeleteTab(0);
5780 }
5781 
testTdf141146()5782 void TestFormula::testTdf141146()
5783 {
5784     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5785     m_pDoc->InsertTab(0, "Test1");
5786     m_pDoc->InsertTab(1, "Test2");
5787 
5788     std::vector<std::vector<const char*>> aData = {
5789         { "k1", "value1"},
5790         { "k2", "value2"},
5791         { "k3", "value3"}
5792     };
5793 
5794     insertRangeData(m_pDoc, ScAddress(0,1,1), aData); // A2:B4
5795     m_pDoc->SetString(4,0,1, "k2");                   // E1
5796 
5797     m_pDoc->SetString(4,1,1, "=LOOKUP(1;1/(A$2:A$4=E$1);1)");
5798     m_pDoc->SetString(4,2,1, "=LOOKUP(E1;A$2:A$4;B2:B4)");
5799     m_pDoc->SetString(4,3,1, "=LOOKUP(1;1/(A$2:A$4=E$1);B2:B4)");
5800 
5801     // Without the fix in place, this test would have failed with
5802     // - Expected: #N/A
5803     // - Actual  :
5804     CPPUNIT_ASSERT_EQUAL(OUString("#N/A"), m_pDoc->GetString(4,1,1));
5805     CPPUNIT_ASSERT_EQUAL(OUString("value2"), m_pDoc->GetString(4,2,1));
5806     CPPUNIT_ASSERT_EQUAL(OUString("value2"), m_pDoc->GetString(4,3,1));
5807 
5808     m_pDoc->DeleteTab(1);
5809     m_pDoc->DeleteTab(0);
5810 }
5811 
testFuncVLOOKUP()5812 void TestFormula::testFuncVLOOKUP()
5813 {
5814     // VLOOKUP
5815 
5816     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5817                             m_pDoc->InsertTab (0, "foo"));
5818 
5819     // Clear A1:F40.
5820     clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
5821 
5822     // Raw data
5823     const char* aData[][2] = {
5824         { "Key", "Val" },
5825         {  "10",   "3" },
5826         {  "20",   "4" },
5827         {  "30",   "5" },
5828         {  "40",   "6" },
5829         {  "50",   "7" },
5830         {  "60",   "8" },
5831         {  "70",   "9" },
5832         {   "B",  "10" },
5833         {   "B",  "11" },
5834         {   "C",  "12" },
5835         {   "D",  "13" },
5836         {   "E",  "14" },
5837         {   "F",  "15" },
5838         { nullptr, nullptr } // terminator
5839     };
5840 
5841     // Insert raw data into A1:B14.
5842     for (SCROW i = 0; aData[i][0]; ++i)
5843     {
5844         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5845         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5846     }
5847 
5848     printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
5849 
5850     // Formula data
5851     static const struct {
5852         const char* pLookup; const char* pFormula; const char* pRes;
5853     } aChecks[] = {
5854         { "Lookup",  "Formula", nullptr },
5855         { "12",      "=VLOOKUP(D2;A2:B14;2;1)",     "3" },
5856         { "29",      "=VLOOKUP(D3;A2:B14;2;1)",     "4" },
5857         { "31",      "=VLOOKUP(D4;A2:B14;2;1)",     "5" },
5858         { "45",      "=VLOOKUP(D5;A2:B14;2;1)",     "6" },
5859         { "56",      "=VLOOKUP(D6;A2:B14;2;1)",     "7" },
5860         { "65",      "=VLOOKUP(D7;A2:B14;2;1)",     "8" },
5861         { "78",      "=VLOOKUP(D8;A2:B14;2;1)",     "9" },
5862         { "Andy",    "=VLOOKUP(D9;A2:B14;2;1)",  "#N/A" },
5863         { "Bruce",   "=VLOOKUP(D10;A2:B14;2;1)",   "11" },
5864         { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)",   "12" },
5865         { "David",   "=VLOOKUP(D12;A2:B14;2;1)",   "13" },
5866         { "Edward",  "=VLOOKUP(D13;A2:B14;2;1)",   "14" },
5867         { "Frank",   "=VLOOKUP(D14;A2:B14;2;1)",   "15" },
5868         { "Henry",   "=VLOOKUP(D15;A2:B14;2;1)",   "15" },
5869         { "100",     "=VLOOKUP(D16;A2:B14;2;1)",    "9" },
5870         { "1000",    "=VLOOKUP(D17;A2:B14;2;1)",    "9" },
5871         { "Zena",    "=VLOOKUP(D18;A2:B14;2;1)",   "15" }
5872     };
5873 
5874     // Insert formula data into D1:E18.
5875     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5876     {
5877         m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
5878         m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
5879     }
5880     m_pDoc->CalcAll();
5881     printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
5882 
5883     // Verify results.
5884     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5885     {
5886         if (i == 0)
5887             // Skip the header row.
5888             continue;
5889 
5890         OUString aRes = m_pDoc->GetString(4, i, 0);
5891         bool bGood = aRes.equalsAscii(aChecks[i].pRes);
5892         if (!bGood)
5893         {
5894             cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
5895                 << "'  expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
5896             CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
5897         }
5898     }
5899 
5900     // Clear the sheet and start over.
5901     clearSheet(m_pDoc, 0);
5902 
5903     // Lookup on sorted data interspersed with empty cells.
5904 
5905     // A1:B8 is the search range.
5906     m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
5907     m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
5908     m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
5909     m_pDoc->SetString(ScAddress(1,2,0), "One");
5910     m_pDoc->SetString(ScAddress(1,4,0), "Two");
5911     m_pDoc->SetString(ScAddress(1,7,0), "Four");
5912 
5913     // D1:D5 contain match values.
5914     m_pDoc->SetValue(ScAddress(3,0,0), 1.0);
5915     m_pDoc->SetValue(ScAddress(3,1,0), 2.0);
5916     m_pDoc->SetValue(ScAddress(3,2,0), 3.0);
5917     m_pDoc->SetValue(ScAddress(3,3,0), 4.0);
5918     m_pDoc->SetValue(ScAddress(3,4,0), 5.0);
5919 
5920     // E1:E5 contain formulas.
5921     m_pDoc->SetString(ScAddress(4,0,0), "=VLOOKUP(D1;$A$1:$B$8;2)");
5922     m_pDoc->SetString(ScAddress(4,1,0), "=VLOOKUP(D2;$A$1:$B$8;2)");
5923     m_pDoc->SetString(ScAddress(4,2,0), "=VLOOKUP(D3;$A$1:$B$8;2)");
5924     m_pDoc->SetString(ScAddress(4,3,0), "=VLOOKUP(D4;$A$1:$B$8;2)");
5925     m_pDoc->SetString(ScAddress(4,4,0), "=VLOOKUP(D5;$A$1:$B$8;2)");
5926     m_pDoc->CalcAll();
5927 
5928     // Check the formula results in E1:E5.
5929     CPPUNIT_ASSERT_EQUAL(OUString("One"), m_pDoc->GetString(ScAddress(4,0,0)));
5930     CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,1,0)));
5931     CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,2,0)));
5932     CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,3,0)));
5933     CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,4,0)));
5934 
5935     // Start over again.
5936     clearSheet(m_pDoc, 0);
5937 
5938     // Set A,B,...,G to A1:A7.
5939     m_pDoc->SetString(ScAddress(0,0,0), "A");
5940     m_pDoc->SetString(ScAddress(0,1,0), "B");
5941     m_pDoc->SetString(ScAddress(0,2,0), "C");
5942     m_pDoc->SetString(ScAddress(0,3,0), "D");
5943     m_pDoc->SetString(ScAddress(0,4,0), "E");
5944     m_pDoc->SetString(ScAddress(0,5,0), "F");
5945     m_pDoc->SetString(ScAddress(0,6,0), "G");
5946 
5947     // Set the formula in C1.
5948     m_pDoc->SetString(ScAddress(2,0,0), "=VLOOKUP(\"C\";A1:A16;1)");
5949     CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc->GetString(ScAddress(2,0,0)));
5950 
5951 
5952     // A21:E24, test position dependent implicit intersection as argument to a
5953     // scalar value parameter in a function that has a ReferenceOrForceArray
5954     // type parameter somewhere else and formula is not in array mode,
5955     // VLOOKUP(Value;ReferenceOrForceArray;...)
5956     std::vector<std::vector<const char*>> aData2 = {
5957         { "1", "one",   "3", "=VLOOKUP(C21:C24;A21:B24;2;0)", "three" },
5958         { "2", "two",   "1", "=VLOOKUP(C21:C24;A21:B24;2;0)", "one"   },
5959         { "3", "three", "4", "=VLOOKUP(C21:C24;A21:B24;2;0)", "four"  },
5960         { "4", "four",  "2", "=VLOOKUP(C21:C24;A21:B24;2;0)", "two"   }
5961     };
5962 
5963     ScAddress aPos2(0,20,0);
5964     ScRange aRange2 = insertRangeData(m_pDoc, aPos2, aData2);
5965     CPPUNIT_ASSERT_EQUAL(aPos2, aRange2.aStart);
5966 
5967     aPos2.SetCol(3);    // column D formula results
5968     for (size_t i=0; i < aData2.size(); ++i)
5969     {
5970         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aData2[i][4]), m_pDoc->GetString(aPos2));
5971         aPos2.IncRow();
5972     }
5973 
5974     m_pDoc->DeleteTab(0);
5975 }
5976 
5977 namespace {
5978 
5979 struct StrStrCheck {
5980     const char* pVal;
5981     const char* pRes;
5982 };
5983 
5984 }
5985 
5986 template<size_t DataSize, size_t FormulaSize, int Type>
runTestMATCH(ScDocument * pDoc,const char * aData[DataSize],const StrStrCheck aChecks[FormulaSize])5987 static void runTestMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
5988 {
5989     size_t nDataSize = DataSize;
5990     for (size_t i = 0; i < nDataSize; ++i)
5991         pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5992 
5993     for (size_t i = 0; i < FormulaSize; ++i)
5994     {
5995         pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
5996 
5997         OUString aFormula = "=MATCH(B" + OUString::number(i+1) + ";A1:A"
5998             + OUString::number(nDataSize) + ";" + OUString::number(Type) + ")";
5999         pDoc->SetString(2, i, 0, aFormula);
6000     }
6001 
6002     pDoc->CalcAll();
6003     printRange(pDoc, ScRange(0, 0, 0, 2, FormulaSize-1, 0), "MATCH");
6004 
6005     // verify the results.
6006     for (size_t i = 0; i < FormulaSize; ++i)
6007     {
6008         OUString aStr = pDoc->GetString(2, i, 0);
6009         if (!aStr.equalsAscii(aChecks[i].pRes))
6010         {
6011             cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
6012                 " criterion='" << aChecks[i].pVal << "'" << endl;
6013             CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
6014         }
6015     }
6016 }
6017 
6018 template<size_t DataSize, size_t FormulaSize, int Type>
runTestHorizontalMATCH(ScDocument * pDoc,const char * aData[DataSize],const StrStrCheck aChecks[FormulaSize])6019 static void runTestHorizontalMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
6020 {
6021     size_t nDataSize = DataSize;
6022     for (size_t i = 0; i < nDataSize; ++i)
6023         pDoc->SetString(i, 0, 0, OUString::createFromAscii(aData[i]));
6024 
6025     for (size_t i = 0; i < FormulaSize; ++i)
6026     {
6027         pDoc->SetString(i, 1, 0, OUString::createFromAscii(aChecks[i].pVal));
6028 
6029         // Assume we don't have more than 26 data columns...
6030         OUString aFormula = "=MATCH(" + OUStringChar(static_cast<sal_Unicode>('A'+i)) + "2;A1:"
6031             + OUStringChar(static_cast<sal_Unicode>('A'+nDataSize)) + "1;" + OUString::number(Type)
6032             + ")";
6033         pDoc->SetString(i, 2, 0, aFormula);
6034     }
6035 
6036     pDoc->CalcAll();
6037     printRange(pDoc, ScRange(0, 0, 0, FormulaSize-1, 2, 0), "MATCH");
6038 
6039     // verify the results.
6040     for (size_t i = 0; i < FormulaSize; ++i)
6041     {
6042         OUString aStr = pDoc->GetString(i, 2, 0);
6043         if (!aStr.equalsAscii(aChecks[i].pRes))
6044         {
6045             cerr << "column " << char('A'+i) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
6046                 " criterion='" << aChecks[i].pVal << "'" << endl;
6047             CPPUNIT_ASSERT_MESSAGE("Unexpected result for horizontal MATCH", false);
6048         }
6049     }
6050 }
6051 
testFuncMATCH()6052 void TestFormula::testFuncMATCH()
6053 {
6054     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6055                             m_pDoc->InsertTab (0, "foo"));
6056 
6057     clearRange(m_pDoc, ScRange(0, 0, 0, 40, 40, 0));
6058     {
6059         // Ascending in-exact match
6060 
6061         // data range (A1:A9)
6062         const char* aData[] = {
6063             "1",
6064             "2",
6065             "3",
6066             "4",
6067             "5",
6068             "6",
6069             "7",
6070             "8",
6071             "9",
6072             "B",
6073             "B",
6074             "C",
6075         };
6076 
6077         // formula (B1:C12)
6078         static const StrStrCheck aChecks[] = {
6079             { "0.8",   "#N/A" },
6080             { "1.2",      "1" },
6081             { "2.3",      "2" },
6082             { "3.9",      "3" },
6083             { "4.1",      "4" },
6084             { "5.99",     "5" },
6085             { "6.1",      "6" },
6086             { "7.2",      "7" },
6087             { "8.569",    "8" },
6088             { "9.59",     "9" },
6089             { "10",       "9" },
6090             { "100",      "9" },
6091             { "Andy",  "#N/A" },
6092             { "Bruce",   "11" },
6093             { "Charlie", "12" }
6094         };
6095 
6096         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
6097         clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
6098         runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
6099         clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
6100     }
6101 
6102     {
6103         // Descending in-exact match
6104 
6105         // data range (A1:A9)
6106         const char* aData[] = {
6107             "D",
6108             "C",
6109             "B",
6110             "9",
6111             "8",
6112             "7",
6113             "6",
6114             "5",
6115             "4",
6116             "3",
6117             "2",
6118             "1"
6119         };
6120 
6121         // formula (B1:C12)
6122         static const StrStrCheck aChecks[] = {
6123             { "10",      "#N/A" },
6124             { "8.9",     "4" },
6125             { "7.8",     "5" },
6126             { "6.7",     "6" },
6127             { "5.5",     "7" },
6128             { "4.6",     "8" },
6129             { "3.3",     "9" },
6130             { "2.2",     "10" },
6131             { "1.1",     "11" },
6132             { "0.8",     "12" },
6133             { "0",       "12" },
6134             { "-2",      "12" },
6135             { "Andy",    "3" },
6136             { "Bruce",   "2" },
6137             { "Charlie", "1" },
6138             { "David", "#N/A" }
6139         };
6140 
6141         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
6142         clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
6143         runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
6144         clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
6145     }
6146 
6147     {
6148         // search range contains leading and trailing empty cell ranges.
6149 
6150         clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
6151 
6152         // A5:A8 contains sorted values.
6153         m_pDoc->SetValue(ScAddress(0,4,0), 1.0);
6154         m_pDoc->SetValue(ScAddress(0,5,0), 2.0);
6155         m_pDoc->SetValue(ScAddress(0,6,0), 3.0);
6156         m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
6157 
6158         // Find value 2 which is in A6.
6159         m_pDoc->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
6160         m_pDoc->CalcAll();
6161 
6162         CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc->GetString(ScAddress(1,0,0)));
6163     }
6164 
6165     {
6166         // Test the ReferenceOrForceArray parameter.
6167 
6168         clearRange(m_pDoc, ScRange(0,0,0,1,7,0));
6169 
6170         // B1:B5 contain numeric values.
6171         m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
6172         m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
6173         m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
6174         m_pDoc->SetValue(ScAddress(1,3,0), 4.0);
6175         m_pDoc->SetValue(ScAddress(1,4,0), 5.0);
6176 
6177         // Find string value "33" in concatenated array, no implicit
6178         // intersection is involved, array is forced.
6179         m_pDoc->SetString(ScAddress(0,5,0), "=MATCH(\"33\";B1:B5&B1:B5)");
6180         m_pDoc->CalcAll();
6181         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
6182     }
6183 
6184     m_pDoc->DeleteTab(0);
6185 }
6186 
testFuncCELL()6187 void TestFormula::testFuncCELL()
6188 {
6189     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6190                             m_pDoc->InsertTab (0, "foo"));
6191 
6192     clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
6193 
6194     {
6195         const char* pContent = "Some random text";
6196         m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
6197         m_pDoc->SetValue(2, 0, 0, 1.2); // Set numeric value to C1;
6198 
6199         // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
6200         StrStrCheck aChecks[] = {
6201             { "=CELL(\"COL\";C10)",           "3" },
6202             { "=CELL(\"COL\";C5:C10)",        "3" },
6203             { "=CELL(\"ROW\";C10)",          "10" },
6204             { "=CELL(\"ROW\";C10:E10)",      "10" },
6205             { "=CELL(\"SHEET\";C10)",         "1" },
6206             { "=CELL(\"ADDRESS\";C10)",   "$C$10" },
6207             { "=CELL(\"CONTENTS\";C10)", pContent },
6208             { "=CELL(\"COLOR\";C10)",         "0" },
6209             { "=CELL(\"TYPE\";C9)",           "b" },
6210             { "=CELL(\"TYPE\";C10)",          "l" },
6211             { "=CELL(\"TYPE\";C1)",           "v" },
6212             { "=CELL(\"PARENTHESES\";C10)",   "0" }
6213         };
6214 
6215         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6216             m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
6217         m_pDoc->CalcAll();
6218 
6219         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6220         {
6221             OUString aVal = m_pDoc->GetString(0, i, 0);
6222             CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
6223         }
6224     }
6225 
6226     m_pDoc->DeleteTab(0);
6227 }
6228 
6229 /** See also test case document fdo#44456 sheet cpearson */
testFuncDATEDIF()6230 void TestFormula::testFuncDATEDIF()
6231 {
6232     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6233                             m_pDoc->InsertTab (0, "foo"));
6234 
6235     std::vector<std::vector<const char*>> aData = {
6236         { "2007-01-01", "2007-01-10",  "d",   "9", "=DATEDIF(A1;B1;C1)" } ,
6237         { "2007-01-01", "2007-01-31",  "m",   "0", "=DATEDIF(A2;B2;C2)" } ,
6238         { "2007-01-01", "2007-02-01",  "m",   "1", "=DATEDIF(A3;B3;C3)" } ,
6239         { "2007-01-01", "2007-02-28",  "m",   "1", "=DATEDIF(A4;B4;C4)" } ,
6240         { "2007-01-01", "2007-12-31",  "d", "364", "=DATEDIF(A5;B5;C5)" } ,
6241         { "2007-01-01", "2007-01-31",  "y",   "0", "=DATEDIF(A6;B6;C6)" } ,
6242         { "2007-01-01", "2008-07-01",  "d", "547", "=DATEDIF(A7;B7;C7)" } ,
6243         { "2007-01-01", "2008-07-01",  "m",  "18", "=DATEDIF(A8;B8;C8)" } ,
6244         { "2007-01-01", "2008-07-01", "ym",   "6", "=DATEDIF(A9;B9;C9)" } ,
6245         { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
6246         { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
6247         { "2007-01-01", "2007-01-31", "md",  "30", "=DATEDIF(A12;B12;C12)" } ,
6248         { "2007-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A13;B13;C13)" } ,
6249         { "2008-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A14;B14;C14)" } ,
6250         { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" }    // fail date1 > date2
6251     };
6252 
6253     clearRange( m_pDoc, ScRange(0, 0, 0, 4, aData.size(), 0));
6254     ScAddress aPos(0,0,0);
6255     ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
6256     CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
6257 
6258     m_pDoc->CalcAll();
6259 
6260     for (size_t i = 0; i < aData.size(); ++i)
6261     {
6262         OUString aVal = m_pDoc->GetString( 4, i, 0);
6263         //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
6264         CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
6265     }
6266 
6267     m_pDoc->DeleteTab(0);
6268 }
6269 
testFuncINDIRECT()6270 void TestFormula::testFuncINDIRECT()
6271 {
6272     OUString aTabName("foo");
6273     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6274                             m_pDoc->InsertTab (0, aTabName));
6275     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
6276 
6277     bool bGood = m_pDoc->GetName(0, aTabName);
6278     CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
6279 
6280     OUString aTest = "Test", aRefErr = "#REF!";
6281     m_pDoc->SetString(0, 10, 0, aTest);
6282     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cell value.", aTest, m_pDoc->GetString(0,10,0));
6283 
6284     OUString aPrefix = "=INDIRECT(\"";
6285 
6286     OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
6287     m_pDoc->SetString(0, 0, 0, aFormula);
6288     aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
6289     m_pDoc->SetString(0, 1, 0, aFormula);
6290     aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
6291     m_pDoc->SetString(0, 2, 0, aFormula);
6292     aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
6293     m_pDoc->SetString(0, 3, 0, aFormula);
6294 
6295     m_pDoc->CalcAll();
6296     {
6297         // Default (for new documents) is to use current formula syntax
6298         // which is Calc A1
6299         const OUString* aChecks[] = {
6300             &aTest, &aRefErr, &aRefErr, &aTest
6301         };
6302 
6303         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6304         {
6305             OUString aVal = m_pDoc->GetString(0, i, 0);
6306             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6307         }
6308     }
6309 
6310     ScCalcConfig aConfig;
6311     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO );
6312     m_pDoc->SetCalcConfig(aConfig);
6313     m_pDoc->CalcAll();
6314     {
6315         // Explicit Calc A1 syntax
6316         const OUString* aChecks[] = {
6317             &aTest, &aRefErr, &aRefErr, &aTest
6318         };
6319 
6320         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6321         {
6322             OUString aVal = m_pDoc->GetString(0, i, 0);
6323             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6324         }
6325     }
6326 
6327     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 );
6328     m_pDoc->SetCalcConfig(aConfig);
6329     m_pDoc->CalcAll();
6330     {
6331         // Excel A1 syntax
6332         const OUString* aChecks[] = {
6333             &aRefErr, &aTest, &aRefErr, &aTest
6334         };
6335 
6336         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6337         {
6338             OUString aVal = m_pDoc->GetString(0, i, 0);
6339             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6340         }
6341     }
6342 
6343     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
6344     m_pDoc->SetCalcConfig(aConfig);
6345     m_pDoc->CalcAll();
6346     {
6347         // Excel R1C1 syntax
6348         const OUString* aChecks[] = {
6349             &aRefErr, &aRefErr, &aTest, &aTest
6350         };
6351 
6352         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6353         {
6354             OUString aVal = m_pDoc->GetString(0, i, 0);
6355             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6356         }
6357     }
6358 
6359     m_pDoc->DeleteTab(0);
6360 }
6361 
6362 // Test case for tdf#83365 - Access across spreadsheet returns Err:504
6363 //
testFuncINDIRECT2()6364 void TestFormula::testFuncINDIRECT2()
6365 {
6366     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6367                             m_pDoc->InsertTab (0, "foo"));
6368     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6369                             m_pDoc->InsertTab (1, "bar"));
6370     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6371                             m_pDoc->InsertTab (2, "baz"));
6372 
6373     m_pDoc->SetValue(0,0,0, 10.0);
6374     m_pDoc->SetValue(0,1,0, 10.0);
6375     m_pDoc->SetValue(0,2,0, 10.0);
6376 
6377     // Fill range bar.$A1:bar.$A10 with 1s
6378     for (SCROW i = 0; i < 10; ++i)
6379         m_pDoc->SetValue(0,i,1, 1.0);
6380 
6381     // Test range triplet (absolute, relative, relative) : (absolute, relative, relative)
6382     m_pDoc->SetString(0,0,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$1),1)");
6383 
6384     // Test range triplet (absolute, relative, relative) : (absolute, absolute, relative)
6385     m_pDoc->SetString(0,1,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$2),1)");
6386 
6387     // Test range triplet (absolute, relative, relative) : (absolute, absolute, absolute)
6388     m_pDoc->SetString(0,2,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$3),1)");
6389 
6390     // Test range triplet (absolute, absolute, relative) : (absolute, relative, relative)
6391     m_pDoc->SetString(0,3,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
6392 
6393     // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
6394     m_pDoc->SetString(0,4,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
6395 
6396     // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
6397     m_pDoc->SetString(0,5,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
6398 
6399     // Test range triplet (absolute, absolute, absolute) : (absolute, relative, relative)
6400     m_pDoc->SetString(0,6,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
6401 
6402     // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, relative)
6403     m_pDoc->SetString(0,7,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
6404 
6405     // Check indirect reference "bar.$A\"&foo.$A$1
6406     m_pDoc->SetString(0,8,2, "=COUNTIF(bar.$A$1:INDIRECT(\"bar.$A\"&foo.$A$1),1)");
6407 
6408     // This case should return illegal argument error because
6409     // they reference 2 different absolute sheets
6410     // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, absolute)
6411     m_pDoc->SetString(0,9,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
6412 
6413     m_pDoc->CalcAll();
6414 
6415     // Loop all formulas and check result = 10.0
6416     for (SCROW i = 0; i < 9; ++i)
6417         CPPUNIT_ASSERT_MESSAGE(OString("Failed to INDIRECT reference formula value: " +
6418                     OString::number(i)).getStr(), m_pDoc->GetValue(0,i,2) != 10.0);
6419 
6420     // Check formula cell error
6421     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,2));
6422     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
6423     CPPUNIT_ASSERT_MESSAGE("This formula cell should be an error.", pFC->GetErrCode() != FormulaError::NONE);
6424 
6425     m_pDoc->DeleteTab(2);
6426     m_pDoc->DeleteTab(1);
6427     m_pDoc->DeleteTab(0);
6428 }
6429 
6430 // Test for tdf#107724 do not propagate an array context from MATCH to INDIRECT
6431 // as INDIRECT returns ParamClass::Reference
testFunc_MATCH_INDIRECT()6432 void TestFormula::testFunc_MATCH_INDIRECT()
6433 {
6434     CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab( 0, "foo"));
6435 
6436     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6437 
6438     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
6439     ScRangeData* pRangeData = new ScRangeData( *m_pDoc, "RoleAssignment", "$D$4:$D$13");
6440     pGlobalNames->insert(pRangeData);
6441 
6442     // D6: data to match, in 3rd row of named range.
6443     m_pDoc->SetString( 3,5,0, "Test1");
6444     // F15: Formula generating indirect reference of corner addresses taking
6445     // row+offset and column from named range, which are not in array context
6446     // thus don't create arrays of offsets.
6447     m_pDoc->SetString( 5,14,0, "=MATCH(\"Test1\";INDIRECT(ADDRESS(ROW(RoleAssignment)+1;COLUMN(RoleAssignment))&\":\"&ADDRESS(ROW(RoleAssignment)+ROWS(RoleAssignment)-1;COLUMN(RoleAssignment)));0)");
6448 
6449     // Match in 2nd row of range offset by 1 expected.
6450     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to not propagate array context from MATCH to INDIRECT",
6451             2.0, m_pDoc->GetValue(5,14,0));
6452 
6453     m_pDoc->DeleteTab(0);
6454 }
6455 
testFormulaDepTracking()6456 void TestFormula::testFormulaDepTracking()
6457 {
6458     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
6459 
6460     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6461 
6462     // B2 listens on D2.
6463     m_pDoc->SetString(1, 1, 0, "=D2");
6464     double val = -999.0; // dummy initial value
6465     m_pDoc->GetValue(1, 1, 0, val);
6466     ASSERT_DOUBLES_EQUAL_MESSAGE("Referencing an empty cell should yield zero.", 0.0, val);
6467 
6468     // Changing the value of D2 should trigger recalculation of B2.
6469     m_pDoc->SetValue(3, 1, 0, 1.1);
6470     m_pDoc->GetValue(1, 1, 0, val);
6471     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 1.1, val);
6472 
6473     // And again.
6474     m_pDoc->SetValue(3, 1, 0, 2.2);
6475     m_pDoc->GetValue(1, 1, 0, val);
6476     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 2.2, val);
6477 
6478     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
6479 
6480     // Now, let's test the range dependency tracking.
6481 
6482     // B2 listens on D2:E6.
6483     m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
6484     m_pDoc->GetValue(1, 1, 0, val);
6485     ASSERT_DOUBLES_EQUAL_MESSAGE("Summing an empty range should yield zero.", 0.0, val);
6486 
6487     // Set value to E3. This should trigger recalc on B2.
6488     m_pDoc->SetValue(4, 2, 0, 2.4);
6489     m_pDoc->GetValue(1, 1, 0, val);
6490     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on single value change.", 2.4, val);
6491 
6492     // Set value to D5 to trigger recalc again.  Note that this causes an
6493     // addition of 1.2 + 2.4 which is subject to binary floating point
6494     // rounding error.  We need to use approxEqual to assess its value.
6495 
6496     m_pDoc->SetValue(3, 4, 0, 1.2);
6497     m_pDoc->GetValue(1, 1, 0, val);
6498     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
6499 
6500     // Change the value of D2 (boundary case).
6501     m_pDoc->SetValue(3, 1, 0, 1.0);
6502     m_pDoc->GetValue(1, 1, 0, val);
6503     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
6504 
6505     // Change the value of E6 (another boundary case).
6506     m_pDoc->SetValue(4, 5, 0, 2.0);
6507     m_pDoc->GetValue(1, 1, 0, val);
6508     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
6509 
6510     // Change the value of D6 (another boundary case).
6511     m_pDoc->SetValue(3, 5, 0, 3.0);
6512     m_pDoc->GetValue(1, 1, 0, val);
6513     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
6514 
6515     // Change the value of E2 (another boundary case).
6516     m_pDoc->SetValue(4, 1, 0, 0.4);
6517     m_pDoc->GetValue(1, 1, 0, val);
6518     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
6519 
6520     // Change the existing non-empty value cell (E2).
6521     m_pDoc->SetValue(4, 1, 0, 2.4);
6522     m_pDoc->GetValue(1, 1, 0, val);
6523     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
6524 
6525     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
6526 
6527     // Now, column-based dependency tracking.  We now switch to the R1C1
6528     // syntax which is easier to use for repeated relative references.
6529 
6530     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
6531 
6532     val = 0.0;
6533     for (SCROW nRow = 1; nRow <= 9; ++nRow)
6534     {
6535         // Static value in column 1.
6536         m_pDoc->SetValue(0, nRow, 0, ++val);
6537 
6538         // Formula in column 2 that references cell to the left.
6539         m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
6540 
6541         // Formula in column 3 that references cell to the left.
6542         m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
6543     }
6544 
6545     // Check formula values.
6546     val = 0.0;
6547     for (SCROW nRow = 1; nRow <= 9; ++nRow)
6548     {
6549         ++val;
6550         ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val, m_pDoc->GetValue(1, nRow, 0));
6551         ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val*2.0, m_pDoc->GetValue(2, nRow, 0));
6552     }
6553 
6554     // Intentionally insert a formula in column 1. This will break column 1's
6555     // uniformity of consisting only of static value cells.
6556     m_pDoc->SetString(0, 4, 0, "=R2C3");
6557     ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(0, 4, 0));
6558     ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(1, 4, 0));
6559     ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc->GetValue(2, 4, 0));
6560 
6561     m_pDoc->DeleteTab(0);
6562 }
6563 
testFormulaDepTracking2()6564 void TestFormula::testFormulaDepTracking2()
6565 {
6566     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
6567 
6568     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6569 
6570     double val = 2.0;
6571     m_pDoc->SetValue(0, 0, 0, val);
6572     val = 4.0;
6573     m_pDoc->SetValue(1, 0, 0, val);
6574     val = 5.0;
6575     m_pDoc->SetValue(0, 1, 0, val);
6576     m_pDoc->SetString(2, 0, 0, "=A1/B1");
6577     m_pDoc->SetString(1, 1, 0, "=B1*C1");
6578 
6579     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
6580 
6581     clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
6582 
6583     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
6584 
6585     m_pDoc->DeleteTab(0);
6586 }
6587 
testFormulaDepTracking3()6588 void TestFormula::testFormulaDepTracking3()
6589 {
6590     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6591 
6592     m_pDoc->InsertTab(0, "Formula");
6593 
6594     std::vector<std::vector<const char*>> aData = {
6595         { "1", "2", "=SUM(A1:B1)", "=SUM(C1:C3)" },
6596         { "3", "4", "=SUM(A2:B2)", nullptr },
6597         { "5", "6", "=SUM(A3:B3)", nullptr },
6598     };
6599 
6600     insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
6601 
6602     // Check the initial formula results.
6603     CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
6604     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(ScAddress(2,1,0)));
6605     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,2,0)));
6606     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(3,0,0)));
6607 
6608     // Change B3 and make sure the change gets propagated to D1.
6609     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6610     rFunc.SetValueCell(ScAddress(1,2,0), 60.0, false);
6611     CPPUNIT_ASSERT_EQUAL(65.0, m_pDoc->GetValue(ScAddress(2,2,0)));
6612     CPPUNIT_ASSERT_EQUAL(75.0, m_pDoc->GetValue(ScAddress(3,0,0)));
6613 
6614     m_pDoc->DeleteTab(0);
6615 }
6616 
testFormulaDepTrackingDeleteRow()6617 void TestFormula::testFormulaDepTrackingDeleteRow()
6618 {
6619     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6620 
6621     m_pDoc->InsertTab(0, "Test");
6622 
6623     // Values in A1:A3.
6624     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
6625     m_pDoc->SetValue(ScAddress(0,1,0), 3.0);
6626     m_pDoc->SetValue(ScAddress(0,2,0), 5.0);
6627 
6628     // SUM(A1:A3) in A5.
6629     m_pDoc->SetString(ScAddress(0,4,0), "=SUM(A1:A3)");
6630 
6631     // A6 to reference A5.
6632     m_pDoc->SetString(ScAddress(0,5,0), "=A5*10");
6633     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
6634     CPPUNIT_ASSERT(pFC);
6635 
6636     // A4 should have a broadcaster with A5 listening to it.
6637     SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,4,0));
6638     CPPUNIT_ASSERT(pBC);
6639     SvtBroadcaster::ListenersType* pListeners = &pBC->GetAllListeners();
6640     CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should have one listener.", size_t(1), pListeners->size());
6641     const SvtListener* pListener = pListeners->at(0);
6642     CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 should be listening to A5.", static_cast<const ScFormulaCell*>(pListener), pFC);
6643 
6644     // Check initial values.
6645     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
6646     CPPUNIT_ASSERT_EQUAL(90.0, m_pDoc->GetValue(ScAddress(0,5,0)));
6647 
6648     // Delete row 2.
6649     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6650     ScMarkData aMark(m_pDoc->GetSheetLimits());
6651     aMark.SelectOneTable(0);
6652     rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
6653 
6654     pBC = m_pDoc->GetBroadcaster(ScAddress(0,3,0));
6655     CPPUNIT_ASSERT_MESSAGE("Broadcaster at A5 should have shifted to A4.", pBC);
6656     pListeners = &pBC->GetAllListeners();
6657     CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should have one listener.", size_t(1), pListeners->size());
6658     pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
6659     CPPUNIT_ASSERT(pFC);
6660     pListener = pListeners->at(0);
6661     CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should be listening to A4.", static_cast<const ScFormulaCell*>(pListener), pFC);
6662 
6663     // Check values after row deletion.
6664     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,3,0)));
6665     CPPUNIT_ASSERT_EQUAL(60.0, m_pDoc->GetValue(ScAddress(0,4,0)));
6666 
6667     m_pDoc->DeleteTab(0);
6668 }
6669 
testFormulaDepTrackingDeleteCol()6670 void TestFormula::testFormulaDepTrackingDeleteCol()
6671 {
6672     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6673 
6674     m_pDoc->InsertTab(0, "Formula");
6675 
6676     std::vector<std::vector<const char*>> aData = {
6677         { "2", "=A1", "=B1" }, // not grouped
6678         { nullptr, nullptr, nullptr },           // empty row to separate the formula groups.
6679         { "3", "=A3", "=B3" }, // grouped
6680         { "4", "=A4", "=B4" }, // grouped
6681     };
6682 
6683     ScAddress aPos(0,0,0);
6684     ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
6685     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
6686 
6687     // Check the initial values.
6688     for (SCCOL i = 0; i <= 2; ++i)
6689     {
6690         CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(i,0,0)));
6691         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(i,2,0)));
6692         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(i,3,0)));
6693     }
6694 
6695     // Make sure B3:B4 and C3:C4 are grouped.
6696     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
6697     CPPUNIT_ASSERT(pFC);
6698     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6699     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6700 
6701     pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
6702     CPPUNIT_ASSERT(pFC);
6703     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6704     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6705 
6706     // Delete column A.  A1, B1, A3:A4 and B3:B4 should all show #REF!.
6707     ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6708     ScMarkData aMark(m_pDoc->GetSheetLimits());
6709     aMark.SelectOneTable(0);
6710     rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
6711 
6712     {
6713         // Expected output table content.  0 = empty cell
6714         std::vector<std::vector<const char*>> aOutputCheck = {
6715             { "#REF!", "#REF!" },
6716             { nullptr,  nullptr },
6717             { "#REF!", "#REF!" },
6718             { "#REF!", "#REF!" },
6719         };
6720 
6721         ScRange aCheckRange(0,0,0,1,3,0);
6722         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after deleting column A");
6723         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6724     }
6725 
6726     // Undo and check the result.
6727     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6728     CPPUNIT_ASSERT(pUndoMgr);
6729     pUndoMgr->Undo();
6730 
6731     {
6732         // Expected output table content.  0 = empty cell
6733         std::vector<std::vector<const char*>> aOutputCheck = {
6734             { "2", "2", "2" },
6735             { nullptr,  nullptr, nullptr },
6736             { "3", "3", "3" },
6737             { "4", "4", "4" },
6738         };
6739 
6740         ScRange aCheckRange(0,0,0,2,3,0);
6741         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after undo");
6742         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6743     }
6744 
6745     // Redo and check.
6746     pUndoMgr->Redo();
6747     {
6748         // Expected output table content.  0 = empty cell
6749         std::vector<std::vector<const char*>> aOutputCheck = {
6750             { "#REF!", "#REF!" },
6751             { nullptr, nullptr },
6752             { "#REF!", "#REF!" },
6753             { "#REF!", "#REF!" },
6754         };
6755 
6756         ScRange aCheckRange(0,0,0,1,3,0);
6757         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after redo");
6758         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6759     }
6760 
6761     // Undo and change the values in column A.
6762     pUndoMgr->Undo();
6763     m_pDoc->SetValue(ScAddress(0,0,0), 22.0);
6764     m_pDoc->SetValue(ScAddress(0,2,0), 23.0);
6765     m_pDoc->SetValue(ScAddress(0,3,0), 24.0);
6766 
6767     {
6768         // Expected output table content.  0 = empty cell
6769         std::vector<std::vector<const char*>> aOutputCheck = {
6770             { "22", "22", "22" },
6771             { nullptr, nullptr, nullptr },
6772             { "23", "23", "23" },
6773             { "24", "24", "24" },
6774         };
6775 
6776         ScRange aCheckRange(0,0,0,2,3,0);
6777         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after undo & value change in column A");
6778         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6779     }
6780 
6781     m_pDoc->DeleteTab(0);
6782 }
6783 
testFormulaMatrixResultUpdate()6784 void TestFormula::testFormulaMatrixResultUpdate()
6785 {
6786     m_pDoc->InsertTab(0, "Test");
6787 
6788     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6789 
6790     // Set a numeric value to A1.
6791     m_pDoc->SetValue(ScAddress(0,0,0), 11.0);
6792 
6793     ScMarkData aMark(m_pDoc->GetSheetLimits());
6794     aMark.SelectOneTable(0);
6795     m_pDoc->InsertMatrixFormula(1, 0, 1, 0, aMark, "=A1");
6796     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
6797     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
6798     CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC);
6799     pFC->SetChanged(false); // Clear this flag to simulate displaying of formula cell value on screen.
6800 
6801     m_pDoc->SetString(ScAddress(0,0,0), "ABC");
6802     CPPUNIT_ASSERT_EQUAL(OUString("ABC"), m_pDoc->GetString(ScAddress(1,0,0)));
6803     pFC->SetChanged(false);
6804 
6805     // Put a new value into A1. The formula should update.
6806     m_pDoc->SetValue(ScAddress(0,0,0), 13.0);
6807     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(1,0,0)));
6808 
6809     m_pDoc->DeleteTab(0);
6810 }
6811 
testExternalRef()6812 void TestFormula::testExternalRef()
6813 {
6814     ScDocShellRef xExtDocSh = new ScDocShell;
6815     xExtDocSh->SetIsInUcalc();
6816     OUString aExtDocName("file:///extdata.fake");
6817     OUString aExtSh1Name("Data1");
6818     OUString aExtSh2Name("Data2");
6819     OUString aExtSh3Name("Data3");
6820     SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6821     xExtDocSh->DoLoad(pMed);
6822     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6823                            findLoadedDocShellByName(aExtDocName) != nullptr);
6824 
6825     // Populate the external source document.
6826     ScDocument& rExtDoc = xExtDocSh->GetDocument();
6827     rExtDoc.InsertTab(0, aExtSh1Name);
6828     rExtDoc.InsertTab(1, aExtSh2Name);
6829     rExtDoc.InsertTab(2, aExtSh3Name);
6830 
6831     OUString const name("Name");
6832     OUString const value("Value");
6833 
6834     // Sheet 1
6835     rExtDoc.SetString(0, 0, 0, name);
6836     rExtDoc.SetString(0, 1, 0, "Andy");
6837     rExtDoc.SetString(0, 2, 0, "Bruce");
6838     rExtDoc.SetString(0, 3, 0, "Charlie");
6839     rExtDoc.SetString(0, 4, 0, "David");
6840     rExtDoc.SetString(1, 0, 0, value);
6841     double val = 10;
6842     rExtDoc.SetValue(1, 1, 0, val);
6843     val = 11;
6844     rExtDoc.SetValue(1, 2, 0, val);
6845     val = 12;
6846     rExtDoc.SetValue(1, 3, 0, val);
6847     val = 13;
6848     rExtDoc.SetValue(1, 4, 0, val);
6849 
6850     // Sheet 2 remains empty.
6851 
6852     // Sheet 3
6853     rExtDoc.SetString(0, 0, 2, name);
6854     rExtDoc.SetString(0, 1, 2, "Edward");
6855     rExtDoc.SetString(0, 2, 2, "Frank");
6856     rExtDoc.SetString(0, 3, 2, "George");
6857     rExtDoc.SetString(0, 4, 2, "Henry");
6858     rExtDoc.SetString(1, 0, 2, value);
6859     val = 99;
6860     rExtDoc.SetValue(1, 1, 2, val);
6861     val = 98;
6862     rExtDoc.SetValue(1, 2, 2, val);
6863     val = 97;
6864     rExtDoc.SetValue(1, 3, 2, val);
6865     val = 96;
6866     rExtDoc.SetValue(1, 4, 2, val);
6867 
6868     // Test external references on the main document while the external
6869     // document is still in memory.
6870     m_pDoc->InsertTab(0, "Test Sheet");
6871     m_pDoc->SetString(0, 0, 0, "='file:///extdata.fake'#Data1.A1");
6872     OUString test = m_pDoc->GetString(0, 0, 0);
6873     CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test, name);
6874 
6875     // After the initial access to the external document, the external ref
6876     // manager should create sheet cache entries for *all* sheets from that
6877     // document.  Note that the doc may have more than 3 sheets but ensure
6878     // that the first 3 are what we expect.
6879     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
6880     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
6881     vector<OUString> aTabNames;
6882     pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
6883     CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
6884     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[0], aExtSh1Name);
6885     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[1], aExtSh2Name);
6886     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[2], aExtSh3Name);
6887 
6888     m_pDoc->SetString(1, 0, 0, "='file:///extdata.fake'#Data1.B1");
6889     test = m_pDoc->GetString(1, 0, 0);
6890     CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test, value);
6891 
6892     m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#Data1.A2");
6893     m_pDoc->SetString(0, 2, 0, "='file:///extdata.fake'#Data1.A3");
6894     m_pDoc->SetString(0, 3, 0, "='file:///extdata.fake'#Data1.A4");
6895     m_pDoc->SetString(0, 4, 0, "='file:///extdata.fake'#Data1.A5");
6896     m_pDoc->SetString(0, 5, 0, "='file:///extdata.fake'#Data1.A6");
6897 
6898     {
6899         // Referencing an empty cell should display '0'.
6900         const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
6901         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6902         {
6903             test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
6904             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6905         }
6906     }
6907     m_pDoc->SetString(1, 1, 0, "='file:///extdata.fake'#Data1.B2");
6908     m_pDoc->SetString(1, 2, 0, "='file:///extdata.fake'#Data1.B3");
6909     m_pDoc->SetString(1, 3, 0, "='file:///extdata.fake'#Data1.B4");
6910     m_pDoc->SetString(1, 4, 0, "='file:///extdata.fake'#Data1.B5");
6911     m_pDoc->SetString(1, 5, 0, "='file:///extdata.fake'#Data1.B6");
6912     {
6913         double pChecks[] = { 10, 11, 12, 13, 0 };
6914         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6915         {
6916             m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
6917             ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected cell value.", pChecks[i], val);
6918         }
6919     }
6920 
6921     m_pDoc->SetString(2, 0, 0, "='file:///extdata.fake'#Data3.A1");
6922     m_pDoc->SetString(2, 1, 0, "='file:///extdata.fake'#Data3.A2");
6923     m_pDoc->SetString(2, 2, 0, "='file:///extdata.fake'#Data3.A3");
6924     m_pDoc->SetString(2, 3, 0, "='file:///extdata.fake'#Data3.A4");
6925     {
6926         const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
6927         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6928         {
6929             test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
6930             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6931         }
6932     }
6933 
6934     m_pDoc->SetString(3, 0, 0, "='file:///extdata.fake'#Data3.B1");
6935     m_pDoc->SetString(3, 1, 0, "='file:///extdata.fake'#Data3.B2");
6936     m_pDoc->SetString(3, 2, 0, "='file:///extdata.fake'#Data3.B3");
6937     m_pDoc->SetString(3, 3, 0, "='file:///extdata.fake'#Data3.B4");
6938     {
6939         const char* pChecks[] = { "Value", "99", "98", "97" };
6940         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6941         {
6942             test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
6943             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6944         }
6945     }
6946 
6947     // At this point, all accessed cell data from the external document should
6948     // have been cached.
6949     ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
6950         nFileId, aExtSh1Name, false);
6951     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab);
6952     ScRange aCachedRange = getCachedRange(pCacheTab);
6953     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6954                            SCCOL(0), aCachedRange.aStart.Col());
6955     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6956                            SCCOL(1), aCachedRange.aEnd.Col());
6957     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6958                            SCROW(0), aCachedRange.aStart.Row());
6959     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6960                            SCROW(4), aCachedRange.aEnd.Row());
6961 
6962     // Sheet2 is not referenced at all; the cache table shouldn't even exist.
6963     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
6964     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", !pCacheTab);
6965 
6966     // Sheet3's row 5 is not referenced; it should not be cached.
6967     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
6968     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab);
6969     aCachedRange = getCachedRange(pCacheTab);
6970     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6971                            SCCOL(0), aCachedRange.aStart.Col());
6972     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6973                            SCCOL(1), aCachedRange.aEnd.Col());
6974     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6975                            SCROW(0), aCachedRange.aStart.Row());
6976     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6977                            SCROW(3), aCachedRange.aEnd.Row());
6978 
6979     // Unload the external document shell.
6980     xExtDocSh->DoClose();
6981     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6982                            !findLoadedDocShellByName(aExtDocName));
6983 
6984     m_pDoc->DeleteTab(0);
6985 }
6986 
testExternalRangeName()6987 void TestFormula::testExternalRangeName()
6988 {
6989     ScDocShellRef xExtDocSh = new ScDocShell;
6990     xExtDocSh->SetIsInUcalc();
6991     OUString const aExtDocName("file:///extdata.fake");
6992     SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6993     xExtDocSh->DoLoad(pMed);
6994     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6995                            findLoadedDocShellByName(aExtDocName) != nullptr);
6996 
6997     ScDocument& rExtDoc = xExtDocSh->GetDocument();
6998     rExtDoc.InsertTab(0, "Data1");
6999     rExtDoc.SetValue(0, 0, 0, 123.456);
7000 
7001     ScRangeName* pRangeName = rExtDoc.GetRangeName();
7002     ScRangeData* pRangeData = new ScRangeData(rExtDoc, "ExternalName",
7003             "$Data1.$A$1");
7004     pRangeName->insert(pRangeData);
7005 
7006     m_pDoc->InsertTab(0, "Test Sheet");
7007     m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#ExternalName");
7008 
7009     double nVal = m_pDoc->GetValue(0, 1, 0);
7010     ASSERT_DOUBLES_EQUAL(123.456, nVal);
7011 
7012     xExtDocSh->DoClose();
7013     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
7014                            !findLoadedDocShellByName(aExtDocName));
7015     m_pDoc->DeleteTab(0);
7016 }
7017 
testExtRefFuncT(ScDocument * pDoc,ScDocument & rExtDoc)7018 static void testExtRefFuncT(ScDocument* pDoc, ScDocument& rExtDoc)
7019 {
7020     clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7021     clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7022 
7023     rExtDoc.SetString(0, 0, 0, "'1.2");
7024     rExtDoc.SetString(0, 1, 0, "Foo");
7025     rExtDoc.SetValue(0, 2, 0, 12.3);
7026     pDoc->SetString(0, 0, 0, "=T('file:///extdata.fake'#Data.A1)");
7027     pDoc->SetString(0, 1, 0, "=T('file:///extdata.fake'#Data.A2)");
7028     pDoc->SetString(0, 2, 0, "=T('file:///extdata.fake'#Data.A3)");
7029     pDoc->CalcAll();
7030 
7031     OUString aRes = pDoc->GetString(0, 0, 0);
7032     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("1.2"), aRes);
7033     aRes = pDoc->GetString(0, 1, 0);
7034     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("Foo"), aRes);
7035     aRes = pDoc->GetString(0, 2, 0);
7036     CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
7037 }
7038 
testExtRefFuncOFFSET(ScDocument * pDoc,ScDocument & rExtDoc)7039 static void testExtRefFuncOFFSET(ScDocument* pDoc, ScDocument& rExtDoc)
7040 {
7041     clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7042     clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7043 
7044     sc::AutoCalcSwitch aACSwitch(*pDoc, true);
7045 
7046     // External document has sheet named 'Data', and the internal doc has sheet named 'Test'.
7047     rExtDoc.SetValue(ScAddress(0,1,0), 1.2); // Set 1.2 to A2.
7048     pDoc->SetString(ScAddress(0,0,0), "=OFFSET('file:///extdata.fake'#Data.$A$1;1;0;1;1)");
7049     CPPUNIT_ASSERT_EQUAL(1.2, pDoc->GetValue(ScAddress(0,0,0)));
7050 }
7051 
testExtRefFuncVLOOKUP(ScDocument * pDoc,ScDocument & rExtDoc)7052 static void testExtRefFuncVLOOKUP(ScDocument* pDoc, ScDocument& rExtDoc)
7053 {
7054     clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7055     clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7056 
7057     // Populate the external document.
7058     rExtDoc.SetString(ScAddress(0,0,0), "A1");
7059     rExtDoc.SetString(ScAddress(0,1,0), "A2");
7060     rExtDoc.SetString(ScAddress(0,2,0), "A3");
7061     rExtDoc.SetString(ScAddress(0,3,0), "A4");
7062     rExtDoc.SetString(ScAddress(0,4,0), "A5");
7063 
7064     rExtDoc.SetString(ScAddress(1,0,0), "B1");
7065     rExtDoc.SetString(ScAddress(1,1,0), "B2");
7066     rExtDoc.SetString(ScAddress(1,2,0), "B3");
7067     rExtDoc.SetString(ScAddress(1,3,0), "B4");
7068     rExtDoc.SetString(ScAddress(1,4,0), "B5");
7069 
7070     // Put formula in the source document.
7071 
7072     pDoc->SetString(ScAddress(0,0,0), "A2");
7073 
7074     // Sort order TRUE
7075     pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;1)");
7076     CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
7077 
7078     // Sort order FALSE. It should return the same result.
7079     pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;0)");
7080     CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
7081 }
7082 
testExtRefConcat(ScDocument * pDoc,ScDocument & rExtDoc)7083 static void testExtRefConcat(ScDocument* pDoc, ScDocument& rExtDoc)
7084 {
7085     clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7086     clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7087 
7088     sc::AutoCalcSwitch aACSwitch(*pDoc, true);
7089 
7090     // String and number
7091     rExtDoc.SetString(ScAddress(0,0,0), "Answer: ");
7092     rExtDoc.SetValue(ScAddress(0,1,0), 42);
7093 
7094     // Concat operation should combine string and number converted to string
7095     pDoc->SetString(ScAddress(0,0,0), "='file:///extdata.fake'#Data.A1 & 'file:///extdata.fake'#Data.A2");
7096     CPPUNIT_ASSERT_EQUAL(OUString("Answer: 42"), pDoc->GetString(ScAddress(0,0,0)));
7097 }
7098 
testExternalRefFunctions()7099 void TestFormula::testExternalRefFunctions()
7100 {
7101     ScDocShellRef xExtDocSh = new ScDocShell;
7102     xExtDocSh->SetIsInUcalc();
7103     OUString aExtDocName("file:///extdata.fake");
7104     SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
7105     xExtDocSh->DoLoad(pMed);
7106     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
7107                            findLoadedDocShellByName(aExtDocName) != nullptr);
7108 
7109     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
7110     CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
7111     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
7112     const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
7113     CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
7114                            pFileName);
7115     CPPUNIT_ASSERT_EQUAL_MESSAGE("file name registration has somehow failed.",
7116                            aExtDocName, *pFileName);
7117 
7118     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7119 
7120     // Populate the external source document.
7121     ScDocument& rExtDoc = xExtDocSh->GetDocument();
7122     rExtDoc.InsertTab(0, "Data");
7123     double val = 1;
7124     rExtDoc.SetValue(0, 0, 0, val);
7125     // leave cell B1 empty.
7126     val = 2;
7127     rExtDoc.SetValue(0, 1, 0, val);
7128     rExtDoc.SetValue(1, 1, 0, val);
7129     val = 3;
7130     rExtDoc.SetValue(0, 2, 0, val);
7131     rExtDoc.SetValue(1, 2, 0, val);
7132     val = 4;
7133     rExtDoc.SetValue(0, 3, 0, val);
7134     rExtDoc.SetValue(1, 3, 0, val);
7135 
7136     m_pDoc->InsertTab(0, "Test");
7137 
7138     static const struct {
7139         const char* pFormula; double fResult;
7140     } aChecks[] = {
7141         { "=SUM('file:///extdata.fake'#Data.A1:A4)",     10 },
7142         { "=SUM('file:///extdata.fake'#Data.B1:B4)",     9 },
7143         { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
7144         { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
7145         { "=COUNT('file:///extdata.fake'#Data.A1:A4)",   4 },
7146         { "=COUNT('file:///extdata.fake'#Data.B1:B4)",   3 },
7147         // Should not crash, MUST be 0,m_pDoc->MaxRow() and/or 0,m_pDoc->MaxCol() range (here both)
7148         // to yield a result instead of 1x1 error matrix.
7149         { "=SUM('file:///extdata.fake'#Data.1:1048576)", 19 }
7150     };
7151 
7152     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
7153     {
7154         m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
7155         m_pDoc->GetValue(0, 0, 0, val);
7156         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected result involving external ranges.", aChecks[i].fResult, val, 1e-15);
7157     }
7158 
7159     // A huge external range should not crash, the matrix generated from the
7160     // external range reference should be 1x1 and have one error value.
7161     // XXX NOTE: in case we supported sparse matrix that can hold this large
7162     // areas these tests may be adapted.
7163     m_pDoc->SetString(0, 0, 0, "=SUM('file:///extdata.fake'#Data.B1:AMJ1048575)");
7164     ScFormulaCell* pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
7165     FormulaError nErr = pFC->GetErrCode();
7166     CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize), static_cast<int>(nErr));
7167 
7168     ScMarkData aMark(m_pDoc->GetSheetLimits());
7169     aMark.SelectOneTable(0);
7170     m_pDoc->InsertMatrixFormula(0,0,0,0, aMark, "'file:///extdata.fake'#Data.B1:AMJ1048575");
7171     pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
7172     nErr = pFC->GetErrCode();
7173     CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize), static_cast<int>(nErr));
7174     SCSIZE nMatCols, nMatRows;
7175     const ScMatrix* pMat = pFC->GetMatrix();
7176     CPPUNIT_ASSERT_MESSAGE("matrix expected", pMat != nullptr);
7177     pMat->GetDimensions( nMatCols, nMatRows);
7178     CPPUNIT_ASSERT_EQUAL_MESSAGE("1x1 matrix expected", SCSIZE(1), nMatCols);
7179     CPPUNIT_ASSERT_EQUAL_MESSAGE("1x1 matrix expected", SCSIZE(1), nMatRows);
7180 
7181     pRefMgr->clearCache(nFileId);
7182     testExtRefFuncT(m_pDoc, rExtDoc);
7183     testExtRefFuncOFFSET(m_pDoc, rExtDoc);
7184     testExtRefFuncVLOOKUP(m_pDoc, rExtDoc);
7185     testExtRefConcat(m_pDoc, rExtDoc);
7186 
7187     // Unload the external document shell.
7188     xExtDocSh->DoClose();
7189     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
7190                            !findLoadedDocShellByName(aExtDocName));
7191 
7192     m_pDoc->DeleteTab(0);
7193 }
7194 
testExternalRefUnresolved()7195 void TestFormula::testExternalRefUnresolved()
7196 {
7197 #if !defined(_WIN32) //FIXME
7198     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7199     m_pDoc->InsertTab(0, "Test");
7200 
7201     // Test error propagation of unresolved (not existing document) external
7202     // references. Well, let's hope no build machine has such file with sheet...
7203 
7204     std::vector<std::vector<const char*>> aData = {
7205         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1" },
7206         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23" },
7207         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\"" },
7208         { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7209         { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7210         { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7211         { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7212         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7213         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7214         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23)" },
7215         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\")" },
7216         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=0" },
7217         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=\"\"" },
7218         { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1\")" },
7219         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2" },
7220         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23" },
7221         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\"" },
7222         { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7223         { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7224         { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7225         { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7226         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7227         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7228         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23)" },
7229         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\")" },
7230         // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=0" },
7231         // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=\"\"" },
7232         { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2\")" },
7233     };
7234 
7235     ScAddress aPos(0,0,0);
7236     ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7237     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7238 
7239     std::vector<std::vector<const char*>> aOutputCheck = {
7240         { "#REF!" },    // plain single ref
7241         { "#REF!" },    // +23
7242         { "#REF!" },    // &"W"
7243         { "FALSE" },    // ISREF
7244         { "TRUE"  },    // ISERROR
7245         { "TRUE"  },    // ISERR
7246         { "FALSE" },    // ISBLANK
7247         { "FALSE" },    // ISNUMBER
7248         { "FALSE" },    // ISTEXT
7249         { "FALSE" },    // ISNUMBER
7250         { "FALSE" },    // ISTEXT
7251         { "#REF!" },    // =0
7252         { "#REF!" },    // =""
7253         { "#REF!" },    // INDIRECT
7254         { "#REF!" },    // A1:A2 range
7255         { "#REF!" },    // +23
7256         { "#REF!" },    // &"W"
7257         { "FALSE" },    // ISREF
7258         { "TRUE"  },    // ISERROR
7259         { "TRUE"  },    // ISERR
7260         { "FALSE" },    // ISBLANK
7261         { "FALSE" },    // ISNUMBER
7262         { "FALSE" },    // ISTEXT
7263         { "FALSE" },    // ISNUMBER
7264         { "FALSE" },    // ISTEXT
7265         // TODO: gives Err:504 FIXME { "#REF!" },    // =0
7266         // TODO: gives Err:504 FIXME { "#REF!" },    // =""
7267         { "#REF!" },    // INDIRECT
7268     };
7269 
7270     bool bSuccess = checkOutput(m_pDoc, aRange, aOutputCheck, "Check unresolved external reference.");
7271     CPPUNIT_ASSERT_MESSAGE("Unresolved reference check failed", bSuccess);
7272 
7273     m_pDoc->DeleteTab(0);
7274 #endif
7275 }
7276 
testMatrixOp()7277 void TestFormula::testMatrixOp()
7278 {
7279     m_pDoc->InsertTab(0, "Test");
7280 
7281     for (SCROW nRow = 0; nRow < 4; ++nRow)
7282     {
7283         m_pDoc->SetValue(0, nRow, 0, nRow);
7284     }
7285     m_pDoc->SetValue(1, 0, 0, 2.0);
7286     m_pDoc->SetValue(3, 0, 0, 1.0);
7287     m_pDoc->SetValue(3, 1, 0, 2.0);
7288     m_pDoc->SetString(2, 0, 0, "=SUMPRODUCT((A1:A4)*B1+D1)");
7289     m_pDoc->SetString(2, 1, 0, "=SUMPRODUCT((A1:A4)*B1-D2)");
7290 
7291     double nVal = m_pDoc->GetValue(2, 0, 0);
7292     CPPUNIT_ASSERT_EQUAL(16.0, nVal);
7293 
7294     nVal = m_pDoc->GetValue(2, 1, 0);
7295     CPPUNIT_ASSERT_EQUAL(4.0, nVal);
7296 
7297     m_pDoc->SetString(4, 0, 0, "=SUMPRODUCT({1;2;4}+8)");
7298     m_pDoc->SetString(4, 1, 0, "=SUMPRODUCT(8+{1;2;4})");
7299     m_pDoc->SetString(4, 2, 0, "=SUMPRODUCT({1;2;4}-8)");
7300     m_pDoc->SetString(4, 3, 0, "=SUMPRODUCT(8-{1;2;4})");
7301     m_pDoc->SetString(4, 4, 0, "=SUMPRODUCT({1;2;4}+{8;16;32})");
7302     m_pDoc->SetString(4, 5, 0, "=SUMPRODUCT({8;16;32}+{1;2;4})");
7303     m_pDoc->SetString(4, 6, 0, "=SUMPRODUCT({1;2;4}-{8;16;32})");
7304     m_pDoc->SetString(4, 7, 0, "=SUMPRODUCT({8;16;32}-{1;2;4})");
7305     double fResult[8] = { 31.0, 31.0, -17.0, 17.0, 63.0, 63.0, -49.0, 49.0 };
7306     for (size_t i = 0; i < SAL_N_ELEMENTS(fResult); ++i)
7307     {
7308         CPPUNIT_ASSERT_EQUAL( fResult[i], m_pDoc->GetValue(4, i, 0));
7309     }
7310 
7311     m_pDoc->DeleteTab(0);
7312 }
7313 
testFuncRangeOp()7314 void TestFormula::testFuncRangeOp()
7315 {
7316     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7317 
7318     m_pDoc->InsertTab(0, "Sheet1");
7319     m_pDoc->InsertTab(1, "Sheet2");
7320     m_pDoc->InsertTab(2, "Sheet3");
7321 
7322     // Sheet1.B1:B3
7323     m_pDoc->SetValue(1,0,0, 1.0);
7324     m_pDoc->SetValue(1,1,0, 2.0);
7325     m_pDoc->SetValue(1,2,0, 4.0);
7326     // Sheet2.B1:B3
7327     m_pDoc->SetValue(1,0,1, 8.0);
7328     m_pDoc->SetValue(1,1,1, 16.0);
7329     m_pDoc->SetValue(1,2,1, 32.0);
7330     // Sheet3.B1:B3
7331     m_pDoc->SetValue(1,0,2, 64.0);
7332     m_pDoc->SetValue(1,1,2, 128.0);
7333     m_pDoc->SetValue(1,2,2, 256.0);
7334 
7335     // Range operator should extend concatenated literal references during
7336     // parse time already, so with this we can test ScComplexRefData::Extend()
7337 
7338     // Current sheet is Sheet1, so B1:B2 implies relative Sheet1.B1:B2
7339 
7340     ScAddress aPos(0,0,0);
7341     m_pDoc->SetString( aPos, "=SUM(B1:B2:B3)");
7342     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
7343     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
7344 
7345     aPos.IncRow();
7346     m_pDoc->SetString( aPos, "=SUM(B1:B3:B2)");
7347     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
7348     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
7349 
7350     aPos.IncRow();
7351     m_pDoc->SetString( aPos, "=SUM(B2:B3:B1)");
7352     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
7353     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
7354 
7355     aPos.IncRow();
7356     m_pDoc->SetString( aPos, "=SUM(Sheet2.B1:B2:B3)");
7357     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet2.B1:B3)", "Wrong formula.");
7358     CPPUNIT_ASSERT_EQUAL( 56.0, m_pDoc->GetValue(aPos));
7359 
7360     aPos.IncRow();
7361     m_pDoc->SetString( aPos, "=SUM(B2:B2:Sheet1.B2)");
7362     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B2:B2)", "Wrong formula.");
7363     CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(aPos));
7364 
7365     aPos.IncRow();
7366     m_pDoc->SetString( aPos, "=SUM(B2:B3:Sheet2.B1)");
7367     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet2.B3)", "Wrong formula.");
7368     CPPUNIT_ASSERT_EQUAL( 63.0, m_pDoc->GetValue(aPos));
7369 
7370     aPos.IncRow();
7371     m_pDoc->SetString( aPos, "=SUM(Sheet1.B1:Sheet2.B2:Sheet3.B3)");
7372     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
7373     CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc->GetValue(aPos));
7374 
7375     // B1:Sheet2.B2 would be ambiguous, Sheet1.B1:Sheet2.B2 or Sheet2.B1:B2
7376     // The actual representation of the error case may change, so this test may
7377     // have to be adapted.
7378     aPos.IncRow();
7379     m_pDoc->SetString( aPos, "=SUM(B1:Sheet2.B2:Sheet3.B3)");
7380     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(#REF!.B2:#REF!.B3)", "Wrong formula.");
7381     CPPUNIT_ASSERT_EQUAL( OUString("#REF!"), m_pDoc->GetString(aPos));
7382 
7383     aPos.IncRow();
7384     m_pDoc->SetString( aPos, "=SUM(Sheet1.B1:Sheet3.B2:Sheet2.B3)");
7385     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
7386     CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc->GetValue(aPos));
7387 
7388     aPos.IncRow();
7389     m_pDoc->SetString( aPos, "=SUM(B$2:B$2:B2)");
7390     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B$2:B2)", "Wrong formula.");
7391     CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(aPos));
7392 
7393     m_pDoc->DeleteTab(2);
7394     m_pDoc->DeleteTab(1);
7395     m_pDoc->DeleteTab(0);
7396 }
7397 
testFuncFORMULA()7398 void TestFormula::testFuncFORMULA()
7399 {
7400     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7401 
7402     m_pDoc->InsertTab(0, "Sheet1");
7403 
7404     // Data in B1:D3
7405     std::vector<std::vector<const char*>> aData = {
7406         { "=A1", "=FORMULA(B1)", "=FORMULA(B1:B3)" },
7407         {     nullptr, "=FORMULA(B2)", "=FORMULA(B1:B3)" },
7408         { "=A3", "=FORMULA(B3)", "=FORMULA(B1:B3)" },
7409     };
7410 
7411     ScAddress aPos(1,0,0);
7412     ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7413     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7414 
7415     // Checks of C1:D3, where Cy==Dy, and D4:D6
7416     const char* aChecks[] = {
7417         "=A1",
7418         "#N/A",
7419         "=A3",
7420     };
7421     for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
7422     {
7423         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(2,i,0));
7424         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i,0));
7425     }
7426 
7427     // Matrix in D4:D6, no intersection with B1:B3
7428     ScMarkData aMark(m_pDoc->GetSheetLimits());
7429     aMark.SelectOneTable(0);
7430     m_pDoc->InsertMatrixFormula(3, 3, 3, 5, aMark, "=FORMULA(B1:B3)");
7431     for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
7432     {
7433         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i+3,0));
7434     }
7435 
7436     m_pDoc->DeleteTab(0);
7437 }
7438 
testFuncTableRef()7439 void TestFormula::testFuncTableRef()
7440 {
7441     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7442 
7443     m_pDoc->InsertTab(0, "Sheet1");
7444     ScMarkData aMark(m_pDoc->GetSheetLimits());
7445     aMark.SelectOneTable(0);
7446     ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
7447 
7448     {
7449         ScDBCollection* pDBs = m_pDoc->GetDBCollection();
7450         CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
7451 
7452         // Insert "table" database range definition for A1:B4, with default
7453         // HasHeader=true and HasTotals=false.
7454         std::unique_ptr<ScDBData> pData(new ScDBData( "table", 0,0,0, 1,3));
7455         bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
7456         CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"table\" database range.", bInserted);
7457     }
7458 
7459     {
7460         // Populate "table" database range with headers and data in A1:B4
7461         std::vector<std::vector<const char*>> aData = {
7462             { "Header1", "Header2" },
7463             { "1", "2" },
7464             { "4", "8" },
7465             { "16", "32" }
7466         };
7467         ScAddress aPos(0,0,0);
7468         ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7469         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7470     }
7471 
7472     // Named expressions that use Table structured references.
7473     /* TODO: should the item/header separator really be equal to the parameter
7474      * separator, thus be locale dependent and ';' semicolon here, or should it
7475      * be a fixed ',' comma instead? */
7476     static const struct {
7477         const char* pName;
7478         const char* pExpr;
7479         const char* pCounta; // expected result when used in row 2 (first data row) as argument to COUNTA()
7480         const char* pSum3;   // expected result when used in row 3 (second data row) as argument to SUM().
7481         const char* pSum4;   // expected result when used in row 4 (third data row) as argument to SUM().
7482         const char* pSumX;   // expected result when used in row 5 (non-intersecting) as argument to SUM().
7483     } aNames[] = {
7484         { "all",                            "table[[#All]]",                            "8", "63", "63", "63" },
7485         { "data_implicit",                  "table[]",                                  "6", "63", "63", "63" },
7486         { "data",                           "table[[#Data]]",                           "6", "63", "63", "63" },
7487         { "headers",                        "table[[#Headers]]",                        "2",  "0",  "0",  "0" },
7488         { "header1",                        "table[[Header1]]",                         "3", "21", "21", "21" },
7489         { "header2",                        "table[[Header2]]",                         "3", "42", "42", "42" },
7490         { "data_header1",                   "table[[#Data];[Header1]]",                 "3", "21", "21", "21" },
7491         { "data_header2",                   "table[[#Data];[Header2]]",                 "3", "42", "42", "42" },
7492         { "this_row",                       "table[[#This Row]]",                       "2", "12", "48", "#VALUE!" },
7493         { "this_row_header1",               "table[[#This Row];[Header1]]",             "1",  "4", "16", "#VALUE!" },
7494         { "this_row_header2",               "table[[#This Row];[Header2]]",             "1",  "8", "32", "#VALUE!" },
7495         { "this_row_range_header_1_to_2",   "table[[#This Row];[Header1]:[Header2]]",   "2", "12", "48", "#VALUE!" }
7496     };
7497 
7498     {
7499         // Insert named expressions.
7500         ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
7501         CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
7502 
7503         for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7504         {
7505             // Choose base position that does not intersect with the database
7506             // range definition to test later use of [#This Row] results in
7507             // proper rows.
7508             ScRangeData* pName = new ScRangeData(
7509                     *m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
7510                     ScAddress(2,4,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
7511             bool bInserted = pGlobalNames->insert(pName);
7512             CPPUNIT_ASSERT_MESSAGE(
7513                     OString(OString::Concat("Failed to insert named expression ") + aNames[i].pName +".").getStr(), bInserted);
7514         }
7515     }
7516 
7517     // Use the named expressions in COUNTA() formulas, on row 2 that intersects.
7518     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7519     {
7520         OUString aFormula( "=COUNTA(" + OUString::createFromAscii( aNames[i].pName) + ")");
7521         ScAddress aPos(3+i,1,0);
7522         m_pDoc->SetString( aPos, aFormula);
7523         // For easier "debugability" have position and formula in assertion.
7524         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7525         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pCounta)),
7526                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7527     }
7528 
7529     // Use the named expressions in SUM() formulas, on row 3 that intersects.
7530     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7531     {
7532         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7533         ScAddress aPos(3+i,2,0);
7534         m_pDoc->SetString( aPos, aFormula);
7535         // For easier "debugability" have position and formula in assertion.
7536         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7537         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum3)),
7538                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7539     }
7540 
7541     // Use the named expressions in SUM() formulas, on row 4 that intersects.
7542     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7543     {
7544         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7545         ScAddress aPos(3+i,3,0);
7546         m_pDoc->SetString( aPos, aFormula);
7547         // For easier "debugability" have position and formula in assertion.
7548         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7549         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum4)),
7550                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7551     }
7552 
7553     // Use the named expressions in SUM() formulas, on row 5 that does not intersect.
7554     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7555     {
7556         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7557         ScAddress aPos(3+i,4,0);
7558         m_pDoc->SetString( aPos, aFormula);
7559         // For easier "debugability" have position and formula in assertion.
7560         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7561         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSumX)),
7562                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7563     }
7564 
7565     // Insert a column at column B to extend database range from column A,B to
7566     // A,B,C. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7567     rDocFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
7568 
7569     // Re-verify the named expression in SUM() formula, on row 4 that
7570     // intersects, now starting at column E, still works.
7571     m_pDoc->CalcAll();
7572     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7573     {
7574         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7575         ScAddress aPos(4+i,3,0);
7576         // For easier "debugability" have position and formula in assertion.
7577         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7578         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum4)),
7579                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7580     }
7581 
7582     const char* pColumn2Formula = "=SUM(table[[#Data];[Column2]])";
7583     {
7584         // Populate "table" database range with empty header and data in newly
7585         // inserted column, B1:B4 plus a table formula in B6. The empty header
7586         // should result in the internal table column name "Column2" that is
7587         // used in the formula.
7588         std::vector<std::vector<const char*>> aData = {
7589             { "" },
7590             { "64" },
7591             { "128" },
7592             { "256" },
7593             { "" },
7594             { pColumn2Formula }
7595         };
7596         ScAddress aPos(1,0,0);
7597         ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7598         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7599     }
7600 
7601     // Verify the formula result in B6 (64+128+256=448).
7602     {
7603         OUString aFormula( OUString::createFromAscii( pColumn2Formula));
7604         ScAddress aPos(1,5,0);
7605         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7606         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + "448"), OUString(aPrefix + m_pDoc->GetString(aPos)));
7607     }
7608 
7609     // Set header in column B. Use ScDocFunc to have table column names refreshed.
7610     rDocFunc.SetStringCell(ScAddress(1,0,0), "NewHeader",true);
7611     // Verify that formula adapted using the updated table column names.
7612     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader]])", "Wrong formula");
7613 
7614     // Set header in column A to identical string. Internal table column name
7615     // for B should get a "2" appended.
7616     rDocFunc.SetStringCell(ScAddress(0,0,0), "NewHeader",true);
7617     // Verify that formula adapted using the updated table column names.
7618     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7619 
7620     // Set header in column B to empty string, effectively clearing the cell.
7621     rDocFunc.SetStringCell(ScAddress(1,0,0), "",true);
7622     // Verify that formula is still using the previous table column name.
7623     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7624 
7625     // === header-less ===
7626 
7627     {
7628         ScDBCollection* pDBs = m_pDoc->GetDBCollection();
7629         CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
7630 
7631         // Insert "headerless" database range definition for E10:F12, without headers.
7632         std::unique_ptr<ScDBData> pData(new ScDBData( "hltable", 0, 4,9, 5,11, true, false));
7633         bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
7634         CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"hltable\" database range.", bInserted);
7635     }
7636 
7637     {
7638         // Populate "hltable" database range with data in E10:F12
7639         std::vector<std::vector<const char*>> aData = {
7640             { "1", "2" },
7641             { "4", "8" },
7642             { "16", "32" }
7643         };
7644         ScAddress aPos(4,9,0);
7645         ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7646         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7647     }
7648 
7649     // Named expressions that use header-less Table structured references.
7650     static const struct {
7651         const char* pName;
7652         const char* pExpr;
7653         const char* pCounta; // expected result when used in row 10 (first data row) as argument to COUNTA()
7654         const char* pSum3;   // expected result when used in row 11 (second data row) as argument to SUM().
7655         const char* pSum4;   // expected result when used in row 12 (third data row) as argument to SUM().
7656         const char* pSumX;   // expected result when used in row 13 (non-intersecting) as argument to SUM().
7657     } aHlNames[] = {
7658         { "hl_all",                          "hltable[[#All]]",                          "6", "63", "63", "63" },
7659         { "hl_data_implicit",                "hltable[]",                                "6", "63", "63", "63" },
7660         { "hl_data",                         "hltable[[#Data]]",                         "6", "63", "63", "63" },
7661         { "hl_headers",                      "hltable[[#Headers]]",                      "1", "#REF!", "#REF!", "#REF!" },
7662         { "hl_column1",                      "hltable[[Column1]]",                       "3", "21", "21", "21" },
7663         { "hl_column2",                      "hltable[[Column2]]",                       "3", "42", "42", "42" },
7664         { "hl_data_column1",                 "hltable[[#Data];[Column1]]",               "3", "21", "21", "21" },
7665         { "hl_data_column2",                 "hltable[[#Data];[Column2]]",               "3", "42", "42", "42" },
7666         { "hl_this_row",                     "hltable[[#This Row]]",                     "2", "12", "48", "#VALUE!" },
7667         { "hl_this_row_column1",             "hltable[[#This Row];[Column1]]",           "1",  "4", "16", "#VALUE!" },
7668         { "hl_this_row_column2",             "hltable[[#This Row];[Column2]]",           "1",  "8", "32", "#VALUE!" },
7669         { "hl_this_row_range_column_1_to_2", "hltable[[#This Row];[Column1]:[Column2]]", "2", "12", "48", "#VALUE!" }
7670     };
7671 
7672     {
7673         // Insert named expressions.
7674         ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
7675         CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
7676 
7677         for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7678         {
7679             // Choose base position that does not intersect with the database
7680             // range definition to test later use of [#This Row] results in
7681             // proper rows.
7682             ScRangeData* pName = new ScRangeData(
7683                     *m_pDoc, OUString::createFromAscii(aHlNames[i].pName), OUString::createFromAscii(aHlNames[i].pExpr),
7684                     ScAddress(6,12,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
7685             bool bInserted = pGlobalNames->insert(pName);
7686             CPPUNIT_ASSERT_MESSAGE(
7687                     OString(OString::Concat("Failed to insert named expression ") + aHlNames[i].pName +".").getStr(), bInserted);
7688         }
7689     }
7690 
7691     // Use the named expressions in COUNTA() formulas, on row 10 that intersects.
7692     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7693     {
7694         OUString aFormula( "=COUNTA(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7695         ScAddress aPos(7+i,9,0);
7696         m_pDoc->SetString( aPos, aFormula);
7697         // For easier "debugability" have position and formula in assertion.
7698         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7699         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pCounta)),
7700                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7701     }
7702 
7703     // Use the named expressions in SUM() formulas, on row 11 that intersects.
7704     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7705     {
7706         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7707         ScAddress aPos(7+i,10,0);
7708         m_pDoc->SetString( aPos, aFormula);
7709         // For easier "debugability" have position and formula in assertion.
7710         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7711         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum3)),
7712                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7713     }
7714 
7715     // Use the named expressions in SUM() formulas, on row 12 that intersects.
7716     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7717     {
7718         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7719         ScAddress aPos(7+i,11,0);
7720         m_pDoc->SetString( aPos, aFormula);
7721         // For easier "debugability" have position and formula in assertion.
7722         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7723         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum4)),
7724                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7725     }
7726 
7727     // Use the named expressions in SUM() formulas, on row 13 that does not intersect.
7728     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7729     {
7730         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7731         ScAddress aPos(7+i,12,0);
7732         m_pDoc->SetString( aPos, aFormula);
7733         // For easier "debugability" have position and formula in assertion.
7734         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7735         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSumX)),
7736                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7737     }
7738 
7739     // Insert a column at column F to extend database range from column E,F to
7740     // E,F,G. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7741     rDocFunc.InsertCells(ScRange(5,0,0,5,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
7742 
7743     // Re-verify the named expression in SUM() formula, on row 12 that
7744     // intersects, now starting at column I, still works.
7745     m_pDoc->CalcAll();
7746     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7747     {
7748         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7749         ScAddress aPos(8+i,11,0);
7750         // For easier "debugability" have position and formula in assertion.
7751         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7752         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum4)),
7753                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7754     }
7755 
7756     const char* pColumn3Formula = "=SUM(hltable[[#Data];[Column3]])";
7757     {
7758         // Populate "hltable" database range with data in newly inserted
7759         // column, F10:F12 plus a table formula in F14. The new header should
7760         // result in the internal table column name "Column3" that is used in
7761         // the formula.
7762         std::vector<std::vector<const char*>> aData = {
7763             { "64" },
7764             { "128" },
7765             { "256" },
7766             { "" },
7767             { pColumn3Formula }
7768         };
7769         ScAddress aPos(5,9,0);
7770         ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7771         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7772     }
7773 
7774     // Verify the formula result in F14 (64+128+256=448).
7775     {
7776         OUString aFormula( OUString::createFromAscii( pColumn3Formula));
7777         ScAddress aPos(5,13,0);
7778         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7779         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + "448"), OUString(aPrefix + m_pDoc->GetString(aPos)));
7780     }
7781 
7782     m_pDoc->DeleteTab(0);
7783 }
7784 
testFuncFTEST()7785 void TestFormula::testFuncFTEST()
7786 {
7787     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7788 
7789     m_pDoc->InsertTab(0, "FTest");
7790 
7791     ScAddress aPos(6,0,0);
7792     m_pDoc->SetString(aPos, "=FTEST(A1:C3;D1:F3)");
7793     m_pDoc->SetValue(0, 0, 0, 9.0); // A1
7794     OUString aVal = m_pDoc->GetString(aPos);
7795     CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7796             OUString("#VALUE!"), aVal);
7797     m_pDoc->SetValue(0, 1, 0, 8.0); // A2
7798     aVal = m_pDoc->GetString(aPos);
7799     CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7800             OUString("#VALUE!"), aVal);
7801     m_pDoc->SetValue(3, 0, 0, 5.0); // D1
7802     aVal = m_pDoc->GetString(aPos);
7803     CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7804             OUString("#VALUE!"), aVal);
7805     m_pDoc->SetValue(3, 1, 0, 6.0); // D2
7806     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 1.0000, m_pDoc->GetValue(aPos), 10e-4);
7807     m_pDoc->SetValue(1, 0, 0, 6.0); // B1
7808     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.6222, m_pDoc->GetValue(aPos), 10e-4);
7809     m_pDoc->SetValue(1, 1, 0, 8.0); // B2
7810     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.7732, m_pDoc->GetValue(aPos), 10e-4);
7811     m_pDoc->SetValue(4, 0, 0, 7.0); // E1
7812     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.8194, m_pDoc->GetValue(aPos), 10e-4);
7813     m_pDoc->SetValue(4, 1, 0, 4.0); // E2
7814     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9674, m_pDoc->GetValue(aPos), 10e-4);
7815     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7816     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.3402, m_pDoc->GetValue(aPos), 10e-4);
7817     m_pDoc->SetValue(5, 0, 0, 28.0); // F1
7818     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc->GetValue(aPos), 10e-4);
7819     m_pDoc->SetValue(2, 1, 0, 9.0); // C2
7820     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0063, m_pDoc->GetValue(aPos), 10e-4);
7821     m_pDoc->SetValue(5, 1, 0, 4.0); // F2
7822     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0081, m_pDoc->GetValue(aPos), 10e-4);
7823     m_pDoc->SetValue(0, 2, 0, 2.0); // A3
7824     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0122, m_pDoc->GetValue(aPos), 10e-4);
7825     m_pDoc->SetValue(3, 2, 0, 8.0); // D3
7826     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0178, m_pDoc->GetValue(aPos), 10e-4);
7827     m_pDoc->SetValue(1, 2, 0, 4.0); // B3
7828     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0093, m_pDoc->GetValue(aPos), 10e-4);
7829     m_pDoc->SetValue(4, 2, 0, 7.0); // E3
7830     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0132, m_pDoc->GetValue(aPos), 10e-4);
7831     m_pDoc->SetValue(5, 2, 0, 5.0); // F3
7832     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0168, m_pDoc->GetValue(aPos), 10e-4);
7833     m_pDoc->SetValue(2, 2, 0, 13.0); // C3
7834     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0422, m_pDoc->GetValue(aPos), 10e-4);
7835 
7836     m_pDoc->SetString(0, 2, 0, "a"); // A3
7837     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0334, m_pDoc->GetValue(aPos), 10e-4);
7838     m_pDoc->SetString(2, 0, 0, "b"); // C1
7839     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0261, m_pDoc->GetValue(aPos), 10e-4);
7840     m_pDoc->SetString(5, 1, 0, "c"); // F2
7841     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0219, m_pDoc->GetValue(aPos), 10e-4);
7842     m_pDoc->SetString(4, 2, 0, "d"); // E3
7843     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc->GetValue(aPos), 10e-4);
7844     m_pDoc->SetString(3, 2, 0, "e"); // D3
7845     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0110, m_pDoc->GetValue(aPos), 10e-4);
7846 
7847     m_pDoc->DeleteTab(0);
7848     m_pDoc->InsertTab(0, "FTest2");
7849 
7850     /* Summary of the following test
7851        A1:A5   =  SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7852        A6:A10  = -SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7853        B1:B10  =  SQRT(C2*19/20)*{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
7854        B11:B20 = -SQRT(C2*19/20)*{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
7855        C1      =  POWER(1.5, D1)   ; This is going to be the sample variance of the vector A1:A10
7856        C2      =  POWER(1.5, D2)   ; This is going to be the sample variance of the vector B1:B20
7857        D1 and D2 are varied over { -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 }
7858 
7859        Result of FTEST(A1:A10;B1:B20) in Calc is compared with that from Octave's var_test() function for each value of D1 and D2.
7860 
7861        The minimum variance ratio obtained in this way is 0.017342 and the maximum variance ratio is 57.665039
7862     */
7863 
7864     const size_t nNumParams = 11;
7865     const double fParameter[nNumParams] = { -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
7866 
7867     // Results of var_test() from Octave
7868     const double fResults[nNumParams][nNumParams] = {
7869         { 0.9451191535603041,0.5429768686792684,0.213130093422756,0.06607644828558357,0.0169804365506927,0.003790723514148109,
7870           0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06,7.495090956766148e-07 },
7871         { 0.4360331979746912,0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927,
7872           0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06 },
7873         { 0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.2131300934227565,0.06607644828558357,
7874           0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05 },
7875         { 0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603058,0.5429768686792689,0.2131300934227565,
7876           0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777 },
7877         { 0.002886791075972228,0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603041,0.5429768686792689,
7878           0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703 },
7879         { 0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746912,0.9451191535603036,
7880           0.5429768686792689,0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109 },
7881         { 1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,
7882           0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927 },
7883         { 5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,
7884           0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.213130093422756,0.06607644828558357 },
7885         { 1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,
7886           0.02453502500565086,0.1309752286653509,0.4360331979746914,0.9451191535603041,0.5429768686792684,0.2131300934227565 },
7887         { 5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,
7888           0.002886791075972228,0.02453502500565108,0.1309752286653516,0.4360331979746914,0.9451191535603058,0.5429768686792684 },
7889         { 1.354649725726631e-11,5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,
7890           0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,0.9451191535603054 }
7891     };
7892 
7893     m_pDoc->SetValue(3, 0, 0, fParameter[0]); // D1
7894     m_pDoc->SetValue(3, 1, 0, fParameter[0]); // D2
7895     aPos.Set(2,0,0); // C1
7896     m_pDoc->SetString(aPos, "=POWER(1.5;D1)" ); // C1
7897     aPos.Set(2, 1, 0);     // C2
7898     m_pDoc->SetString(aPos, "=POWER(1.5;D2)" ); // C2
7899     for ( SCROW nRow = 0; nRow < 5; ++nRow )    // Set A1:A5  = SQRT(C1*9/10), and A6:A10 = -SQRT(C1*9/10)
7900     {
7901         aPos.Set(0, nRow, 0);
7902         m_pDoc->SetString(aPos, "=SQRT(C1*9/10)");
7903         aPos.Set(0, nRow + 5, 0);
7904         m_pDoc->SetString(aPos, "=-SQRT(C1*9/10)");
7905     }
7906 
7907     for ( SCROW nRow = 0; nRow < 10; ++nRow )    // Set B1:B10  = SQRT(C2*19/20), and B11:B20 = -SQRT(C2*19/20)
7908     {
7909         aPos.Set(1, nRow, 0);
7910         m_pDoc->SetString(aPos, "=SQRT(C2*19/20)");
7911         aPos.Set(1, nRow + 10, 0);
7912         m_pDoc->SetString(aPos, "=-SQRT(C2*19/20)");
7913     }
7914 
7915     aPos.Set(4, 0, 0); // E1
7916     m_pDoc->SetString(aPos, "=FTEST(A1:A10;B1:B20)");
7917     aPos.Set(4, 1, 0); // E2
7918     m_pDoc->SetString(aPos, "=FTEST(B1:B20;A1:A10)");
7919 
7920     ScAddress aPosRev(4, 1, 0); // E2
7921     aPos.Set(4, 0, 0);  // E1
7922 
7923     for ( size_t nFirstIdx = 0; nFirstIdx < nNumParams; ++nFirstIdx )
7924     {
7925         m_pDoc->SetValue(3, 0, 0, fParameter[nFirstIdx]); // Set D1
7926         for ( size_t nSecondIdx = 0; nSecondIdx < nNumParams; ++nSecondIdx )
7927         {
7928             m_pDoc->SetValue(3, 1, 0, fParameter[nSecondIdx]); // Set D2
7929             double fExpected = fResults[nFirstIdx][nSecondIdx];
7930             // Here a dynamic error limit is used. This is to handle correctly when the expected value is lower than the fixed error limit of 10e-5
7931             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected, m_pDoc->GetValue(aPos),    std::min(10e-5, fExpected*0.0001) );
7932             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected, m_pDoc->GetValue(aPosRev), std::min(10e-5, fExpected*0.0001) );
7933         }
7934     }
7935     m_pDoc->DeleteTab(0);
7936 }
7937 
testFuncFTESTBug()7938 void TestFormula::testFuncFTESTBug()
7939 {
7940     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7941 
7942     m_pDoc->InsertTab(0, "FTest");
7943 
7944     ScAddress aPos(9,0,0);
7945     m_pDoc->SetString(aPos, "=FTEST(H1:H3;I1:I3)");
7946 
7947     m_pDoc->SetValue(7, 0, 0, 9.0); // H1
7948     m_pDoc->SetValue(7, 1, 0, 8.0); // H2
7949     m_pDoc->SetValue(7, 2, 0, 6.0); // H3
7950     m_pDoc->SetValue(8, 0, 0, 5.0); // I1
7951     m_pDoc->SetValue(8, 1, 0, 7.0); // I2
7952     // tdf#93329
7953     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9046, m_pDoc->GetValue(aPos), 10e-4);
7954 
7955     m_pDoc->DeleteTab(0);
7956 }
7957 
testFuncCHITEST()7958 void TestFormula::testFuncCHITEST()
7959 {
7960     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7961 
7962     m_pDoc->InsertTab(0, "ChiTest");
7963 
7964     ScAddress aPos(6,0,0);
7965     // 2x2 matrices test
7966     m_pDoc->SetString(aPos, "=CHITEST(A1:B2;D1:E2)");
7967     OUString aVal = m_pDoc->GetString(aPos);
7968     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with empty cells",
7969             OUString("Err:502"), aVal);
7970 
7971     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
7972     m_pDoc->SetValue(0, 1, 0, 2.0); // A2
7973     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
7974     m_pDoc->SetValue(1, 1, 0, 1.0); // B2
7975     aVal = m_pDoc->GetString(aPos);
7976     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrix with empty cells",
7977             OUString("Err:502"), aVal);
7978 
7979     m_pDoc->SetValue(3, 0, 0, 2.0); // D1
7980     m_pDoc->SetValue(3, 1, 0, 3.0); // D2
7981     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc->GetValue(aPos), 10e-4);
7982 
7983     m_pDoc->SetValue(4, 1, 0, 1.0); // E2
7984     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc->GetValue(aPos), 10e-4);
7985     m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7986     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.2801, m_pDoc->GetValue(aPos), 10e-4);
7987     m_pDoc->SetValue(4, 0, 0, 0.0); // E1
7988     aVal = m_pDoc->GetString(aPos);
7989     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0 for expected values of 0", OUString("#DIV/0!"), aVal);
7990     m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7991     m_pDoc->SetValue(1, 1, 0, 0.0); // B2
7992     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1410, m_pDoc->GetValue(aPos), 10e-4);
7993 
7994     // 3x3 matrices test
7995     m_pDoc->SetString(aPos, "=CHITEST(A1:C3;D1:F3)");
7996     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.7051, m_pDoc->GetValue(aPos), 10e-4);
7997 
7998     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7999     m_pDoc->SetValue(2, 1, 0, 2.0); // C2
8000     m_pDoc->SetValue(2, 2, 0, 3.0); // C3
8001     m_pDoc->SetValue(0, 2, 0, 4.0); // A3
8002     m_pDoc->SetValue(1, 2, 0, 2.0); // B3
8003     m_pDoc->SetValue(5, 0, 0, 1.0); // F1
8004     m_pDoc->SetValue(5, 1, 0, 2.0); // F2
8005     m_pDoc->SetValue(5, 2, 0, 3.0); // F3
8006     m_pDoc->SetValue(3, 2, 0, 3.0); // D3
8007     m_pDoc->SetValue(4, 2, 0, 1.0); // E3
8008     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc->GetValue(aPos), 10e-4);
8009 
8010     // test with strings
8011     m_pDoc->SetString(4, 2, 0, "a"); // E3
8012     aVal = m_pDoc->GetString(aPos);
8013     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
8014             OUString("Err:502"), aVal);
8015     m_pDoc->SetString(1, 2, 0, "a"); // B3
8016     aVal = m_pDoc->GetString(aPos);
8017     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
8018             OUString("Err:502"), aVal);
8019     m_pDoc->SetValue(4, 2, 0, 1.0); // E3
8020     aVal = m_pDoc->GetString(aPos);
8021     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
8022             OUString("Err:502"), aVal);
8023     m_pDoc->SetValue(1, 2, 0, 2.0); // B3
8024     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc->GetValue(aPos), 10e-4);
8025 
8026     m_pDoc->SetValue(4, 1, 0, 5.0); // E2
8027     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0215, m_pDoc->GetValue(aPos), 10e-4);
8028     m_pDoc->SetValue(1, 2, 0, 1.0); // B3
8029     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0328, m_pDoc->GetValue(aPos), 10e-4);
8030     m_pDoc->SetValue(5, 0, 0, 3.0); // F1
8031     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1648, m_pDoc->GetValue(aPos), 10e-4);
8032     m_pDoc->SetValue(0, 1, 0, 3.0); // A2
8033     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1870, m_pDoc->GetValue(aPos), 10e-4);
8034     m_pDoc->SetValue(3, 1, 0, 5.0); // D2
8035     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1377, m_pDoc->GetValue(aPos), 10e-4);
8036     m_pDoc->SetValue(3, 2, 0, 4.0); // D3
8037     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1566, m_pDoc->GetValue(aPos), 10e-4);
8038 
8039     m_pDoc->SetValue(0, 0, 0, 0.0); // A1
8040     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0868, m_pDoc->GetValue(aPos), 10e-4);
8041 
8042     // no convergence error
8043     m_pDoc->SetValue(4, 0, 0, 1.0E308); // E1
8044     aVal = m_pDoc->GetString(aPos);
8045     CPPUNIT_ASSERT_EQUAL(OUString("Err:523"), aVal);
8046     m_pDoc->SetValue(4, 0, 0, 3.0); // E1
8047 
8048     // zero in all cells
8049     m_pDoc->SetValue(0, 1, 0, 0.0); // A2
8050     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0150, m_pDoc->GetValue(aPos), 10e-4);
8051     m_pDoc->SetValue(0, 2, 0, 0.0); // A3
8052     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0026, m_pDoc->GetValue(aPos), 10e-4);
8053     m_pDoc->SetValue(1, 0, 0, 0.0); // B1
8054     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.00079, m_pDoc->GetValue(aPos), 10e-5);
8055     m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8056     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0005, m_pDoc->GetValue(aPos), 10e-4);
8057     m_pDoc->SetValue(2, 0, 0, 0.0); // C1
8058     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0001, m_pDoc->GetValue(aPos), 10e-4);
8059     m_pDoc->SetValue(2, 1, 0, 0.0); // C2
8060     m_pDoc->SetValue(2, 2, 0, 0.0); // C3
8061     m_pDoc->SetValue(3, 0, 0, 0.0); // D1
8062     aVal = m_pDoc->GetString(aPos);
8063     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
8064             OUString("#DIV/0!"), aVal);
8065     m_pDoc->SetValue(3, 1, 0, 0.0); // D2
8066     m_pDoc->SetValue(3, 2, 0, 0.0); // D3
8067     m_pDoc->SetValue(4, 0, 0, 0.0); // E1
8068     m_pDoc->SetValue(4, 1, 0, 0.0); // E2
8069     m_pDoc->SetValue(4, 2, 0, 0.0); // E3
8070     m_pDoc->SetValue(5, 0, 0, 0.0); // F1
8071     m_pDoc->SetValue(5, 1, 0, 0.0); // F2
8072     m_pDoc->SetValue(5, 2, 0, 0.0); // F3
8073     aVal = m_pDoc->GetString(aPos);
8074     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
8075             OUString("#DIV/0!"), aVal);
8076 
8077     m_pDoc->DeleteTab(0);
8078 }
8079 
testFuncTTEST()8080 void TestFormula::testFuncTTEST()
8081 {
8082     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8083 
8084     m_pDoc->InsertTab(0, "TTest");
8085 
8086     ScAddress aPos(6,0,0);
8087     // type 1, mode/tails 1
8088     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;1;1)");
8089     OUString aVal = m_pDoc->GetString(aPos);
8090     CPPUNIT_ASSERT_EQUAL_MESSAGE("TTEST should return #VALUE! for empty matrices",
8091             OUString("#VALUE!"), aVal);
8092 
8093     m_pDoc->SetValue(0, 0, 0, 8.0); // A1
8094     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
8095     m_pDoc->SetValue(3, 0, 0, 3.0); // D1
8096     m_pDoc->SetValue(4, 0, 0, 1.0); // E1
8097     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc->GetValue(aPos), 10e-5);
8098     m_pDoc->SetValue(2, 0, 0, 1.0); // C1
8099     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc->GetValue(aPos), 10e-5);
8100     m_pDoc->SetValue(5, 0, 0, 6.0); // F1
8101     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc->GetValue(aPos), 10e-5);
8102     m_pDoc->SetValue(0, 1, 0, -4.0); // A2
8103     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc->GetValue(aPos), 10e-5);
8104     m_pDoc->SetValue(3, 1, 0, 1.0); // D2
8105     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc->GetValue(aPos), 10e-5);
8106     m_pDoc->SetValue(1, 1, 0, 5.0); // B2
8107     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc->GetValue(aPos), 10e-5);
8108     m_pDoc->SetValue(4, 1, 0, -2.0); // E2
8109     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc->GetValue(aPos), 10e-5);
8110     m_pDoc->SetValue(2, 1, 0, -1.0); // C2
8111     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc->GetValue(aPos), 10e-5);
8112     m_pDoc->SetValue(5, 1, 0, -3.0); // F2
8113     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc->GetValue(aPos), 10e-5);
8114     m_pDoc->SetValue(0, 2, 0, 10.0); // A3
8115     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc->GetValue(aPos), 10e-5);
8116     m_pDoc->SetValue(3, 2, 0, 10.0); // D3
8117     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc->GetValue(aPos), 10e-5);
8118     m_pDoc->SetValue(1, 2, 0, 3.0); // B3
8119     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc->GetValue(aPos), 10e-5);
8120     m_pDoc->SetValue(4, 2, 0, 9.0); // E3
8121     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc->GetValue(aPos), 10e-5);
8122     m_pDoc->SetValue(2, 2, 0, -5.0); // C3
8123     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc->GetValue(aPos), 10e-5);
8124     m_pDoc->SetValue(5, 2, 0, 6.0); // F3
8125     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25529, m_pDoc->GetValue(aPos), 10e-5);
8126 
8127     m_pDoc->SetString(1, 1, 0, "a"); // B2
8128     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc->GetValue(aPos), 10e-5);
8129     m_pDoc->SetString(4, 1, 0, "b"); // E2
8130     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc->GetValue(aPos), 10e-5);
8131     m_pDoc->SetString(2, 2, 0, "c"); // C3
8132     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25030, m_pDoc->GetValue(aPos), 10e-5);
8133     m_pDoc->SetString(5, 1, 0, "d"); // F2
8134     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.19637, m_pDoc->GetValue(aPos), 10e-5);
8135 
8136     // type 1, mode/tails 2
8137     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;2;1)");
8138     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc->GetValue(aPos), 10e-5);
8139     m_pDoc->SetValue(1, 1, 0, 4.0); // B2
8140     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc->GetValue(aPos), 10e-5);
8141     m_pDoc->SetValue(4, 1, 0, 3.0); // E2
8142     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43970, m_pDoc->GetValue(aPos), 10e-5);
8143     m_pDoc->SetValue(2, 2, 0, -2.0); // C3
8144     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22217, m_pDoc->GetValue(aPos), 10e-5);
8145     m_pDoc->SetValue(5, 1, 0, -10.0); // F2
8146     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.64668, m_pDoc->GetValue(aPos), 10e-5);
8147     m_pDoc->SetValue(0, 1, 0, 3.0); // A2
8148     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.95266, m_pDoc->GetValue(aPos), 10e-5);
8149     m_pDoc->SetValue(3, 2, 0, -1.0); // D3
8150     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62636, m_pDoc->GetValue(aPos), 10e-5);
8151 
8152     // type 2, mode/tails 2
8153     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;2;2)");
8154     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62549, m_pDoc->GetValue(aPos), 10e-5);
8155     m_pDoc->SetValue(5, 1, 0, -1.0); // F2
8156     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.94952, m_pDoc->GetValue(aPos), 10e-5);
8157     m_pDoc->SetValue(2, 2, 0, 5.0); // C3
8158     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.58876, m_pDoc->GetValue(aPos), 10e-5);
8159     m_pDoc->SetValue(2, 1, 0, 2.0); // C2
8160     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43205, m_pDoc->GetValue(aPos), 10e-5);
8161     m_pDoc->SetValue(3, 2, 0, -4.0); // D3
8162     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36165, m_pDoc->GetValue(aPos), 10e-5);
8163     m_pDoc->SetValue(0, 1, 0, 1.0); // A2
8164     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.44207, m_pDoc->GetValue(aPos), 10e-5);
8165 
8166     // type 3, mode/tails 1
8167     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;1;3)");
8168     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22132, m_pDoc->GetValue(aPos), 10e-5);
8169     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
8170     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36977, m_pDoc->GetValue(aPos), 10e-5);
8171     m_pDoc->SetValue(0, 2, 0, -30.0); // A3
8172     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16871, m_pDoc->GetValue(aPos), 10e-5);
8173     m_pDoc->SetValue(3, 1, 0, 5.0); // D2
8174     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.14396, m_pDoc->GetValue(aPos), 10e-5);
8175     m_pDoc->SetValue(5, 1, 0, 2.0); // F2
8176     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12590, m_pDoc->GetValue(aPos), 10e-5);
8177     m_pDoc->SetValue(4, 2, 0, 2.0); // E3
8178     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16424, m_pDoc->GetValue(aPos), 10e-5);
8179     m_pDoc->SetValue(5, 0, 0, -1.0); // F1
8180     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.21472, m_pDoc->GetValue(aPos), 10e-5);
8181 
8182     m_pDoc->DeleteTab(0);
8183 }
8184 
testFuncSUMX2PY2()8185 void TestFormula::testFuncSUMX2PY2()
8186 {
8187     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8188 
8189     m_pDoc->InsertTab(0, "SumX2PY2 Test");
8190 
8191     OUString aVal;
8192     ScAddress aPos(6,0,0);
8193     m_pDoc->SetString(aPos, "=SUMX2PY2(A1:C3;D1:F3)");
8194     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc->GetValue(aPos));
8195 
8196     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
8197     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc->GetValue(aPos));
8198     m_pDoc->SetValue(3, 0, 0, 2.0); // D1
8199     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc->GetValue(aPos));
8200     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
8201     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc->GetValue(aPos));
8202     m_pDoc->SetValue(4, 0, 0, 0.0); // E1
8203     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 9.0, m_pDoc->GetValue(aPos));
8204     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
8205     m_pDoc->SetValue(5, 0, 0, 3.0); // F1
8206     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 27.0, m_pDoc->GetValue(aPos));
8207     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8208     m_pDoc->SetValue(3, 1, 0, -10.0); // D2
8209     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc->GetValue(aPos));
8210     m_pDoc->SetValue(1, 1, 0, -5.0); // B2
8211     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc->GetValue(aPos));
8212     m_pDoc->SetValue(4, 1, 0, -5.0); // E2
8213     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc->GetValue(aPos));
8214     m_pDoc->SetValue(2, 1, 0, 0.0); // C2
8215     m_pDoc->SetValue(5, 1, 0, 0.0); // F2
8216     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc->GetValue(aPos));
8217     m_pDoc->SetValue(0, 2, 0, -8.0); // A3
8218     m_pDoc->SetValue(3, 2, 0, 8.0); // D3
8219     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc->GetValue(aPos));
8220     m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8221     m_pDoc->SetValue(4, 2, 0, 0.0); // E3
8222     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc->GetValue(aPos));
8223     m_pDoc->SetValue(2, 2, 0, 1.0); // C3
8224     m_pDoc->SetValue(5, 2, 0, 1.0); // F3
8225     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 407.0, m_pDoc->GetValue(aPos));
8226 
8227     // add some strings
8228     m_pDoc->SetString(4, 1, 0, "a"); // E2
8229     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc->GetValue(aPos));
8230     m_pDoc->SetString(1, 1, 0, "a"); // B2
8231     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc->GetValue(aPos));
8232     m_pDoc->SetString(0, 0, 0, "a"); // A1
8233     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc->GetValue(aPos));
8234     m_pDoc->SetString(3, 0, 0, "a"); // D1
8235     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc->GetValue(aPos));
8236 
8237     m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3};{2;3;4})");
8238     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 43.0, m_pDoc->GetValue(aPos));
8239     m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3};{2;3})");
8240     aVal = m_pDoc->GetString(aPos);
8241     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 should return #VALUE! for matrices with different sizes",
8242             OUString("#VALUE!"), aVal);
8243     m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3})");
8244     aVal = m_pDoc->GetString(aPos);
8245     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 needs two parameters",
8246             OUString("Err:511"), aVal);
8247 
8248     m_pDoc->DeleteTab(0);
8249 }
8250 
testFuncSUMX2MY2()8251 void TestFormula::testFuncSUMX2MY2()
8252 {
8253     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8254 
8255     m_pDoc->InsertTab(0, "SumX2MY2 Test");
8256 
8257     OUString aVal;
8258     ScAddress aPos(6,0,0);
8259     m_pDoc->SetString(aPos, "=SUMX2MY2(A1:C3;D1:F3)");
8260     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc->GetValue(aPos));
8261 
8262     m_pDoc->SetValue(0, 0, 0, 10.0); // A1
8263     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc->GetValue(aPos));
8264     m_pDoc->SetValue(3, 0, 0, -9.0); // D1
8265     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc->GetValue(aPos));
8266     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
8267     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc->GetValue(aPos));
8268     m_pDoc->SetValue(4, 0, 0, 1.0); // E1
8269     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8270     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
8271     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8272     m_pDoc->SetValue(5, 0, 0, 3.0); // F1
8273     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8274     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8275     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8276     m_pDoc->SetValue(3, 1, 0, -10.0); // D2
8277     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8278     m_pDoc->SetValue(1, 1, 0, -5.0); // B2
8279     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8280     m_pDoc->SetValue(4, 1, 0, -5.0); // E2
8281     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8282     m_pDoc->SetValue(2, 1, 0, -3.0); // C2
8283     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8284     m_pDoc->SetValue(5, 1, 0, 3.0); // F2
8285     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8286     m_pDoc->SetValue(0, 2, 0, -8.0); // A3
8287     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8288     m_pDoc->SetValue(3, 2, 0, 3.0); // D3
8289     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc->GetValue(aPos));
8290     m_pDoc->SetValue(1, 2, 0, 2.0); // B3
8291     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc->GetValue(aPos));
8292     m_pDoc->SetValue(4, 2, 0, -6.0); // E3
8293     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
8294     m_pDoc->SetValue(2, 2, 0, -4.0); // C3
8295     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
8296     m_pDoc->SetValue(5, 2, 0, 6.0); // F3
8297     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 25.0, m_pDoc->GetValue(aPos));
8298 
8299     // add some strings
8300     m_pDoc->SetString(5, 2, 0, "a"); // F3
8301     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
8302     m_pDoc->SetString(0, 2, 0, "a"); // A3
8303     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -10.0, m_pDoc->GetValue(aPos));
8304     m_pDoc->SetString(1, 0, 0, "a"); // B1
8305     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -13.0, m_pDoc->GetValue(aPos));
8306     m_pDoc->SetString(3, 0, 0, "a"); // D1
8307     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -32.0, m_pDoc->GetValue(aPos));
8308 
8309     m_pDoc->SetString(aPos, "=SUMX2MY2({1;3;5};{0;4;4})");
8310     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc->GetValue(aPos));
8311     m_pDoc->SetString(aPos, "=SUMX2MY2({1;-3;-5};{0;-4;4})");
8312     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc->GetValue(aPos));
8313     m_pDoc->SetString(aPos, "=SUMX2MY2({9;5;1};{3;-3;3})");
8314     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 80.0, m_pDoc->GetValue(aPos));
8315     m_pDoc->SetString(aPos, "=SUMX2MY2({1;2;3};{2;3})");
8316     aVal = m_pDoc->GetString(aPos);
8317     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 should return #VALUE! for matrices with different sizes",
8318             OUString("#VALUE!"), aVal);
8319     m_pDoc->SetString(aPos, "=SUMX2MY2({1;2;3})");
8320     aVal = m_pDoc->GetString(aPos);
8321     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 needs two parameters",
8322             OUString("Err:511"), aVal);
8323 
8324     m_pDoc->DeleteTab(0);
8325 }
8326 
testFuncGCD()8327 void TestFormula::testFuncGCD()
8328 {
8329     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8330 
8331     m_pDoc->InsertTab(0, "GCDTest");
8332 
8333     OUString aVal;
8334     ScAddress aPos(4,0,0);
8335 
8336     m_pDoc->SetString(aPos, "=GCD(A1)");
8337     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc->GetValue(aPos));
8338     m_pDoc->SetValue(0, 0, 0, 10.0); // A1
8339     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
8340     m_pDoc->SetValue(0, 0, 0, -2.0); // A1
8341     aVal = m_pDoc->GetString(aPos);
8342     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for values less than 0",
8343             OUString("Err:502"), aVal);
8344     m_pDoc->SetString(0, 0, 0, "a"); // A1
8345     aVal = m_pDoc->GetString(aPos);
8346     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for a single string",
8347             OUString("#VALUE!"), aVal);
8348 
8349     m_pDoc->SetString(aPos, "=GCD(A1:B2)");
8350     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc->GetValue(aPos));
8351     m_pDoc->SetValue(0, 1, 0, -12.0); // B1
8352     aVal = m_pDoc->GetString(aPos);
8353     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for a matrix with values less than 0",
8354             OUString("Err:502"), aVal);
8355     m_pDoc->SetValue(0, 0, 0, 15.0); // A1
8356     m_pDoc->SetValue(0, 1, 0, 0.0); // B1
8357     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 15.0, m_pDoc->GetValue(aPos));
8358     m_pDoc->SetValue(1, 0, 0, 5.0); // B1
8359     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8360     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8361     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8362     m_pDoc->SetValue(1, 0, 0, 30.0); // B1
8363     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8364     m_pDoc->SetValue(0, 0, 0, 20.0); // A1
8365     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
8366     m_pDoc->SetValue(1, 1, 0, 120.0); // B2
8367     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
8368     m_pDoc->SetValue(0, 1, 0, 80.0); // A2
8369     m_pDoc->SetValue(1, 0, 0, 40.0); // B1
8370     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 20.0, m_pDoc->GetValue(aPos));
8371     m_pDoc->SetValue(1, 0, 0, 45.0); // B1
8372     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8373 
8374     // with floor
8375     m_pDoc->SetValue(1, 0, 0, 45.381); // B1
8376     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8377     m_pDoc->SetValue(1, 1, 0, 120.895); // B2
8378     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8379     m_pDoc->SetValue(0, 0, 0, 20.97); // A1
8380     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8381     m_pDoc->SetValue(0, 1, 0, 10.15); // A2
8382     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8383 
8384     // inline array
8385     m_pDoc->SetString(aPos, "=GCD({3;6;9})");
8386     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc->GetValue(aPos));
8387     m_pDoc->SetString(aPos, "=GCD({150;0})");
8388     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc->GetValue(aPos));
8389     m_pDoc->SetString(aPos, "=GCD({-3;6;9})");
8390     aVal = m_pDoc->GetString(aPos);
8391     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
8392             OUString("Err:502"), aVal);
8393     m_pDoc->SetString(aPos, "=GCD({\"a\";6;9})");
8394     aVal = m_pDoc->GetString(aPos);
8395     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
8396             OUString("Err:502"), aVal);
8397 
8398     //many inline array
8399     m_pDoc->SetString(aPos, "=GCD({6;6;6};{3;6;9})");
8400     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc->GetValue(aPos));
8401     m_pDoc->SetString(aPos, "=GCD({300;300;300};{150;0})");
8402     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc->GetValue(aPos));
8403     m_pDoc->SetString(aPos,"=GCD({3;6;9};{3;-6;9})");
8404     aVal = m_pDoc->GetString(aPos);
8405     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
8406             OUString("Err:502"), aVal);
8407     m_pDoc->SetString(aPos, "=GCD({3;6;9};{\"a\";6;9})");
8408     aVal = m_pDoc->GetString(aPos);
8409     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
8410             OUString("Err:502"), aVal);
8411 
8412     // inline list of values
8413     m_pDoc->SetString(aPos, "=GCD(12;24;36;48;60)");
8414     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc->GetValue(aPos));
8415     m_pDoc->SetString(aPos, "=GCD(0;12;24;36;48;60)");
8416     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc->GetValue(aPos));
8417     m_pDoc->SetString(aPos, "=GCD(\"a\";1)");
8418     aVal = m_pDoc->GetString(aPos);
8419     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for an array with strings",
8420             OUString("#VALUE!"), aVal);
8421 
8422     m_pDoc->DeleteTab(0);
8423 }
8424 
testFuncLCM()8425 void TestFormula::testFuncLCM()
8426 {
8427     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8428 
8429     m_pDoc->InsertTab(0, "LCMTest");
8430 
8431     OUString aVal;
8432     ScAddress aPos(4,0,0);
8433 
8434     m_pDoc->SetString(aPos, "=LCM(A1)");
8435     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8436     m_pDoc->SetValue(0, 0, 0, 10.0); // A1
8437     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 10.0, m_pDoc->GetValue(aPos));
8438     m_pDoc->SetValue(0, 0, 0, -2.0); // A1
8439     aVal = m_pDoc->GetString(aPos);
8440     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for values less than 0",
8441             OUString("Err:502"), aVal);
8442     m_pDoc->SetString(0, 0, 0, "a"); // A1
8443     aVal = m_pDoc->GetString(aPos);
8444     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for a single string",
8445             OUString("#VALUE!"), aVal);
8446 
8447     m_pDoc->SetString(aPos, "=LCM(A1:B2)");
8448     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 1.0, m_pDoc->GetValue(aPos));
8449     m_pDoc->SetValue(0, 1, 0, -12.0); // B1
8450     aVal = m_pDoc->GetString(aPos);
8451     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for a matrix with values less than 0",
8452             OUString("Err:502"), aVal);
8453     m_pDoc->SetValue(0, 0, 0, 15.0); // A1
8454     m_pDoc->SetValue(0, 1, 0, 0.0); // A2
8455     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8456     m_pDoc->SetValue(1, 0, 0, 5.0); // B1
8457     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8458     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8459     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc->GetValue(aPos));
8460     m_pDoc->SetValue(1, 0, 0, 30.0); // B1
8461     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc->GetValue(aPos));
8462     m_pDoc->SetValue(0, 0, 0, 20.0); // A1
8463     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 60.0, m_pDoc->GetValue(aPos));
8464     m_pDoc->SetValue(1, 1, 0, 125.0); // B2
8465     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1500.0, m_pDoc->GetValue(aPos));
8466     m_pDoc->SetValue(1, 0, 0, 99.0); // B1
8467     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 49500.0, m_pDoc->GetValue(aPos));
8468     m_pDoc->SetValue(0, 1, 0, 37.0); // A2
8469     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc->GetValue(aPos));
8470 
8471     // with floor
8472     m_pDoc->SetValue(1, 0, 0, 99.89); // B1
8473     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc->GetValue(aPos));
8474     m_pDoc->SetValue(1, 1, 0, 11.32); // B2
8475     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 73260.0, m_pDoc->GetValue(aPos));
8476     m_pDoc->SetValue(0, 0, 0, 22.58); // A1
8477     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 7326.0, m_pDoc->GetValue(aPos));
8478     m_pDoc->SetValue(0, 1, 0, 3.99); // A2
8479     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 198.0, m_pDoc->GetValue(aPos));
8480 
8481     // inline array
8482     m_pDoc->SetString(aPos, "=LCM({3;6;9})");
8483     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc->GetValue(aPos));
8484     m_pDoc->SetString(aPos, "=LCM({150;0})");
8485     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8486     m_pDoc->SetString(aPos, "=LCM({-3;6;9})");
8487     aVal = m_pDoc->GetString(aPos);
8488     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
8489             OUString("Err:502"), aVal);
8490     m_pDoc->SetString(aPos, "=LCM({\"a\";6;9})");
8491     aVal = m_pDoc->GetString(aPos);
8492     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
8493             OUString("Err:502"), aVal);
8494 
8495         //many inline array
8496     m_pDoc->SetString(aPos, "=LCM({6;6;6};{3;6;9})");
8497     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc->GetValue(aPos));
8498     m_pDoc->SetString(aPos, "=LCM({300;300;300};{150;0})");
8499     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8500     m_pDoc->SetString(aPos,"=LCM({3;6;9};{3;-6;9})");
8501     aVal = m_pDoc->GetString(aPos);
8502     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
8503             OUString("Err:502"), aVal);
8504     m_pDoc->SetString(aPos, "=LCM({3;6;9};{\"a\";6;9})");
8505     aVal = m_pDoc->GetString(aPos);
8506     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
8507             OUString("Err:502"), aVal);
8508 
8509     m_pDoc->SetString(aPos, "=LCM(12;24;36;48;60)");
8510     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 720.0, m_pDoc->GetValue(aPos));
8511     m_pDoc->SetString(aPos, "=LCM(0;12;24;36;48;60)");
8512     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8513     m_pDoc->SetString(aPos, "=LCM(\"a\";1)");
8514     aVal = m_pDoc->GetString(aPos);
8515     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for an array with strings",
8516             OUString("#VALUE!"), aVal);
8517 
8518     m_pDoc->DeleteTab(0);
8519 }
8520 
testFuncSUMSQ()8521 void TestFormula::testFuncSUMSQ()
8522 {
8523     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8524 
8525     m_pDoc->InsertTab(0, "SUMSQTest");
8526 
8527     ScAddress aPos(4,0,0);
8528 
8529     m_pDoc->SetString(aPos, "=SUMSQ(A1)");
8530     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 0.0, m_pDoc->GetValue(aPos));
8531     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
8532     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8533     m_pDoc->SetValue(0, 0, 0, -1.0); // A1
8534     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8535     m_pDoc->SetValue(0, 1, 0, -2.0); // A2
8536     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8537 
8538     m_pDoc->SetString(aPos, "=SUMSQ(A1:A3)");
8539     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc->GetValue(aPos));
8540     m_pDoc->SetValue(1, 0, 0, 3.0); // B1
8541     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc->GetValue(aPos));
8542     m_pDoc->SetString(aPos, "=SUMSQ(A1:C3)");
8543     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc->GetValue(aPos));
8544     m_pDoc->SetValue(1, 1, 0, -4.0); // B2
8545     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 30.0, m_pDoc->GetValue(aPos));
8546     m_pDoc->SetString(1, 2, 0, "a"); // B3
8547     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc->GetValue(aPos));
8548     m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8549     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc->GetValue(aPos));
8550     m_pDoc->SetValue(0, 2, 0, 6.0); // A3
8551     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 66.0, m_pDoc->GetValue(aPos));
8552     m_pDoc->SetValue(2, 0, 0, -5.0); // C1
8553     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 91.0, m_pDoc->GetValue(aPos));
8554     m_pDoc->SetValue(2, 1, 0, 3.0); // C2
8555     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 100.0, m_pDoc->GetValue(aPos));
8556     m_pDoc->SetValue(2, 2, 0, 2.0); // C3
8557     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 104.0, m_pDoc->GetValue(aPos));
8558 
8559     // inline array
8560     m_pDoc->SetString(aPos, "=SUMSQ({1;2;3})");
8561     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc->GetValue(aPos));
8562     m_pDoc->SetString(aPos, "=SUMSQ({3;6;9})");
8563     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 126.0, m_pDoc->GetValue(aPos));
8564     m_pDoc->SetString(aPos, "=SUMSQ({15;0})");
8565     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 225.0, m_pDoc->GetValue(aPos));
8566     m_pDoc->SetString(aPos, "=SUMSQ({-3;3;1})");
8567     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 19.0, m_pDoc->GetValue(aPos));
8568     m_pDoc->SetString(aPos, "=SUMSQ({\"a\";-4;-5})");
8569     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 41.0, m_pDoc->GetValue(aPos));
8570 
8571     m_pDoc->SetString(aPos, "=SUMSQ({2;3};{4;5})");
8572     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 54.0, m_pDoc->GetValue(aPos));
8573     m_pDoc->SetString(aPos, "=SUMSQ({-3;3;1};{-1})");
8574     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 20.0, m_pDoc->GetValue(aPos));
8575     m_pDoc->SetString(aPos, "=SUMSQ({-4};{1;4;2};{-5;7};{9})");
8576     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 192.0, m_pDoc->GetValue(aPos));
8577     m_pDoc->SetString(aPos, "=SUMSQ({-2;2};{1};{-1};{0;0;0;4})");
8578     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc->GetValue(aPos));
8579 
8580     m_pDoc->SetString(aPos, "=SUMSQ(4;1;-3)");
8581     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc->GetValue(aPos));
8582     m_pDoc->SetString(aPos, "=SUMSQ(0;5;13;-7;-4)");
8583     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 259.0, m_pDoc->GetValue(aPos));
8584     m_pDoc->SetString(aPos, "=SUMSQ(0;12;24;36;48;60)");
8585     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc->GetValue(aPos));
8586     m_pDoc->SetString(aPos, "=SUMSQ(0;-12;-24;36;-48;60)");
8587     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc->GetValue(aPos));
8588     m_pDoc->SetString(aPos, "=SUMSQ(\"a\";1;\"d\";-4;2)");
8589     OUString aVal = m_pDoc->GetString(aPos);
8590     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMSQ should return #VALUE! for an array with strings",
8591             OUString("#VALUE!"), aVal);
8592 
8593     m_pDoc->DeleteTab(0);
8594 }
8595 
testFuncMDETERM()8596 void TestFormula::testFuncMDETERM()
8597 {
8598     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8599 
8600     m_pDoc->InsertTab(0, "MDETERM_test");
8601     ScAddress aPos(8,0,0);
8602     OUString const aColCodes("ABCDEFGH");
8603     OUStringBuffer aFormulaBuffer("=MDETERM(A1:B2)");
8604     for( SCSIZE nSize = 3; nSize <= 8; nSize++ )
8605     {
8606         double fVal = 1.0;
8607         // Generate a singular integer matrix
8608         for( SCROW nRow = 0; nRow < static_cast<SCROW>(nSize); nRow++ )
8609         {
8610             for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(nSize); nCol++ )
8611             {
8612                 m_pDoc->SetValue(nCol, nRow, 0, fVal);
8613                 fVal += 1.0;
8614             }
8615         }
8616         aFormulaBuffer[12] = aColCodes[nSize-1];
8617         aFormulaBuffer[13] = static_cast<sal_Unicode>( '0' + nSize );
8618         m_pDoc->SetString(aPos, aFormulaBuffer.toString());
8619 
8620 #if SAL_TYPES_SIZEOFPOINTER == 4
8621         // On crappy 32-bit targets, presumably without extended precision on
8622         // interim results or optimization not catching it, this test fails
8623         // when comparing to 0.0, so have a narrow error margin. See also
8624         // commit message of 8140309d636d4a870875f2dd75ed3dfff2c0fbaf
8625         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8626                 0.0, m_pDoc->GetValue(aPos), 1e-12);
8627 #else
8628         // Even on one (and only one) x86_64 target the result was
8629         // 6.34413156928661e-17 instead of 0.0 (tdf#99730) so lower the bar to
8630         // 10e-14.
8631         // Then again on aarch64, ppc64* and s390x it also fails.
8632         // Sigh... why do we even test this? The original complaint in tdf#32834
8633         // was about -9.51712667007776E-016
8634         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8635                 0.0, m_pDoc->GetValue(aPos), 1e-14);
8636 #endif
8637     }
8638 
8639     int const aVals[] = {23, 31, 13, 12, 34, 64, 34, 31, 98, 32, 33, 63, 45, 54, 65, 76};
8640     int nIdx = 0;
8641     for( SCROW nRow = 0; nRow < 4; nRow++ )
8642         for( SCCOL nCol = 0; nCol < 4; nCol++ )
8643             m_pDoc->SetValue(nCol, nRow, 0, static_cast<double>(aVals[nIdx++]));
8644     m_pDoc->SetString(aPos, "=MDETERM(A1:D4)");
8645     // Following test is conservative in the sense that on Linux x86_64 the error is less that 1.0E-9
8646     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for non-singular integer matrix",
8647                                          -180655.0, m_pDoc->GetValue(aPos), 1.0E-6);
8648     m_pDoc->DeleteTab(0);
8649 }
8650 
testFormulaErrorPropagation()8651 void TestFormula::testFormulaErrorPropagation()
8652 {
8653     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8654 
8655     m_pDoc->InsertTab(0, "Sheet1");
8656 
8657     ScMarkData aMark(m_pDoc->GetSheetLimits());
8658     aMark.SelectOneTable(0);
8659     ScAddress aPos, aPos2;
8660     const OUString aTRUE("TRUE");
8661     const OUString aFALSE("FALSE");
8662 
8663     aPos.Set(0,0,0);// A1
8664     m_pDoc->SetValue( aPos, 1.0);
8665     aPos.IncCol();  // B1
8666     m_pDoc->SetValue( aPos, 2.0);
8667     aPos.IncCol();
8668 
8669     aPos.IncRow();  // C2
8670     m_pDoc->SetString( aPos, "=ISERROR(A1:B1+3)");
8671     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8672 
8673     aPos.IncRow();  // C3
8674     m_pDoc->SetString( aPos, "=ISERROR(A1:B1+{3})");
8675     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8676     aPos.IncRow();  // C4
8677     aPos2 = aPos;
8678     aPos2.IncCol(); // D4
8679     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(A1:B1+{3})");
8680     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8681     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8682 
8683     aPos.IncRow();  // C5
8684     m_pDoc->SetString( aPos, "=ISERROR({1;\"x\"}+{3;4})");
8685     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8686     aPos.IncRow();  // C6
8687     aPos2 = aPos;
8688     aPos2.IncCol(); // D6
8689     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR({1;\"x\"}+{3;4})");
8690     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8691     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos2));
8692 
8693     aPos.IncRow();  // C7
8694     m_pDoc->SetString( aPos, "=ISERROR({\"x\";2}+{3;4})");
8695     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8696     aPos.IncRow();  // C8
8697     aPos2 = aPos;
8698     aPos2.IncCol(); // D8
8699     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR({\"x\";2}+{3;4})");
8700     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8701     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8702 
8703     aPos.IncRow();  // C9
8704     m_pDoc->SetString( aPos, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8705     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8706     aPos.IncRow();  // C10
8707     aPos2 = aPos;
8708     aPos2.IncCol(); // D10
8709     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8710     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8711     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos2));
8712 
8713     aPos.IncRow();  // C11
8714     m_pDoc->SetString( aPos, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8715     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8716     aPos.IncRow();  // C12
8717     aPos2 = aPos;
8718     aPos2.IncCol(); // D12
8719     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8720     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8721     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8722 
8723     m_pDoc->DeleteTab(0);
8724 }
8725 
testTdf97369()8726 void TestFormula::testTdf97369()
8727 {
8728     const SCROW TOTAL_ROWS = 330;
8729     const SCROW ROW_RANGE = 10;
8730     const SCROW START1 = 9;
8731     const SCROW END1 = 159;
8732     const SCROW START2 = 169;
8733     const SCROW END2 = 319;
8734 
8735     const double SHIFT1 = 200;
8736     const double SHIFT2 = 400;
8737 
8738     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8739                             m_pDoc->InsertTab (0, "tdf97369"));
8740 
8741     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8742 
8743     // set up columns A, B, C
8744     for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8745     {
8746         m_pDoc->SetValue(ScAddress(0, i, 0), i);          // A
8747         m_pDoc->SetValue(ScAddress(1, i, 0), i + SHIFT1); // B
8748         m_pDoc->SetValue(ScAddress(2, i, 0), i + SHIFT2); // C
8749     }
8750 
8751     const ColumnTest columnTest( m_pDoc, TOTAL_ROWS, START1, END1, START2, END2 );
8752 
8753     auto lExpectedinD = [=] (SCROW n) {
8754         return 3.0 * (n-START1) + SHIFT1 + SHIFT2;
8755     };
8756     columnTest(3, "=SUM(A1:C1)", lExpectedinD);
8757 
8758     auto lExpectedinE = [=] (SCROW ) {
8759         return SHIFT1 + SHIFT2;
8760     };
8761     columnTest(4, "=SUM(A$1:C$1)", lExpectedinE);
8762 
8763     auto lExpectedinF = [=] (SCROW n) {
8764         return ((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2.0;
8765     };
8766     columnTest(5, "=SUM(A1:A10)", lExpectedinF);
8767 
8768     auto lExpectedinG = [=] (SCROW n) {
8769         return ((n + 1) * n) / 2.0;
8770     };
8771     columnTest(6, "=SUM(A$1:A10)", lExpectedinG);
8772 
8773     auto lExpectedinH = [=] (SCROW n) {
8774         return 3.0 * (((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8775     };
8776     columnTest(7, "=SUM(A1:C10)", lExpectedinH);
8777 
8778     auto lExpectedinI = [=] (SCROW ) {
8779         return 3.0 * (((2*START1 + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8780     };
8781     columnTest(8, "=SUM(A$1:C$10)", lExpectedinI);
8782 
8783     m_pDoc->DeleteTab(0);
8784 }
8785 
testTdf97587()8786 void TestFormula::testTdf97587()
8787 {
8788     const SCROW TOTAL_ROWS = 150;
8789     const SCROW ROW_RANGE = 10;
8790 
8791     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8792                             m_pDoc->InsertTab (0, "tdf97587"));
8793 
8794     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8795 
8796     std::set<SCROW> emptyCells = {0, 100};
8797     for( SCROW i = 0; i < ROW_RANGE; ++i )
8798     {
8799         emptyCells.insert(i + TOTAL_ROWS / 3);
8800         emptyCells.insert(i + TOTAL_ROWS);
8801     }
8802 
8803     // set up columns A
8804     for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8805     {
8806         if( emptyCells.find(i) != emptyCells.end() )
8807             continue;
8808         m_pDoc->SetValue(ScAddress(0, i, 0), 1.0);
8809     }
8810 
8811     ScDocument aClipDoc(SCDOCMODE_CLIP);
8812     ScMarkData aMark(m_pDoc->GetSheetLimits());
8813 
8814     ScAddress aPos(1, 0, 0);
8815     m_pDoc->SetString(aPos, "=SUM(A1:A10)");
8816 
8817     // Copy formula cell to clipboard.
8818     ScClipParam aClipParam(aPos, false);
8819     aMark.SetMarkArea(aPos);
8820     m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
8821 
8822     // Paste it to first range.
8823     ScRange aDestRange(1, 1, 0, 1, TOTAL_ROWS + ROW_RANGE, 0);
8824     aMark.SetMarkArea(aDestRange);
8825     m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
8826 
8827     // Check the formula results in column B.
8828     for( SCROW i = 0; i < TOTAL_ROWS + 1; ++i )
8829     {
8830         int k = std::count_if( emptyCells.begin(), emptyCells.end(),
8831                 [=](SCROW n) { return (i <= n && n < i + ROW_RANGE); } );
8832         double fExpected = ROW_RANGE - k;
8833         ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(1,i,0)));
8834     }
8835     m_pDoc->DeleteTab(0);
8836 }
8837 
testTdf93415()8838 void TestFormula::testTdf93415()
8839 {
8840     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Sheet1"));
8841 
8842     ScCalcConfig aConfig;
8843     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
8844     m_pDoc->SetCalcConfig(aConfig);
8845     m_pDoc->CalcAll();
8846 
8847     ScAddress aPos(0,0,0);
8848     m_pDoc->SetString(aPos, "=ADDRESS(1;1;;;\"Sheet1\")");
8849 
8850     // Without the fix in place, this would have failed with
8851     // - Expected: Sheet1!$A$1
8852     // - Actual  : Sheet1.$A$1
8853     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1!$A$1"), m_pDoc->GetString(aPos));
8854 
8855     m_pDoc->DeleteTab(0);
8856 }
8857 
testTdf100818()8858 void TestFormula::testTdf100818()
8859 {
8860     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Sheet1"));
8861 
8862     //Insert local range name
8863     ScRangeData* pLocal = new ScRangeData( *m_pDoc, "local", "$Sheet1.$A$1");
8864     std::unique_ptr<ScRangeName> pLocalRangeName(new ScRangeName);
8865     pLocalRangeName->insert(pLocal);
8866     m_pDoc->SetRangeName(0, std::move(pLocalRangeName));
8867 
8868     m_pDoc->SetValue(0, 0, 0, 1.0);
8869 
8870     CPPUNIT_ASSERT(m_pDoc->InsertTab (1, "Sheet2"));
8871 
8872     m_pDoc->SetString(0, 0, 1, "=INDIRECT(\"Sheet1.local\")");
8873 
8874     // Without the fix in place, this test would have failed with
8875     // - Expected: 1
8876     // - Actual  : #REF!
8877     CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(0,0,1));
8878 
8879     m_pDoc->DeleteTab(1);
8880     m_pDoc->SetRangeName(0,nullptr); // Delete the names.
8881     m_pDoc->DeleteTab(0);
8882 }
8883 
testMatConcat()8884 void TestFormula::testMatConcat()
8885 {
8886     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8887 
8888     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8889     {
8890         for (SCROW nRow = 0; nRow < 10; ++nRow)
8891         {
8892             m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8893         }
8894     }
8895 
8896     ScMarkData aMark(m_pDoc->GetSheetLimits());
8897     aMark.SelectOneTable(0);
8898     m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J10");
8899 
8900     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8901     {
8902         for (SCROW nRow = 12; nRow < 22; ++nRow)
8903         {
8904             OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8905             CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + OUString::number(nCol * (nRow - 12))), aStr);
8906         }
8907     }
8908 
8909     {   // Data in A12:B16
8910         std::vector<std::vector<const char*>> aData = {
8911             { "q", "w" },
8912             { "a",  "" },
8913             {  "", "x" },
8914             {  "",  "" },
8915             { "e", "r" },
8916         };
8917 
8918         ScAddress aPos(0,11,0);
8919         ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
8920         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
8921     }
8922     // Matrix formula in C17:C21
8923     m_pDoc->InsertMatrixFormula(2, 16, 2, 20, aMark, "=A12:A16&B12:B16");
8924     // Check proper concatenation including empty cells.
8925     OUString aStr;
8926     ScAddress aPos(2,16,0);
8927     aStr = m_pDoc->GetString(aPos);
8928     CPPUNIT_ASSERT_EQUAL(OUString("qw"),aStr);
8929     aPos.IncRow();
8930     aStr = m_pDoc->GetString(aPos);
8931     CPPUNIT_ASSERT_EQUAL(OUString("a"),aStr);
8932     aPos.IncRow();
8933     aStr = m_pDoc->GetString(aPos);
8934     CPPUNIT_ASSERT_EQUAL(OUString("x"),aStr);
8935     aPos.IncRow();
8936     aStr = m_pDoc->GetString(aPos);
8937     CPPUNIT_ASSERT_EQUAL(OUString(),aStr);
8938     aPos.IncRow();
8939     aStr = m_pDoc->GetString(aPos);
8940     CPPUNIT_ASSERT_EQUAL(OUString("er"),aStr);
8941 
8942     m_pDoc->DeleteTab(0);
8943 }
8944 
testMatConcatReplication()8945 void TestFormula::testMatConcatReplication()
8946 {
8947     // if one of the matrices is a one column or row matrix
8948     // the matrix is replicated across the larger matrix
8949     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8950 
8951     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8952     {
8953         for (SCROW nRow = 0; nRow < 10; ++nRow)
8954         {
8955             m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8956         }
8957     }
8958 
8959     ScMarkData aMark(m_pDoc->GetSheetLimits());
8960     aMark.SelectOneTable(0);
8961     m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J1");
8962 
8963     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8964     {
8965         for (SCROW nRow = 12; nRow < 22; ++nRow)
8966         {
8967             OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8968             CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + "0"), aStr);
8969         }
8970     }
8971 
8972     m_pDoc->DeleteTab(0);
8973 }
8974 
testRefR1C1WholeCol()8975 void TestFormula::testRefR1C1WholeCol()
8976 {
8977     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8978 
8979     ScAddress aPos(1, 1, 1);
8980     ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
8981     std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString("=C[10]"));
8982     sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
8983     OUString aFormula = pTokens->CreateString(aCxt, aPos);
8984 
8985     CPPUNIT_ASSERT_EQUAL(OUString("L:L"), aFormula);
8986 
8987     m_pDoc->DeleteTab(0);
8988 }
8989 
testRefR1C1WholeRow()8990 void TestFormula::testRefR1C1WholeRow()
8991 {
8992     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8993 
8994     ScAddress aPos(1, 1, 1);
8995     ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
8996     std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString("=R[3]"));
8997     sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
8998     OUString aFormula = pTokens->CreateString(aCxt, aPos);
8999 
9000     CPPUNIT_ASSERT_EQUAL(OUString("5:5"), aFormula);
9001 
9002     m_pDoc->DeleteTab(0);
9003 }
9004 
testSingleCellCopyColumnLabel()9005 void TestFormula::testSingleCellCopyColumnLabel()
9006 {
9007     ScDocOptions aOptions = m_pDoc->GetDocOptions();
9008     aOptions.SetLookUpColRowNames(true);
9009     m_pDoc->SetDocOptions(aOptions);
9010     m_pDoc->InsertTab(0, "Test");
9011 
9012     m_pDoc->SetString(0, 0, 0, "a");
9013     m_pDoc->SetValue(0, 1, 0, 1.0);
9014     m_pDoc->SetValue(0, 2, 0, 2.0);
9015     m_pDoc->SetValue(0, 3, 0, 3.0);
9016     m_pDoc->SetString(1, 1, 0, "='a'");
9017 
9018     double nVal = m_pDoc->GetValue(1, 1, 0);
9019     ASSERT_DOUBLES_EQUAL(1.0, nVal);
9020 
9021     ScDocument aClipDoc(SCDOCMODE_CLIP);
9022     copyToClip(m_pDoc, ScRange(1, 1, 0), &aClipDoc);
9023     pasteOneCellFromClip(m_pDoc, ScRange(1, 2, 0), &aClipDoc);
9024     nVal = m_pDoc->GetValue(1, 2, 0);
9025     ASSERT_DOUBLES_EQUAL(2.0, nVal);
9026 
9027     m_pDoc->DeleteTab(0);
9028 }
9029 
9030 // Significant whitespace operator intersection in Excel syntax, tdf#96426
testIntersectionOpExcel()9031 void TestFormula::testIntersectionOpExcel()
9032 {
9033     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
9034 
9035     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
9036     // Horizontal cell range covering C2.
9037     pGlobalNames->insert( new ScRangeData( *m_pDoc, "horz", "$B$2:$D$2"));
9038     // Vertical cell range covering C2.
9039     pGlobalNames->insert( new ScRangeData( *m_pDoc, "vert", "$C$1:$C$3"));
9040     // Data in C2.
9041     m_pDoc->SetValue(2,1,0, 1.0);
9042 
9043     m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH_XL_A1);
9044 
9045     // Choose formula positions that don't intersect with those data ranges.
9046     ScAddress aPos(0,3,0);
9047     m_pDoc->SetString(aPos,"=B2:D2 C1:C3");
9048     CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 intersecting references failed", 1.0, m_pDoc->GetValue(aPos));
9049     aPos.IncRow();
9050     m_pDoc->SetString(aPos,"=horz vert");
9051     CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 intersecting named expressions failed", 1.0, m_pDoc->GetValue(aPos));
9052     aPos.IncRow();
9053     m_pDoc->SetString(aPos,"=(horz vert)*2");
9054     CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 calculating with intersecting named expressions failed", 2.0, m_pDoc->GetValue(aPos));
9055     aPos.IncRow();
9056     m_pDoc->SetString(aPos,"=2*(horz vert)");
9057     CPPUNIT_ASSERT_EQUAL_MESSAGE("A7 calculating with intersecting named expressions failed", 2.0, m_pDoc->GetValue(aPos));
9058 
9059     m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH);
9060 
9061     m_pDoc->DeleteTab(0);
9062 }
9063 
9064 //Test Subtotal and Aggregate during hide rows #tdf93171
testFuncRowsHidden()9065 void TestFormula::testFuncRowsHidden()
9066 {
9067     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9068     m_pDoc->InsertTab(0, "Test");
9069     m_pDoc->SetValue(0, 0, 0, 1); //A1
9070     m_pDoc->SetValue(0, 1, 0, 2); //A2
9071     m_pDoc->SetValue(0, 2, 0, 4); //A3
9072     m_pDoc->SetValue(0, 3, 0, 8); //A4
9073     m_pDoc->SetValue(0, 4, 0, 16); //A5
9074     m_pDoc->SetValue(0, 5, 0, 32); //A6
9075 
9076     ScAddress aPos(0,6,0);
9077     m_pDoc->SetString(aPos, "=SUBTOTAL(109; A1:A6)");
9078     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc->GetValue(aPos));
9079     //Hide row 1
9080     m_pDoc->SetRowHidden(0, 0, 0, true);
9081     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 62.0, m_pDoc->GetValue(aPos));
9082     m_pDoc->SetRowHidden(0, 0, 0, false);
9083     //Hide row 2 and 3
9084     m_pDoc->SetRowHidden(1, 2, 0, true);
9085     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 57.0, m_pDoc->GetValue(aPos));
9086     m_pDoc->SetRowHidden(1, 2, 0, false);
9087     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc->GetValue(aPos));
9088 
9089     m_pDoc->SetString(aPos, "=AGGREGATE(9; 5; A1:A6)"); //9=SUM 5=Ignore only hidden rows
9090     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc->GetValue(aPos));
9091     //Hide row 1
9092     m_pDoc->SetRowHidden(0, 0, 0, true);
9093     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 62.0, m_pDoc->GetValue(aPos));
9094     m_pDoc->SetRowHidden(0, 0, 0, false);
9095     //Hide rows 3 to 5
9096     m_pDoc->SetRowHidden(2, 4, 0, true);
9097     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 35.0, m_pDoc->GetValue(aPos));
9098     m_pDoc->SetRowHidden(2, 4, 0, false);
9099     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc->GetValue(aPos));
9100 
9101     m_pDoc->SetString(aPos, "=SUM(A1:A6)");
9102     m_pDoc->SetRowHidden(2, 4, 0, true);
9103     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUM failed", 63.0, m_pDoc->GetValue(aPos));
9104 
9105     m_pDoc->DeleteTab(0);
9106 }
9107 
9108 // Test COUNTIFS, SUMIFS, AVERAGEIFS in array context.
testFuncSUMIFS()9109 void TestFormula::testFuncSUMIFS()
9110 {
9111     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9112     m_pDoc->InsertTab(0, "Test");
9113 
9114     // Data in A1:B7, query in A9:A11
9115     std::vector<std::vector<const char*>> aData = {
9116         { "a",  "1" },
9117         { "b",  "2" },
9118         { "c",  "4" },
9119         { "d",  "8" },
9120         { "a", "16" },
9121         { "b", "32" },
9122         { "c", "64" },
9123         { "" },             // {} doesn't work with some compilers
9124         { "a" },
9125         { "b" },
9126         { "c" },
9127     };
9128 
9129     insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
9130 
9131     ScMarkData aMark(m_pDoc->GetSheetLimits());
9132     aMark.SelectOneTable(0);
9133     // Matrix formula in C8:C10 with SUMIFS
9134     m_pDoc->InsertMatrixFormula(2, 7, 2, 9, aMark, "=SUMIFS(B1:B7;A1:A7;A9:A11)");
9135     // Matrix formula in D8:D10 with COUNTIFS
9136     m_pDoc->InsertMatrixFormula(3, 7, 3, 9, aMark, "=COUNTIFS(A1:A7;A9:A11)");
9137     // Matrix formula in E8:E10 with AVERAGEIFS
9138     m_pDoc->InsertMatrixFormula(4, 7, 4, 9, aMark, "=AVERAGEIFS(B1:B7;A1:A7;A9:A11)");
9139 
9140     {
9141         // Result B1+B5, B2+B6, B3+B7 and counts and averages.
9142         std::vector<std::vector<const char*>> aCheck = {
9143             { "17", "2",  "8.5" },
9144             { "34", "2", "17" },
9145             { "68", "2", "34" }
9146         };
9147         bool bGood = checkOutput(m_pDoc, ScRange(2,7,0, 4,9,0), aCheck,
9148                 "SUMIFS, COUNTIFS and AVERAGEIFS in array context");
9149         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS in array context failed", bGood);
9150     }
9151 
9152     // Matrix formula in G8:G10 with SUMIFS and reference list arrays.
9153     m_pDoc->InsertMatrixFormula(6, 7, 6, 9, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
9154     // Matrix formula in H8:H10 with COUNTIFS and reference list arrays.
9155     m_pDoc->InsertMatrixFormula(7, 7, 7, 9, aMark, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\")");
9156     // Matrix formula in I8:I10 with AVERAGEIFS and reference list arrays.
9157     m_pDoc->InsertMatrixFormula(8, 7, 8, 9, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
9158 
9159     {
9160         // Result sums, counts and averages.
9161         std::vector<std::vector<const char*>> aCheck = {
9162             {  "0", "0", "#DIV/0!" },
9163             {  "8", "1",  "8" },
9164             { "24", "2", "12" }
9165         };
9166         bool bGood = checkOutput(m_pDoc, ScRange(6,7,0, 8,9,0), aCheck,
9167                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays");
9168         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays failed", bGood);
9169     }
9170 
9171     // Matrix formula in K8:K10 with SUMIFS and reference list array condition
9172     // and "normal" data range.
9173     m_pDoc->InsertMatrixFormula(10, 7, 10, 9, aMark, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
9174     // Matrix formula in L8:L10 with AVERAGEIFS and reference list array
9175     // condition and "normal" data range.
9176     m_pDoc->InsertMatrixFormula(11, 7, 11, 9, aMark, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
9177 
9178     {
9179         // Result sums and averages.
9180         std::vector<std::vector<const char*>> aCheck = {
9181             { "0", "#DIV/0!" },
9182             { "2", "2" },
9183             { "3", "1.5" }
9184         };
9185         bool bGood = checkOutput(m_pDoc, ScRange(10,7,0, 11,9,0), aCheck,
9186                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal range");
9187         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal range failed", bGood);
9188     }
9189 
9190     // Matrix formula in G18:G20 with SUMIFS and reference list arrays and a
9191     // "normal" criteria range.
9192     m_pDoc->InsertMatrixFormula(6, 17, 6, 19, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9193     // Matrix formula in H18:H20 with COUNTIFS and reference list arrays and a
9194     // "normal" criteria range.
9195     m_pDoc->InsertMatrixFormula(7, 17, 7, 19, aMark, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9196     // Matrix formula in I18:I20 with AVERAGEIFS and reference list arrays and
9197     // a "normal" criteria range.
9198     m_pDoc->InsertMatrixFormula(8, 17, 8, 19, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9199 
9200     {
9201         // Result sums, counts and averages.
9202         std::vector<std::vector<const char*>> aCheck = {
9203             {  "0", "0", "#DIV/0!" },
9204             {  "8", "1",  "8" },
9205             { "16", "1", "16" }
9206         };
9207         bool bGood = checkOutput(m_pDoc, ScRange(6,17,0, 8,19,0), aCheck,
9208                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range");
9209         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed", bGood);
9210     }
9211 
9212     // Matrix formula in K18:K20 with SUMIFS and reference list array condition
9213     // and "normal" data range and a "normal" criteria range.
9214     m_pDoc->InsertMatrixFormula(10, 17, 10, 19, aMark, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9215     // Matrix formula in L18:L20 with AVERAGEIFS and reference list array
9216     // condition and "normal" data range and a "normal" criteria range.
9217     m_pDoc->InsertMatrixFormula(11, 17, 11, 19, aMark, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9218 
9219     {
9220         // Result sums and averages.
9221         std::vector<std::vector<const char*>> aCheck = {
9222             { "0", "#DIV/0!" },
9223             { "2", "2" },
9224             { "2", "2" }
9225         };
9226         bool bGood = checkOutput(m_pDoc, ScRange(10,17,0, 11,19,0), aCheck,
9227                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range");
9228         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed", bGood);
9229     }
9230 
9231     // Same, but swapped normal and array criteria.
9232 
9233     // Matrix formula in G28:G30 with SUMIFS and reference list arrays and a
9234     // "normal" criteria range, swapped.
9235     m_pDoc->InsertMatrixFormula(6, 27, 6, 29, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9236     // Matrix formula in H28:H30 with COUNTIFS and reference list arrays and a
9237     // "normal" criteria range, swapped.
9238     m_pDoc->InsertMatrixFormula(7, 27, 7, 29, aMark, "=COUNTIFS(B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9239     // Matrix formula in I28:I30 with AVERAGEIFS and reference list arrays and
9240     // a "normal" criteria range, swapped.
9241     m_pDoc->InsertMatrixFormula(8, 27, 8, 29, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9242 
9243     {
9244         // Result sums, counts and averages.
9245         std::vector<std::vector<const char*>> aCheck = {
9246             {  "0", "0", "#DIV/0!" },
9247             {  "8", "1",  "8" },
9248             { "16", "1", "16" }
9249         };
9250         bool bGood = checkOutput(m_pDoc, ScRange(6,27,0, 8,29,0), aCheck,
9251                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range, swapped");
9252         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed, swapped", bGood);
9253     }
9254 
9255     // Matrix formula in K28:K30 with SUMIFS and reference list array condition
9256     // and "normal" data range and a "normal" criteria range, swapped.
9257     m_pDoc->InsertMatrixFormula(10, 27, 10, 29, aMark, "=SUMIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9258     // Matrix formula in L28:L30 with AVERAGEIFS and reference list array
9259     // condition and "normal" data range and a "normal" criteria range,
9260     // swapped.
9261     m_pDoc->InsertMatrixFormula(11, 27, 11, 29, aMark, "=AVERAGEIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9262 
9263     {
9264         // Result sums and averages.
9265         std::vector<std::vector<const char*>> aCheck = {
9266             { "0", "#DIV/0!" },
9267             { "2", "2" },
9268             { "2", "2" }
9269         };
9270         bool bGood = checkOutput(m_pDoc, ScRange(10,27,0, 11,29,0), aCheck,
9271                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range, swapped");
9272         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed, swapped", bGood);
9273     }
9274 
9275     m_pDoc->DeleteTab(0);
9276 }
9277 
9278 // Test SUBTOTAL with reference lists in array context.
testFuncRefListArraySUBTOTAL()9279 void TestFormula::testFuncRefListArraySUBTOTAL()
9280 {
9281     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9282     m_pDoc->InsertTab(0, "Test");
9283 
9284     m_pDoc->SetValue(0,0,0,  1.0);  // A1
9285     m_pDoc->SetValue(0,1,0,  2.0);  // A2
9286     m_pDoc->SetValue(0,2,0,  4.0);  // A3
9287     m_pDoc->SetValue(0,3,0,  8.0);  // A4
9288     m_pDoc->SetValue(0,4,0, 16.0);  // A5
9289     m_pDoc->SetValue(0,5,0, 32.0);  // A6
9290 
9291     // Matrix in B7:B9, individual SUM of A2:A3, A3:A4 and A4:A5
9292     ScMarkData aMark(m_pDoc->GetSheetLimits());
9293     aMark.SelectOneTable(0);
9294     m_pDoc->InsertMatrixFormula(1, 6, 1, 8, aMark, "=SUBTOTAL(9;OFFSET(A1;ROW(1:3);0;2))");
9295     ScAddress aPos(1,6,0);
9296     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A2:A3 failed",  6.0, m_pDoc->GetValue(aPos));
9297     aPos.IncRow();
9298     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A3:A4 failed", 12.0, m_pDoc->GetValue(aPos));
9299     aPos.IncRow();
9300     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A4:A5 failed", 24.0, m_pDoc->GetValue(aPos));
9301 
9302     // Matrix in C7:C9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
9303     m_pDoc->InsertMatrixFormula(2, 6, 2, 8, aMark, "=SUBTOTAL(1;OFFSET(A1;ROW(1:3);0;2))");
9304     aPos.Set(2,6,0);
9305     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A2:A3 failed",  3.0, m_pDoc->GetValue(aPos));
9306     aPos.IncRow();
9307     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A3:A4 failed",  6.0, m_pDoc->GetValue(aPos));
9308     aPos.IncRow();
9309     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A4:A5 failed", 12.0, m_pDoc->GetValue(aPos));
9310 
9311     // Matrix in D7:D9, individual MIN of A2:A3, A3:A4 and A4:A5
9312     m_pDoc->InsertMatrixFormula(3, 6, 3, 8, aMark, "=SUBTOTAL(5;OFFSET(A1;ROW(1:3);0;2))");
9313     aPos.Set(3,6,0);
9314     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A2:A3 failed",  2.0, m_pDoc->GetValue(aPos));
9315     aPos.IncRow();
9316     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A3:A4 failed",  4.0, m_pDoc->GetValue(aPos));
9317     aPos.IncRow();
9318     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A4:A5 failed",  8.0, m_pDoc->GetValue(aPos));
9319 
9320     // Matrix in E7:E9, individual MAX of A2:A3, A3:A4 and A4:A5
9321     m_pDoc->InsertMatrixFormula(4, 6, 4, 8, aMark, "=SUBTOTAL(4;OFFSET(A1;ROW(1:3);0;2))");
9322     aPos.Set(4,6,0);
9323     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A2:A3 failed",  4.0, m_pDoc->GetValue(aPos));
9324     aPos.IncRow();
9325     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A3:A4 failed",  8.0, m_pDoc->GetValue(aPos));
9326     aPos.IncRow();
9327     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A4:A5 failed", 16.0, m_pDoc->GetValue(aPos));
9328 
9329     // Matrix in F7:F9, individual STDEV of A2:A3, A3:A4 and A4:A5
9330     m_pDoc->InsertMatrixFormula(5, 6, 5, 8, aMark, "=SUBTOTAL(7;OFFSET(A1;ROW(1:3);0;2))");
9331     aPos.Set(5,6,0);
9332     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A2:A3 failed", 1.414214, m_pDoc->GetValue(aPos), 1e-6);
9333     aPos.IncRow();
9334     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A3:A4 failed", 2.828427, m_pDoc->GetValue(aPos), 1e-6);
9335     aPos.IncRow();
9336     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A4:A5 failed", 5.656854, m_pDoc->GetValue(aPos), 1e-6);
9337 
9338     // Matrix in G7:G9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
9339     // Plus two "ordinary" ranges, one before and one after.
9340     m_pDoc->InsertMatrixFormula(6, 6, 6, 8, aMark, "=SUBTOTAL(1;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
9341     aPos.Set(6,6,0);
9342     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A2:A3,A5:A6 failed",  9.5, m_pDoc->GetValue(aPos));
9343     aPos.IncRow();
9344     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A3:A4,A5:A6 failed", 10.5, m_pDoc->GetValue(aPos));
9345     aPos.IncRow();
9346     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A4:A5,A5:A6 failed", 12.5, m_pDoc->GetValue(aPos));
9347 
9348     // Matrix in H7:H9, individual MAX of A2:A3, A3:A4 and A4:A5
9349     // Plus two "ordinary" ranges, one before and one after.
9350     m_pDoc->InsertMatrixFormula(7, 6, 7, 8, aMark, "=SUBTOTAL(4;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
9351     aPos.Set(7,6,0);
9352     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A2:A3,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
9353     aPos.IncRow();
9354     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A3:A4,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
9355     aPos.IncRow();
9356     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A4:A5,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
9357 
9358     // Matrix in I7:I9, individual STDEV of A2:A3, A3:A4 and A4:A5
9359     // Plus two "ordinary" ranges, one before and one after.
9360     m_pDoc->InsertMatrixFormula(8, 6, 8, 8, aMark, "=SUBTOTAL(7;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
9361     aPos.Set(8,6,0);
9362     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A2:A3,A5:A6 failed", 12.35718, m_pDoc->GetValue(aPos), 1e-5);
9363     aPos.IncRow();
9364     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A3:A4,A5:A6 failed", 11.86170, m_pDoc->GetValue(aPos), 1e-5);
9365     aPos.IncRow();
9366     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A4:A5,A5:A6 failed", 11.55422, m_pDoc->GetValue(aPos), 1e-5);
9367 
9368     // Empty two cells such that they affect two ranges.
9369     m_pDoc->SetString(0,1,0, "");   // A2
9370     m_pDoc->SetString(0,2,0, "");   // A3
9371     // Matrix in J7:J9, individual COUNTBLANK of A2:A3, A3:A4 and A4:A5
9372     m_pDoc->InsertMatrixFormula(9, 6, 9, 8, aMark, "=COUNTBLANK(OFFSET(A1;ROW(1:3);0;2))");
9373     aPos.Set(9,6,0);
9374     CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A2:A3,A5:A6 failed", 2.0, m_pDoc->GetValue(aPos));
9375     aPos.IncRow();
9376     CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A3:A4,A5:A6 failed", 1.0, m_pDoc->GetValue(aPos));
9377     aPos.IncRow();
9378     CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A4:A5,A5:A6 failed", 0.0, m_pDoc->GetValue(aPos));
9379 
9380     // Restore these two cell values so we'd catch failures below.
9381     m_pDoc->SetValue(0,1,0,  2.0);  // A2
9382     m_pDoc->SetValue(0,2,0,  4.0);  // A3
9383     // Hide rows 2 to 4.
9384     m_pDoc->SetRowHidden(1,3,0, true);
9385     // Matrix in K7, array of references as OFFSET result.
9386     m_pDoc->InsertMatrixFormula(10, 6, 10, 6, aMark, "=SUM(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
9387     aPos.Set(10,6,0);
9388     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM SUBTOTAL failed", 49.0, m_pDoc->GetValue(aPos));
9389     aPos.IncRow();
9390     // ForceArray in K8, array of references as OFFSET result.
9391     m_pDoc->SetString( aPos, "=SUMPRODUCT(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
9392     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMPRODUCT SUBTOTAL failed", 49.0, m_pDoc->GetValue(aPos));
9393 
9394     m_pDoc->DeleteTab(0);
9395 }
9396 
9397 // tdf#115493 jump commands return the matrix result instead of the reference
9398 // list array.
testFuncJumpMatrixArrayIF()9399 void TestFormula::testFuncJumpMatrixArrayIF()
9400 {
9401     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9402     m_pDoc->InsertTab(0, "Test");
9403 
9404     m_pDoc->SetString(0,0,0, "a");  // A1
9405     std::vector<std::vector<const char*>> aData = {
9406         { "a", "1" },
9407         { "b", "2" },
9408         { "a", "4" }
9409     };                              // A7:B9
9410     insertRangeData(m_pDoc, ScAddress(0,6,0), aData);
9411 
9412     ScMarkData aMark(m_pDoc->GetSheetLimits());
9413     aMark.SelectOneTable(0);
9414 
9415     // Matrix in C10, summing B7,B9
9416     m_pDoc->InsertMatrixFormula( 2,9, 2,9, aMark, "=SUM(IF(EXACT(A7:A9;A$1);B7:B9;0))");
9417     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C10 failed", 5.0, m_pDoc->GetValue(ScAddress(2,9,0)));
9418 
9419     // Matrix in C11, summing B7,B9
9420     m_pDoc->InsertMatrixFormula( 2,10, 2,10, aMark,
9421             "=SUM(IF(EXACT(OFFSET(A7;0;0):OFFSET(A7;2;0);A$1);OFFSET(A7;0;1):OFFSET(A7;2;1);0))");
9422     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C11 failed", 5.0, m_pDoc->GetValue(ScAddress(2,10,0)));
9423 
9424     m_pDoc->DeleteTab(0);
9425 }
9426 
9427 // tdf#123477 OFFSET() returns the matrix result instead of the reference list
9428 // array if result is not used as ReferenceOrRefArray.
testFuncJumpMatrixArrayOFFSET()9429 void TestFormula::testFuncJumpMatrixArrayOFFSET()
9430 {
9431     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9432     m_pDoc->InsertTab(0, "Test");
9433 
9434     std::vector<std::vector<const char*>> aData = {
9435         { "abc" },
9436         { "bcd" },
9437         { "cde" }
9438     };
9439     insertRangeData(m_pDoc, ScAddress(0,0,0), aData);   // A1:A3
9440 
9441     ScMarkData aMark(m_pDoc->GetSheetLimits());
9442     aMark.SelectOneTable(0);
9443 
9444     // Matrix in C5:C7, COLUMN()-3 here offsets by 0 but the entire expression
9445     // is in array/matrix context.
9446     m_pDoc->InsertMatrixFormula( 2,4, 2,6, aMark, "=FIND(\"c\";OFFSET(A1:A3;0;COLUMN()-3))");
9447     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C5 failed", 3.0, m_pDoc->GetValue(ScAddress(2,4,0)));
9448     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C6 failed", 2.0, m_pDoc->GetValue(ScAddress(2,5,0)));
9449     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C7 failed", 1.0, m_pDoc->GetValue(ScAddress(2,6,0)));
9450 
9451     m_pDoc->DeleteTab(0);
9452 }
9453 
9454 // Test iterations with circular chain of references.
testIterations()9455 void TestFormula::testIterations()
9456 {
9457     ScDocOptions aDocOpts = m_pDoc->GetDocOptions();
9458     aDocOpts.SetIter( true );
9459     m_pDoc->SetDocOptions( aDocOpts );
9460 
9461     m_pDoc->InsertTab(0, "Test");
9462 
9463     m_pDoc->SetValue( 0, 0, 0, 0.01 );         // A1
9464     m_pDoc->SetString( 0, 1, 0, "=A1" );       // A2
9465     m_pDoc->SetString( 0, 2, 0, "=COS(A2)" );  // A3
9466     m_pDoc->CalcAll();
9467 
9468     // Establish reference cycle for the computation of the fixed point of COS() function
9469     m_pDoc->SetString( 0, 0, 0, "=A3" );       // A1
9470     m_pDoc->CalcAll();
9471 
9472     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error", FormulaError::NONE, m_pDoc->GetErrCode( ScAddress( 0, 2, 0) ) );
9473     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate fixed point of cos() failed", 0.7387, m_pDoc->GetValue(0, 2, 0), 1e-4 );
9474 
9475     // Modify the formula
9476     m_pDoc->SetString( 0, 2, 0, "=COS(A2)+0.001" );  // A3
9477     m_pDoc->CalcAll();
9478 
9479     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error after perturbation", FormulaError::NONE, m_pDoc->GetErrCode( ScAddress( 0, 2, 0) ) );
9480     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate perturbed fixed point of cos() failed", 0.7399, m_pDoc->GetValue(0, 2, 0), 1e-4 );
9481 
9482     m_pDoc->DeleteTab(0);
9483 
9484     aDocOpts.SetIter( false );
9485     m_pDoc->SetDocOptions( aDocOpts );
9486 }
9487 
9488 // tdf#111428 CellStoreEvent and its counter used for quick "has a column
9489 // formula cells" must point to the correct column.
testInsertColCellStoreEventSwap()9490 void TestFormula::testInsertColCellStoreEventSwap()
9491 {
9492     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9493     m_pDoc->InsertTab(0, "Test");
9494 
9495     m_pDoc->SetValue(  0,0,0, 1.0 );    // A1
9496     m_pDoc->SetString( 1,0,0, "=A1" );  // B1
9497     // Insert column left of B
9498     m_pDoc->InsertCol( ScRange(1,0,0, 1,m_pDoc->MaxRow(),0));
9499     ScAddress aPos(2,0,0);              // C1, new formula position
9500     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Should be formula cell having value", 1.0, m_pDoc->GetValue(aPos));
9501     // After having swapped in an empty column, editing or adding a formula
9502     // cell has to use the correct store context. To test this,
9503     // ScDocument::SetString() can't be used as it doesn't expose the behavior
9504     // in question, use ScDocFunc::SetFormulaCell() instead which actually is
9505     // also called when editing a cell and creating a formula cell.
9506     ScFormulaCell* pCell = new ScFormulaCell(*m_pDoc, aPos, "=A1+1");
9507     ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
9508     rDocFunc.SetFormulaCell( aPos, pCell, false);   // C1, change formula
9509     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Initial calculation failed", 2.0, m_pDoc->GetValue(aPos));
9510     m_pDoc->SetValue( 0,0,0, 2.0 );     // A1, change value
9511     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Recalculation failed", 3.0, m_pDoc->GetValue(aPos));
9512 
9513     m_pDoc->DeleteTab(0);
9514 }
9515 
testFormulaAfterDeleteRows()9516 void TestFormula::testFormulaAfterDeleteRows()
9517 {
9518     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9519     m_pDoc->InsertTab(0, "Test");
9520 
9521     // Fill A1:A70000 with 1.0
9522     std::vector<double> aVals(70000, 1.0);
9523     m_pDoc->SetValues(ScAddress(0, 0, 0), aVals);
9524     // Set A70001 with formula "=SUM(A1:A70000)"
9525     m_pDoc->SetString(0, 70000, 0, "=SUM(A1:A70000)");
9526 
9527     // Delete rows 2:69998
9528     m_pDoc->DeleteRow(ScRange(0, 1, 0, m_pDoc->MaxCol(), 69997, 0));
9529 
9530     const ScAddress aPos(0, 3, 0);  // A4
9531     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A1:A3)", "Wrong formula in A4.");
9532 
9533     ASSERT_DOUBLES_EQUAL_MESSAGE("Wrong value at A4", 3.0, m_pDoc->GetValue(aPos));
9534 }
9535 
9536 CPPUNIT_TEST_SUITE_REGISTRATION(TestFormula);
9537 
9538 CPPUNIT_PLUGIN_IMPLEMENT();
9539 
9540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
9541