1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include "ucalc.hxx"
11 #include "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 <paramisc.hxx>
24 #include <tokenstringcontext.hxx>
25 #include <refupdatecontext.hxx>
26 #include <dbdata.hxx>
27 #include <scmatrix.hxx>
28 #include <validat.hxx>
29 #include <scitems.hxx>
30 #include <patattr.hxx>
31 #include <docpool.hxx>
32 #include <docoptio.hxx>
33 #include <formulaopt.hxx>
34 #include <externalrefmgr.hxx>
35 #include <svl/itemset.hxx>
36 
37 #include <formula/vectortoken.hxx>
38 #include <svl/broadcast.hxx>
39 #include <svl/intitem.hxx>
40 #include <sfx2/docfile.hxx>
41 
42 #include <memory>
43 #include <functional>
44 #include <set>
45 #include <algorithm>
46 
47 using namespace formula;
48 
49 namespace {
50 
getCachedRange(const ScExternalRefCache::TableTypeRef & pCacheTab)51 ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
52 {
53     ScRange aRange;
54 
55     vector<SCROW> aRows;
56     pCacheTab->getAllRows(aRows);
57     bool bFirst = true;
58     for (const SCROW nRow : aRows)
59     {
60         vector<SCCOL> aCols;
61         pCacheTab->getAllCols(nRow, aCols);
62         for (const SCCOL nCol : aCols)
63         {
64             if (bFirst)
65             {
66                 aRange.aStart = ScAddress(nCol, nRow, 0);
67                 aRange.aEnd = aRange.aStart;
68                 bFirst = false;
69             }
70             else
71             {
72                 if (nCol < aRange.aStart.Col())
73                     aRange.aStart.SetCol(nCol);
74                 else if (aRange.aEnd.Col() < nCol)
75                     aRange.aEnd.SetCol(nCol);
76 
77                 if (nRow < aRange.aStart.Row())
78                     aRange.aStart.SetRow(nRow);
79                 else if (aRange.aEnd.Row() < nRow)
80                     aRange.aEnd.SetRow(nRow);
81             }
82         }
83     }
84     return aRange;
85 }
86 
87 }
88 
testFormulaCreateStringFromTokens()89 void Test::testFormulaCreateStringFromTokens()
90 {
91     // Insert sheets.
92     OUString const aTabName1("Test");
93     OUString const aTabName2("Kevin's Data");
94     OUString const aTabName3("Past Data");
95     OUString const aTabName4("2013");
96     m_pDoc->InsertTab(0, aTabName1);
97     m_pDoc->InsertTab(1, aTabName2);
98     m_pDoc->InsertTab(2, aTabName3);
99     m_pDoc->InsertTab(3, aTabName4);
100 
101     // Insert named ranges.
102     static const struct {
103         bool bGlobal;
104         const char* pName;
105         const char* pExpr;
106     } aNames[] = {
107         { true, "x", "Test.H1" },
108         { true, "y", "Test.H2" },
109         { true, "z", "Test.H3" },
110 
111         { false, "sheetx", "Test.J1" }
112     };
113 
114     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
115     ScRangeName* pSheetNames = m_pDoc->GetRangeName(0);
116     CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
117     CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames);
118 
119     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
120     {
121         ScRangeData* pName = new ScRangeData(
122             m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
123             ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
124 
125         if (aNames[i].bGlobal)
126         {
127             bool bInserted = pGlobalNames->insert(pName);
128             CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
129         }
130         else
131         {
132             bool bInserted = pSheetNames->insert(pName);
133             CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
134         }
135     }
136 
137     // Insert DB ranges.
138     static const struct {
139         const char* pName;
140         SCTAB const nTab;
141         SCCOL const nCol1;
142         SCROW const nRow1;
143         SCCOL const nCol2;
144         SCROW const nRow2;
145     } aDBs[] = {
146         { "Table1", 0, 0, 0, 10, 10 },
147         { "Table2", 1, 0, 0, 10, 10 },
148         { "Table3", 2, 0, 0, 10, 10 }
149     };
150 
151     ScDBCollection* pDBs = m_pDoc->GetDBCollection();
152     CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
153 
154     for (size_t i = 0; i < SAL_N_ELEMENTS(aDBs); ++i)
155     {
156         std::unique_ptr<ScDBData> pData( new ScDBData(
157             OUString::createFromAscii(
158                 aDBs[i].pName), aDBs[i].nTab, aDBs[i].nCol1, aDBs[i].nRow1, aDBs[i].nCol2,aDBs[i].nRow2) );
159         bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
160         CPPUNIT_ASSERT_MESSAGE(
161             OString(
162                 OStringLiteral("Failed to insert \"") + aDBs[i].pName + "\"").getStr(),
163             bInserted);
164     }
165 
166     const char* aTests[] = {
167         "1+2",
168         "SUM(A1:A10;B1:B10;C5;D6)",
169         "IF(Test.B10<>10;\"Good\";\"Bad\")",
170         "AVERAGE('2013'.B10:C20)",
171         "'Kevin''s Data'.B10",
172         "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
173         "x+y*z", // named ranges
174         "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
175         "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
176         "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
177         "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
178     };
179     (void) aTests;
180 
181     std::unique_ptr<ScTokenArray> pArray;
182 
183     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
184 
185     // Artificially add external reference data after the context object is
186     // initialized.
187     aCxt.maExternalFileNames.emplace_back("file:///path/to/fake.file");
188     std::vector<OUString> aExtTabNames;
189     aExtTabNames.emplace_back("Sheet");
190     aCxt.maExternalCachedTabNames.emplace(0, aExtTabNames);
191 
192     ScAddress aPos(0,0,0);
193 
194     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
195     {
196 #if 0
197         OUString aFormula = OUString::createFromAscii(aTests[i]);
198 #endif
199         ScCompiler aComp(m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH);
200 #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
201         pArray.reset(aComp.CompileString(aFormula));
202         CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray.get());
203 
204         OUString aCheck = pArray->CreateString(aCxt, aPos);
205         CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
206 #endif
207     }
208 
209     m_pDoc->DeleteTab(3);
210     m_pDoc->DeleteTab(2);
211     m_pDoc->DeleteTab(1);
212     m_pDoc->DeleteTab(0);
213 }
214 
215 namespace {
216 
isEmpty(const formula::VectorRefArray & rArray,size_t nPos)217 bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
218 {
219     if (rArray.mpStringArray)
220     {
221         if (rArray.mpStringArray[nPos])
222             return false;
223     }
224 
225     if (rArray.mpNumericArray)
226         return rtl::math::isNan(rArray.mpNumericArray[nPos]);
227     else
228         return true;
229 }
230 
equals(const formula::VectorRefArray & rArray,size_t nPos,double fVal)231 bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
232 {
233     if (rArray.mpStringArray && rArray.mpStringArray[nPos])
234         // This is a string cell.
235         return false;
236 
237     return rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal;
238 }
239 
equals(const formula::VectorRefArray & rArray,size_t nPos,const OUString & rVal)240 bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
241 {
242     if (!rArray.mpStringArray)
243         return false;
244 
245     bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
246     if (!bEquals)
247     {
248         cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
249         cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
250     }
251     return bEquals;
252 }
253 
254 }
255 
testFormulaParseReference()256 void Test::testFormulaParseReference()
257 {
258     OUString aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
259     m_pDoc->InsertTab(0, "Dummy"); // just to shift the sheet indices...
260     m_pDoc->InsertTab(1, aTab1); // name with a single quote.
261     m_pDoc->InsertTab(2, aTab2); // name with 2 single quotes.
262     m_pDoc->InsertTab(3, aTab3); // name without single quotes.
263     m_pDoc->InsertTab(4, aTab4); // name that doesn't require to be quoted.
264 
265     OUString aTabName;
266     m_pDoc->GetName(1, aTabName);
267     CPPUNIT_ASSERT_EQUAL(aTab1, aTabName);
268     m_pDoc->GetName(2, aTabName);
269     CPPUNIT_ASSERT_EQUAL(aTab2, aTabName);
270     m_pDoc->GetName(3, aTabName);
271     CPPUNIT_ASSERT_EQUAL(aTab3, aTabName);
272     m_pDoc->GetName(4, aTabName);
273     CPPUNIT_ASSERT_EQUAL(aTab4, aTabName);
274 
275     // Make sure the formula input and output match.
276     {
277         const char* aChecks[] = {
278             "'90''s Music'.B12",
279             "'90''s and 70''s'.$AB$100",
280             "'All Others'.Z$100",
281             "NoQuote.$C111"
282         };
283 
284         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
285         {
286             // Use the 'Dummy' sheet for this.
287             OUString aInput = "=" + OUString::createFromAscii(aChecks[i]);
288             m_pDoc->SetString(ScAddress(0,0,0), aInput);
289             ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), aChecks[i], "Wrong formula");
290         }
291     }
292 
293     ScAddress aPos;
294     ScAddress::ExternalInfo aExtInfo;
295     ScRefFlags nRes = aPos.Parse("'90''s Music'.D10", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
296     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
297     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(1), aPos.Tab());
298     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), aPos.Col());
299     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), aPos.Row());
300     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
301 
302     nRes = aPos.Parse("'90''s and 70''s'.C100", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
303     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
304     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(2), aPos.Tab());
305     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aPos.Col());
306     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(99), aPos.Row());
307     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
308 
309     nRes = aPos.Parse("'All Others'.B3", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
310     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
311     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(3), aPos.Tab());
312     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aPos.Col());
313     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), aPos.Row());
314     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
315 
316     nRes = aPos.Parse("NoQuote.E13", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
317     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
318     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aPos.Tab());
319     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(4), aPos.Col());
320     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), aPos.Row());
321     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
322 
323     ScRange aRange;
324 
325     aRange.aStart.SetTab(0);
326     nRes = aRange.Parse(":B", m_pDoc, formula::FormulaGrammar::CONV_OOO);
327     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
328 
329     aRange.aStart.SetTab(0);
330     nRes = aRange.Parse("B:", m_pDoc, formula::FormulaGrammar::CONV_OOO);
331     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
332 
333     aRange.aStart.SetTab(0);
334     nRes = aRange.Parse(":B2", m_pDoc, formula::FormulaGrammar::CONV_OOO);
335     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
336 
337     aRange.aStart.SetTab(0);
338     nRes = aRange.Parse("B2:", m_pDoc, formula::FormulaGrammar::CONV_OOO);
339     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
340 
341     aRange.aStart.SetTab(0);
342     nRes = aRange.Parse(":2", m_pDoc, formula::FormulaGrammar::CONV_OOO);
343     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
344 
345     aRange.aStart.SetTab(0);
346     nRes = aRange.Parse("2:", m_pDoc, formula::FormulaGrammar::CONV_OOO);
347     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
348 
349     aRange.aStart.SetTab(0);
350     nRes = aRange.Parse(":2B", m_pDoc, formula::FormulaGrammar::CONV_OOO);
351     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
352 
353     aRange.aStart.SetTab(0);
354     nRes = aRange.Parse("2B:", m_pDoc, formula::FormulaGrammar::CONV_OOO);
355     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
356 
357     aRange.aStart.SetTab(0);
358     nRes = aRange.Parse("abc_foo:abc_bar", m_pDoc, formula::FormulaGrammar::CONV_OOO);
359     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
360 
361     aRange.aStart.SetTab(0);
362     nRes = aRange.Parse("B1:B2~C1", m_pDoc, formula::FormulaGrammar::CONV_OOO);
363     CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
364 
365     aRange.aStart.SetTab(0);
366     nRes = aRange.Parse("B:B", m_pDoc, formula::FormulaGrammar::CONV_OOO);
367     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
368     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
369     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
370     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
371     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
372     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
373     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
374     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
375                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
376                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
377                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
378     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
379                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
380     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
381                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
382 
383     aRange.aStart.SetTab(0);
384     nRes = aRange.Parse("2:2", m_pDoc, formula::FormulaGrammar::CONV_OOO);
385     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
386     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
387     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
388     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
389     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
390     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
391     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
392     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
393                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
394                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
395                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
396     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
397                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
398     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
399                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
400 
401     nRes = aRange.Parse("NoQuote.B:C", m_pDoc, formula::FormulaGrammar::CONV_OOO);
402     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
403     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aRange.aStart.Tab());
404     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
405     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
406     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aRange.aEnd.Tab());
407     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aRange.aEnd.Col());
408     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
409     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
410                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
411                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
412                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
413     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
414                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
415     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
416                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
417 
418     // Both rows at sheet bounds and relative => convert to absolute => entire column reference.
419     aRange.aStart.SetTab(0);
420     nRes = aRange.Parse("B1:B1048576", m_pDoc, formula::FormulaGrammar::CONV_OOO);
421     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
422     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
423     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
424     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
425     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
426     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
427     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
428     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
429                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
430                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
431                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
432     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
433                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
434     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
435                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
436 
437     // Both columns at sheet bounds and relative => convert to absolute => entire row reference.
438     aRange.aStart.SetTab(0);
439     nRes = aRange.Parse("A2:AMJ2", m_pDoc, formula::FormulaGrammar::CONV_OOO);
440     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
441     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
442     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
443     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
444     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
445     CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
446     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
447     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
448                                                  ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
449                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
450                                                          ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
451     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
452                          static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
453     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
454                          static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
455 
456     // Check for reference input conversion to and display string of entire column/row.
457     {
458         const char* aChecks[][2] = {
459             { "=B:B",           "B:B" },
460             { "=B1:B1048576",   "B:B" },
461             { "=B1:B$1048576",  "B1:B$1048576" },
462             { "=B$1:B1048576",  "B$1:B1048576" },
463             { "=B$1:B$1048576", "B:B" },
464             { "=2:2",           "2:2" },
465             { "=A2:AMJ2",       "2:2" },
466             { "=A2:$AMJ2",      "A2:$AMJ2" },
467             { "=$A2:AMJ2",      "$A2:AMJ2" },
468             { "=$A2:$AMJ2",     "2:2" }
469         };
470 
471         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
472         {
473             // Use the 'Dummy' sheet for this.
474             m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(aChecks[i][0]));
475             ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), aChecks[i][1], "Wrong formula");
476         }
477     }
478 
479     m_pDoc->DeleteTab(4);
480     m_pDoc->DeleteTab(3);
481     m_pDoc->DeleteTab(2);
482     m_pDoc->DeleteTab(1);
483     m_pDoc->DeleteTab(0);
484 }
485 
testFetchVectorRefArray()486 void Test::testFetchVectorRefArray()
487 {
488     m_pDoc->InsertTab(0, "Test");
489 
490     // All numeric cells in Column A.
491     m_pDoc->SetValue(ScAddress(0,0,0), 1);
492     m_pDoc->SetValue(ScAddress(0,1,0), 2);
493     m_pDoc->SetValue(ScAddress(0,2,0), 3);
494     m_pDoc->SetValue(ScAddress(0,3,0), 4);
495 
496     formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 4);
497     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
498     CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
499     CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
500     CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
501     CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
502     CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
503 
504     aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 5);
505     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
506     CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
507     CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
508     CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
509     CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
510     CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
511     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
512 
513     // All string cells in Column B.  Note that the fetched string arrays are
514     // only to be compared case-insensitively.  Right now, we use upper cased
515     // strings to achieve case-insensitive-ness, but that may change. So,
516     // don't count on that.
517     m_pDoc->SetString(ScAddress(1,0,0), "Andy");
518     m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
519     m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
520     m_pDoc->SetString(ScAddress(1,3,0), "David");
521     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 5);
522     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
523     CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
524     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Andy"));
525     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Bruce"));
526     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "Charlie"));
527     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "David"));
528     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
529 
530     // Mixture of numeric, string, and empty cells in Column C.
531     m_pDoc->SetString(ScAddress(2,0,0), "Header");
532     m_pDoc->SetValue(ScAddress(2,1,0), 11);
533     m_pDoc->SetValue(ScAddress(2,2,0), 12);
534     m_pDoc->SetValue(ScAddress(2,3,0), 13);
535     m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
536     m_pDoc->CalcAll();
537 
538     aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,0,0), 7);
539     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
540     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
541     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Header"));
542     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 11));
543     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 12));
544     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 13));
545     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
546     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 36));
547     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
548 
549     // Mixed type again in Column D, but it starts with a numeric cell.
550     m_pDoc->SetValue(ScAddress(3,0,0), 10);
551     m_pDoc->SetString(ScAddress(3,1,0), "Below 10");
552     // Leave 2 empty cells.
553     m_pDoc->SetValue(ScAddress(3,4,0), 11);
554     m_pDoc->SetString(ScAddress(3,5,0), "=12");
555     m_pDoc->SetString(ScAddress(3,6,0), "=13");
556     m_pDoc->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
557     m_pDoc->CalcAll();
558 
559     aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,0,0), 8);
560     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
561     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
562     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 10));
563     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Below 10"));
564     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 2));
565     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
566     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 11));
567     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 12));
568     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13));
569     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC"));
570 
571     // Column E consists of formula cells whose results are all numeric.
572     for (SCROW i = 0; i <= 6; ++i)
573         m_pDoc->SetString(ScAddress(4,i,0), "=ROW()");
574     m_pDoc->CalcAll();
575 
576     // Leave row 7 empty.
577     m_pDoc->SetString(ScAddress(4,8,0), "Andy");
578     m_pDoc->SetValue(ScAddress(4,9,0), 123);
579 
580     // This array fits within a single formula block.
581     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 5);
582     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
583     CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray && !aArray.mpStringArray);
584     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
585     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
586     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
587     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
588     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
589 
590     // This array spans over multiple blocks.
591     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 11);
592     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
593     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
594     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
595     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
596     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
597     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
598     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
599     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6));
600     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7));
601     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
602     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy"));
603     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123));
604     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10));
605 
606     // Hit the cache but at a different start row.
607     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 3);
608     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
609     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
610     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3));
611     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4));
612     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5));
613 
614     // Column F begins with empty rows at the top.
615     m_pDoc->SetValue(ScAddress(5,2,0), 1.1);
616     m_pDoc->SetValue(ScAddress(5,3,0), 1.2);
617     m_pDoc->SetString(ScAddress(5,4,0), "=2*8");
618     m_pDoc->CalcAll();
619 
620     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 4);
621     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
622     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
623     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1));
624     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2));
625     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16));
626     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
627 
628     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 3);
629     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
630     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
631     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
632     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
633     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
634 
635     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 10);
636     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
637     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
638     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
639     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
640     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
641     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2));
642     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16));
643     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5));
644     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
645     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
646     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8));
647     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9));
648 
649     // Get the array for F3:F4. This array should only consist of numeric array.
650     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 3);
651     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
652     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
653     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
654 
655     // Column G consists only of strings.
656     m_pDoc->SetString(ScAddress(6,0,0), "Title");
657     m_pDoc->SetString(ScAddress(6,1,0), "foo");
658     m_pDoc->SetString(ScAddress(6,2,0), "bar");
659     m_pDoc->SetString(ScAddress(6,3,0), "foo");
660     m_pDoc->SetString(ScAddress(6,4,0), "baz");
661     m_pDoc->SetString(ScAddress(6,5,0), "quack");
662     m_pDoc->SetString(ScAddress(6,6,0), "beep");
663     m_pDoc->SetString(ScAddress(6,7,0), "kerker");
664 
665     aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
666     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
667     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
668     CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
669     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "foo"));
670     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "bar"));
671     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "foo"));
672     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "baz"));
673 
674     aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
675     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
676     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
677     CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
678     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "bar"));
679     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "foo"));
680     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "baz"));
681     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "quack"));
682 
683     // Column H starts with formula cells.
684     for (SCROW i = 0; i < 10; ++i)
685         m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
686 
687     m_pDoc->CalcAll();
688     aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
689     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
690     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
691     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
692     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 4.0));
693     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, 5.0));
694     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, 6.0));
695 
696     aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
697     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
698     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
699     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
700     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 5.0));
701 
702     // Clear everything and start over.
703     clearRange(m_pDoc, ScRange(0,0,0,m_pDoc->MaxCol(),m_pDoc->MaxRow(),0));
704     m_pDoc->PrepareFormulaCalc();
705 
706     // Totally empty range in a totally empty column (Column A).
707     aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
708     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
709     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
710     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
711     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
712     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
713 
714     // Totally empty range in a non-empty column (Column B).
715     m_pDoc->SetString(ScAddress(1,10,0), "Some text"); // B11
716     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
717     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
718     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
719     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
720     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
721     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
722 
723     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
724     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
725     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
726     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
727     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
728     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
729 
730     // These values come from a cache because of the call above.
731     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
732     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
733     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
734     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
735     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
736     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
737 
738     // The column begins with a string header at row 1 (Column C).
739     m_pDoc->SetString(ScAddress(2,0,0), "MyHeader");
740     for (SCROW i = 1; i <= 9; ++i) // rows 2-10 are numeric.
741         m_pDoc->SetValue(ScAddress(2,i,0), i);
742 
743     aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,1,0), 9); // C2:C10
744     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
745     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
746     for (size_t i = 0; i < 9; ++i)
747         CPPUNIT_ASSERT_EQUAL(double(i+1), aArray.mpNumericArray[i]);
748 
749     // The column begins with a number, followed by a string then followed by
750     // a block of numbers (Column D).
751     m_pDoc->SetValue(ScAddress(3,0,0), 0.0);
752     m_pDoc->SetString(ScAddress(3,1,0), "Some string");
753     for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
754         m_pDoc->SetValue(ScAddress(3,i,0), i);
755 
756     aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,2,0), 8); // D3:D10
757     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
758     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
759     for (size_t i = 0; i < 8; ++i)
760         CPPUNIT_ASSERT_EQUAL(double(i+2), aArray.mpNumericArray[i]);
761 
762     // The column begins with a formula, followed by a string then followed by
763     // a block of numbers (Column E).
764     m_pDoc->SetString(ScAddress(4,0,0), "=1*2");
765     m_pDoc->SetString(ScAddress(4,1,0), "Some string");
766     for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
767         m_pDoc->SetValue(ScAddress(4,i,0), i*2);
768 
769     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 8); // E3:E10
770     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
771     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
772     for (size_t i = 0; i < 8; ++i)
773         CPPUNIT_ASSERT_EQUAL(double((i+2)*2), aArray.mpNumericArray[i]);
774 
775     m_pDoc->DeleteTab(0);
776 }
777 
testGroupConverter3D()778 void Test::testGroupConverter3D()
779 {
780     m_pDoc->InsertTab(0, "Test");
781     m_pDoc->InsertTab(1, "Test2");
782 
783     m_pDoc->SetValue(1, 0, 0, 1.0);
784     m_pDoc->SetValue(1, 0, 1, 2.0);
785 
786     for (SCROW nRow = 0; nRow < 200; ++nRow)
787     {
788         OUString aFormula = "=SUM(Test.B" + OUString::number(nRow+1) + ":Test2.B" + OUString::number(nRow+1) + ")";
789         m_pDoc->SetString(0, nRow, 0, aFormula);
790     }
791 
792     double nVal = m_pDoc->GetValue(0, 0, 0);
793     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
794 
795     m_pDoc->DeleteTab(1);
796     m_pDoc->DeleteTab(0);
797 }
798 
testFormulaHashAndTag()799 void Test::testFormulaHashAndTag()
800 {
801     m_pDoc->InsertTab(0, "Test");
802 
803     ScAddress aPos1(0,0,0), aPos2(1,0,0);
804 
805     // Test formula hashing.
806 
807     static const struct {
808         const char* pFormula1; const char* pFormula2; bool bEqual;
809     } aHashTests[] = {
810         { "=1", "=2", false }, // different constants
811         { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
812         { "=C2*3", "=D2*3", true },  // relative references
813         { "=C2*3", "=D2*4", false }, // different constants
814         { "=C2*4", "=D2*4", true },  // relative references
815         { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
816         { "=$C3/2", "=$C3/2", true }, // absolute column references
817         { "=C$3/2", "=D$3/2", true }, // absolute row references
818         { "=$E$30/2", "=$E$30/2", true }, // absolute references
819         { "=X20", "=$X$20", false }, // absolute vs relative
820         { "=X20", "=X$20", false }, // absolute vs relative
821         { "=X20", "=$X20", false }, // absolute vs relative
822         { "=X$20", "=$X20", false }, // column absolute vs row absolute
823         // similar enough for merging ...
824         { "=A1", "=B1", true },
825         { "=$A$1", "=$B$1", true },
826         { "=A1", "=C2", true },
827         { "=SUM(A1)", "=SUM(B1)", true },
828         { "=A1+3", "=B1+3", true },
829         { "=A1+7", "=B1+42", false },
830     };
831 
832     for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
833     {
834         m_pDoc->SetString(aPos1, OUString::createFromAscii(aHashTests[i].pFormula1));
835         m_pDoc->SetString(aPos2, OUString::createFromAscii(aHashTests[i].pFormula2));
836         size_t nHashVal1 = m_pDoc->GetFormulaHash(aPos1);
837         size_t nHashVal2 = m_pDoc->GetFormulaHash(aPos2);
838 
839         std::ostringstream os;
840         os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
841         if (aHashTests[i].bEqual)
842         {
843             os << " Error: these hashes should be equal." << endl;
844             CPPUNIT_ASSERT_EQUAL_MESSAGE(os.str(), nHashVal1, nHashVal2);
845         }
846         else
847         {
848             os << " Error: these hashes should differ." << endl;
849             CPPUNIT_ASSERT_MESSAGE(os.str(), nHashVal1 != nHashVal2);
850         }
851 
852         aPos1.IncRow();
853         aPos2.IncRow();
854     }
855 
856     // Go back to row 1.
857     aPos1.SetRow(0);
858     aPos2.SetRow(0);
859 
860     // Test formula vectorization state.
861 
862     static const struct {
863         const char* pFormula;
864         ScFormulaVectorState const eState;
865     } aVectorTests[] = {
866         { "=SUM(1;2;3;4;5)", FormulaVectorEnabled },
867         { "=NOW()", FormulaVectorDisabled },
868         { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference },
869         { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference },
870         { "=MIN(10;11;22)", FormulaVectorEnabled },
871         { "=H4", FormulaVectorCheckReference },
872     };
873 
874     for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
875     {
876         m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
877         ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
878         ScFormulaVectorState eReferenceState = aVectorTests[i].eState;
879 
880         if (eState != eReferenceState)
881         {
882             std::ostringstream os;
883             os << "Unexpected vectorization state: expr: '" << aVectorTests[i].pFormula << "'";
884             CPPUNIT_ASSERT_MESSAGE(os.str(), false);
885         }
886         aPos1.IncRow();
887     }
888 
889     m_pDoc->DeleteTab(0);
890 }
891 
testFormulaTokenEquality()892 void Test::testFormulaTokenEquality()
893 {
894     struct FormulaTokenEqualityTest
895     {
896         const char* mpFormula1;
897         const char* mpFormula2;
898         bool const mbEqual;
899     };
900 
901     static const FormulaTokenEqualityTest aTests[] = {
902         { "R1C2", "R1C2", true },
903         { "R1C2", "R1C3", false },
904         { "R1C2", "R2C2", false },
905         { "RC2",  "RC[1]", false },
906         { "R1C2:R10C2", "R1C2:R10C2", true },
907         { "R1C2:R10C2", "R1C2:R11C2", false },
908         { "1", "2", false },
909         { "RC[1]+1.2", "RC[1]+1.2", true },
910         { "RC[1]*0.2", "RC[1]*0.5", false },
911         { "\"Test1\"", "\"Test2\"", false },
912         { "\"Test\"", "\"Test\"", true },
913         { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
914         { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
915     };
916 
917     formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
918     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
919     {
920         ScFormulaCell aCell1(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
921         ScFormulaCell aCell2(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
922 
923         ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
924         if (aTests[i].mbEqual)
925         {
926             if (eComp == ScFormulaCell::NotEqual)
927             {
928                 std::ostringstream os;
929                 os << "These two formulas should be evaluated equal: '"
930                     << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
931                 CPPUNIT_FAIL(os.str());
932             }
933         }
934         else
935         {
936             if (eComp != ScFormulaCell::NotEqual)
937             {
938                 std::ostringstream os;
939                 os << "These two formulas should be evaluated non-equal: '"
940                     << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
941                 CPPUNIT_FAIL(os.str());
942             }
943         }
944     }
945 }
946 
testFormulaRefData()947 void Test::testFormulaRefData()
948 {
949     ScAddress aAddr(4,5,3), aPos(2,2,2);
950     ScSingleRefData aRef;
951     aRef.InitAddress(aAddr);
952     CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsRowRel() && !aRef.IsColRel() && !aRef.IsTabRel());
953     CPPUNIT_ASSERT_EQUAL(SCCOL(4), aRef.Col());
954     CPPUNIT_ASSERT_EQUAL(SCROW(5), aRef.Row());
955     CPPUNIT_ASSERT_EQUAL(SCTAB(3), aRef.Tab());
956 
957     aRef.SetRowRel(true);
958     aRef.SetColRel(true);
959     aRef.SetTabRel(true);
960     aRef.SetAddress(aAddr, aPos);
961     CPPUNIT_ASSERT_EQUAL(SCCOL(2), aRef.Col());
962     CPPUNIT_ASSERT_EQUAL(SCROW(3), aRef.Row());
963     CPPUNIT_ASSERT_EQUAL(SCTAB(1), aRef.Tab());
964 
965     // Test extension of range reference.
966 
967     ScComplexRefData aDoubleRef;
968     aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
969 
970     aRef.InitAddress(ScAddress(6,5,0));
971 
972     aDoubleRef.Extend(aRef, ScAddress());
973     ScRange aTest = aDoubleRef.toAbs(ScAddress());
974     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(2,2,0), aTest.aStart);
975     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(6,5,0), aTest.aEnd);
976 
977     ScComplexRefData aDoubleRef2;
978     aDoubleRef2.InitRangeRel(ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
979     aDoubleRef.Extend(aDoubleRef2, ScAddress(5,5,0));
980     aTest = aDoubleRef.toAbs(ScAddress(5,5,0));
981 
982     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(1,2,0), aTest.aStart);
983     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(8,6,0), aTest.aEnd);
984 }
985 
testFormulaCompiler()986 void Test::testFormulaCompiler()
987 {
988     static const struct {
989         const char* pInput; FormulaGrammar::Grammar eInputGram;
990         const char* pOutput; FormulaGrammar::Grammar eOutputGram;
991     } aTests[] = {
992         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF },
993         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE },
994         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1 },
995         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1 },
996     };
997 
998     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
999     {
1000         std::unique_ptr<ScTokenArray> pArray = compileFormula(m_pDoc, OUString::createFromAscii(aTests[i].pInput), aTests[i].eInputGram);
1001         CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray);
1002 
1003         OUString aFormula = toString(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
1004         CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
1005     }
1006 }
1007 
testFormulaCompilerJumpReordering()1008 void Test::testFormulaCompilerJumpReordering()
1009 {
1010     struct TokenCheck
1011     {
1012         OpCode const meOp;
1013         StackVar const meType;
1014     };
1015 
1016     // Set separators first.
1017     ScFormulaOptions aOptions;
1018     aOptions.SetFormulaSepArg(";");
1019     aOptions.SetFormulaSepArrayCol(";");
1020     aOptions.SetFormulaSepArrayRow("|");
1021     getDocShell().SetFormulaOptions(aOptions);
1022 
1023     {
1024         OUString const aInput("=IF(B1;12;\"text\")");
1025 
1026         // Compile formula string first.
1027         std::unique_ptr<ScTokenArray> pCode(compileFormula(m_pDoc, aInput));
1028         CPPUNIT_ASSERT(pCode);
1029 
1030         // Then generate RPN tokens.
1031         ScCompiler aCompRPN(m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1032         aCompRPN.CompileTokenArray();
1033 
1034         // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
1035         static const TokenCheck aCheckRPN[] =
1036         {
1037             { ocPush,  svSingleRef },
1038             { ocIf,    svUnknown   }, // type is context dependent, don't test it
1039             { ocPush,  svDouble    },
1040             { ocSep,   svSep       },
1041             { ocPush,  svString    },
1042             { ocClose, svSep       },
1043         };
1044 
1045         sal_uInt16 nLen = pCode->GetCodeLen();
1046         CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN)), nLen);
1047 
1048         FormulaToken** ppTokens = pCode->GetCode();
1049         for (sal_uInt16 i = 0; i < nLen; ++i)
1050         {
1051             const FormulaToken* p = ppTokens[i];
1052             CPPUNIT_ASSERT_EQUAL(aCheckRPN[i].meOp, p->GetOpCode());
1053             if (aCheckRPN[i].meOp != ocIf )
1054                 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN[i].meType), static_cast<int>(p->GetType()));
1055         }
1056 
1057         // Generate RPN tokens again, but this time no jump command reordering.
1058         pCode->DelRPN();
1059         ScCompiler aCompRPN2(m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1060         aCompRPN2.EnableJumpCommandReorder(false);
1061         aCompRPN2.CompileTokenArray();
1062 
1063         static const TokenCheck aCheckRPN2[] =
1064         {
1065             { ocPush,  svSingleRef },
1066             { ocPush,  svDouble    },
1067             { ocPush,  svString    },
1068             { ocIf,    svUnknown   }, // type is context dependent, don't test it
1069         };
1070 
1071         nLen = pCode->GetCodeLen();
1072         CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN2)), nLen);
1073         ppTokens = pCode->GetCode();
1074         for (sal_uInt16 i = 0; i < nLen; ++i)
1075         {
1076             const FormulaToken* p = ppTokens[i];
1077             CPPUNIT_ASSERT_EQUAL(aCheckRPN2[i].meOp, p->GetOpCode());
1078             if (aCheckRPN[i].meOp == ocPush)
1079                 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN2[i].meType), static_cast<int>(p->GetType()));
1080         }
1081     }
1082 }
1083 
testFormulaCompilerImplicitIntersection2Param()1084 void Test::testFormulaCompilerImplicitIntersection2Param()
1085 {
1086     struct TestCaseFormula
1087     {
1088         OUString const  aFormula;
1089         ScAddress const aCellAddress;
1090         ScRange const   aSumRange;
1091         bool const      bStartColRel;  // SumRange-StartCol
1092         bool const      bEndColRel;    // SumRange-EndCol
1093     };
1094 
1095     m_pDoc->InsertTab(0, "Formula");
1096     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1097 
1098     {
1099         TestCaseFormula aTestCases[] =
1100         {
1101             // Formula, FormulaCellAddress, SumRange with Implicit Intersection
1102 
1103             // Sumrange is single cell, address is abs
1104             {
1105                 OUString("=SUMIF($B$2:$B$10;F2;$D$5)"),
1106                 ScAddress(7, 5, 0),
1107                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1108                 false,
1109                 false
1110             },
1111 
1112             // Sumrange is single cell, address is relative
1113             {
1114                 OUString("=SUMIF($B$2:$B$10;F2;D5)"),
1115                 ScAddress(7, 5, 0),
1116                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1117                 true,
1118                 true
1119             },
1120 
1121             // Baserange(abs,abs), Sumrange(abs,abs)
1122             {
1123                 OUString("=SUMIF($B$2:$B$10;F2;$D$5:$D$10)"),
1124                 ScAddress(7, 5, 0),
1125                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1126                 false,
1127                 false
1128             },
1129 
1130             // Baserange(abs,rel), Sumrange(abs,abs)
1131             {
1132                 OUString("=SUMIF($B$2:B10;F2;$D$5:$D$10)"),
1133                 ScAddress(7, 5, 0),
1134                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1135                 false,
1136                 false
1137             },
1138 
1139             // Baserange(rel,abs), Sumrange(abs,abs)
1140             {
1141                 OUString("=SUMIF(B2:$B$10;F2;$D$5:$D$10)"),
1142                 ScAddress(7, 5, 0),
1143                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1144                 false,
1145                 false
1146             },
1147 
1148             // Baserange(rel,rel), Sumrange(abs,abs)
1149             {
1150                 OUString("=SUMIF(B2:B10;F2;$D$5:$D$10)"),
1151                 ScAddress(7, 5, 0),
1152                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1153                 false,
1154                 false
1155             },
1156 
1157             // Baserange(abs,abs), Sumrange(abs,rel)
1158             {
1159                 OUString("=SUMIF($B$2:$B$10;F2;$D$5:D10)"),
1160                 ScAddress(7, 5, 0),
1161                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1162                 false,
1163                 true
1164             },
1165 
1166             // Baserange(abs,abs), Sumrange(rel,abs)
1167             {
1168                 OUString("=SUMIF($B$2:$B$10;F2;D5:$D$10)"),
1169                 ScAddress(7, 5, 0),
1170                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1171                 true,
1172                 false
1173             },
1174 
1175             // Baserange(abs,abs), Sumrange(rel,rel)
1176             {
1177                 OUString("=SUMIF($B$2:$B$10;F2;D5:D10)"),
1178                 ScAddress(7, 5, 0),
1179                 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1180                 true,
1181                 true
1182             }
1183         };
1184 
1185         for (const auto& rCase : aTestCases)
1186         {
1187             m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1188             const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1189             const ScTokenArray* pCode = pCell->GetCode();
1190             CPPUNIT_ASSERT(pCode);
1191 
1192             sal_uInt16 nLen = pCode->GetCodeLen();
1193             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(4), nLen);
1194 
1195             FormulaToken** ppTokens = pCode->GetCode();
1196 
1197             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(first argument to SUMIF)", svDoubleRef, ppTokens[0]->GetType());
1198             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(third argument to SUMIF)", svDoubleRef, ppTokens[2]->GetType());
1199 
1200             ScComplexRefData aSumRangeData = *ppTokens[2]->GetDoubleRef();
1201             ScRange aSumRange = aSumRangeData.toAbs(rCase.aCellAddress);
1202             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sum-range in RPN array", rCase.aSumRange, aSumRange);
1203 
1204             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for start column address in sum-range", rCase.bStartColRel, aSumRangeData.Ref1.IsColRel());
1205             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for end column address in sum-range", rCase.bEndColRel, aSumRangeData.Ref2.IsColRel());
1206         }
1207     }
1208 }
1209 
testFormulaCompilerImplicitIntersection1ParamNoChange()1210 void Test::testFormulaCompilerImplicitIntersection1ParamNoChange()
1211 {
1212     struct TestCaseFormulaNoChange
1213     {
1214         OUString const  aFormula;
1215         ScAddress const aCellAddress;
1216         bool const      bMatrixFormula;
1217         bool const      bForcedArray;
1218     };
1219 
1220     m_pDoc->InsertTab(0, "Formula");
1221     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1222 
1223     {
1224         ScAddress aStartAddr(4, 5, 0);
1225         TestCaseFormulaNoChange aCasesNoChange[] =
1226         {
1227             {
1228                 OUString("=COS(A$2:A$100)"),  // No change because of abs col ref.
1229                 aStartAddr,
1230                 false,
1231                 false
1232             },
1233             {
1234                 OUString("=COS($A7:$A100)"),  // No intersection
1235                 aStartAddr,
1236                 false,
1237                 false
1238             },
1239             {
1240                 OUString("=COS($A5:$C7)"),   // No intersection 2-D range
1241                 aStartAddr,
1242                 false,
1243                 false
1244             },
1245             {
1246                 OUString("=SUMPRODUCT(COS(A6:A10))"),  // COS() in forced array mode
1247                 aStartAddr,
1248                 false,
1249                 true
1250             },
1251             {
1252                 OUString("=COS(A6:A10)"),  // Matrix formula
1253                 aStartAddr,
1254                 true,
1255                 false
1256             }
1257         };
1258 
1259         for (const auto& rCase : aCasesNoChange)
1260         {
1261             if (rCase.bMatrixFormula)
1262             {
1263                 ScMarkData aMark(MAXROW, MAXCOL);
1264                 aMark.SelectOneTable(0);
1265                 SCCOL nColStart = rCase.aCellAddress.Col();
1266                 SCROW nRowStart = rCase.aCellAddress.Row();
1267                 m_pDoc->InsertMatrixFormula(nColStart, nRowStart, nColStart, nRowStart + 4,
1268                                             aMark, rCase.aFormula);
1269             }
1270             else
1271                 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1272 
1273             const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1274             const ScTokenArray* pCode = pCell->GetCode();
1275             CPPUNIT_ASSERT(pCode);
1276 
1277             sal_uInt16 nRPNLen = pCode->GetCodeLen();
1278             sal_uInt16 nRawLen = pCode->GetLen();
1279             sal_uInt16 nRawArgPos;
1280             if (rCase.bForcedArray)
1281             {
1282                 nRawArgPos = 4;
1283                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(7), nRawLen);
1284                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(3), nRPNLen);
1285             }
1286             else
1287             {
1288                 nRawArgPos = 2;
1289                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(4), nRawLen);
1290                 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1291             }
1292 
1293             FormulaToken** ppRawTokens = pCode->GetArray();
1294             FormulaToken** ppRPNTokens = pCode->GetCode();
1295 
1296             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of raw token(argument to COS)", svDoubleRef, ppRawTokens[nRawArgPos]->GetType());
1297             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svDoubleRef, ppRPNTokens[0]->GetType());
1298 
1299             ScComplexRefData aArgRangeRaw = *ppRawTokens[nRawArgPos]->GetDoubleRef();
1300             ScComplexRefData aArgRangeRPN = *ppRPNTokens[0]->GetDoubleRef();
1301             bool bRawMatchRPNToken(aArgRangeRaw == aArgRangeRPN);
1302             CPPUNIT_ASSERT_MESSAGE("raw arg token and RPN arg token contents do not match", bRawMatchRPNToken);
1303         }
1304     }
1305 }
1306 
testFormulaCompilerImplicitIntersection1ParamWithChange()1307 void Test::testFormulaCompilerImplicitIntersection1ParamWithChange()
1308 {
1309     struct TestCaseFormula
1310     {
1311         OUString const  aFormula;
1312         ScAddress const aCellAddress;
1313         ScAddress const aArgAddr;
1314     };
1315 
1316     m_pDoc->InsertTab(0, "Formula");
1317     m_pDoc->InsertTab(1, "Formula1");
1318     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1319 
1320     {
1321         ScAddress aStartAddr(10, 5, 0);
1322         TestCaseFormula aCasesWithChange[] =
1323         {
1324             {
1325                 OUString("=COS($A6:$A100)"),  // Corner case with intersection
1326                 aStartAddr,
1327                 ScAddress(0, 5, 0)
1328             },
1329             {
1330                 OUString("=COS($A2:$A6)"),    // Corner case with intersection
1331                 aStartAddr,
1332                 ScAddress(0, 5, 0)
1333             },
1334             {
1335                 OUString("=COS($A2:$A100)"),    // Typical 1D case
1336                 aStartAddr,
1337                 ScAddress(0, 5, 0)
1338             },
1339             {
1340                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1341                 ScAddress(0, 0, 1),                      // Formula in sheet 1
1342                 ScAddress(0, 0, 0)
1343             },
1344             {
1345                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1346                 ScAddress(0, 2, 1),                      // Formula in sheet 1
1347                 ScAddress(0, 2, 0)
1348             },
1349             {
1350                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1351                 ScAddress(2, 0, 1),                      // Formula in sheet 1
1352                 ScAddress(2, 0, 0)
1353             },
1354             {
1355                 OUString("=COS($Formula.$A1:$C3)"),      // 2D corner case
1356                 ScAddress(2, 2, 1),                      // Formula in sheet 1
1357                 ScAddress(2, 2, 0)
1358             },
1359             {
1360                 OUString("=COS($Formula.$A1:$C3)"),      // Typical 2D case
1361                 ScAddress(1, 1, 1),                      // Formula in sheet 1
1362                 ScAddress(1, 1, 0)
1363             }
1364         };
1365 
1366         for (const auto& rCase : aCasesWithChange)
1367         {
1368             m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1369 
1370             const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1371             const ScTokenArray* pCode = pCell->GetCode();
1372             CPPUNIT_ASSERT(pCode);
1373 
1374             sal_uInt16 nRPNLen = pCode->GetCodeLen();
1375             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1376 
1377             FormulaToken** ppRPNTokens = pCode->GetCode();
1378 
1379             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef, ppRPNTokens[0]->GetType());
1380 
1381             ScSingleRefData aArgAddrRPN = *ppRPNTokens[0]->GetSingleRef();
1382             ScAddress aArgAddrActual = aArgAddrRPN.toAbs(rCase.aCellAddress);
1383             CPPUNIT_ASSERT_EQUAL_MESSAGE("Computed implicit intersection singleref is wrong", rCase.aArgAddr, aArgAddrActual);
1384         }
1385     }
1386 }
1387 
testFormulaCompilerImplicitIntersection1NoGroup()1388 void Test::testFormulaCompilerImplicitIntersection1NoGroup()
1389 {
1390     m_pDoc->InsertTab(0, "Formula");
1391     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1392 
1393     m_pDoc->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
1394     m_pDoc->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
1395 
1396     // Implicit intersection optimization in ScCompiler::HandleIIOpCode() internally changes
1397     // these to "=COS(A3)" and "=COS(A4)", but these shouldn't be merged into a formula group,
1398     // otherwise B4's formula would then be "=COS(A2:A6)".
1399     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "COS(A1:A5)", "Formula in B3 has changed.");
1400     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,3,0), "COS(A1:A5)", "Formula in B4 has changed.");
1401 
1402     m_pDoc->DeleteTab(0);
1403 }
1404 
testFormulaCompilerImplicitIntersectionOperators()1405 void Test::testFormulaCompilerImplicitIntersectionOperators()
1406 {
1407     struct TestCase
1408     {
1409         OUString const formula[3];
1410         double const result[3];
1411     };
1412 
1413     m_pDoc->InsertTab(0, "Test");
1414     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1415 
1416     m_pDoc->SetValue(2, 0, 0, 5); // C1
1417     m_pDoc->SetValue(2, 1, 0, 4); // C2
1418     m_pDoc->SetValue(2, 2, 0, 3); // C3
1419     m_pDoc->SetValue(3, 0, 0, 1); // D1
1420     m_pDoc->SetValue(3, 1, 0, 2); // D2
1421     m_pDoc->SetValue(3, 2, 0, 3); // D3
1422 
1423     TestCase tests[] =
1424     {
1425         { OUString("=C:C/D:D"), OUString("=C:C/D:D"), OUString("=C:C/D:D"), 5, 2, 1 },
1426         { OUString("=C1:C2/D1:D2"), OUString("=C2:C3/D2:D3"), OUString("=C3:C4/D3:D4"), 5, 2, 1 }
1427     };
1428 
1429     for (const TestCase& test : tests)
1430     {
1431         for(int i = 0; i < 2; ++i )
1432             m_pDoc->SetString(ScAddress(4,i,0), test.formula[i]); // E1-3
1433         for(int i = 0; i < 2; ++i )
1434             CPPUNIT_ASSERT_EQUAL_MESSAGE(OUString( test.formula[i] + " result incorrect in row " + OUString::number(i+1)).toUtf8().getStr(),
1435                 test.result[i], m_pDoc->GetValue(ScAddress(4,i,0)));
1436     }
1437 
1438     m_pDoc->DeleteTab(0);
1439 }
1440 
testFormulaRefUpdate()1441 void Test::testFormulaRefUpdate()
1442 {
1443     m_pDoc->InsertTab(0, "Formula");
1444 
1445     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1446 
1447     m_pDoc->SetValue(ScAddress(0,0,0), 2.0); // A1
1448     m_pDoc->SetString(ScAddress(2,2,0), "=A1");   // C3
1449     m_pDoc->SetString(ScAddress(2,3,0), "=$A$1"); // C4
1450 
1451     ScAddress aPos(2,2,0);
1452     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1453 
1454     aPos = ScAddress(2,3,0);
1455     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1456 
1457     // Delete row 2 to push formula cells up (to C2:C3).
1458     m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1459 
1460     aPos = ScAddress(2,1,0);
1461     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C2.");
1462 
1463     aPos = ScAddress(2,2,0);
1464     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C3.");
1465 
1466     // Insert one row at row 2 to move them back.
1467     m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1468 
1469     aPos = ScAddress(2,2,0);
1470     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1471 
1472     aPos = ScAddress(2,3,0);
1473     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1474 
1475     // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
1476     m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1477 
1478     aPos = ScAddress(2,4,0);
1479     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A3", "Wrong formula in C5.");
1480 
1481     aPos = ScAddress(2,5,0);
1482     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$3", "Wrong formula in C6.");
1483 
1484     // Delete 2 rows at row 1 to shift them back.
1485     m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1486 
1487     aPos = ScAddress(2,2,0);
1488     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1489 
1490     aPos = ScAddress(2,3,0);
1491     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1492 
1493     // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
1494     m_pDoc->InsertCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1495 
1496     aPos = ScAddress(5,2,0);
1497     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in F3.");
1498 
1499     aPos = ScAddress(5,3,0);
1500     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in F4.");
1501 
1502     // Delete columns B:D to shift them back.
1503     m_pDoc->DeleteCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1504 
1505     aPos = ScAddress(2,2,0);
1506     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1507 
1508     aPos = ScAddress(2,3,0);
1509     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1510 
1511     // Insert cells over A1:A3 to only shift A1 down to A4.
1512     m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
1513 
1514     aPos = ScAddress(2,2,0);
1515     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A4", "Wrong formula in C3.");
1516 
1517     aPos = ScAddress(2,3,0);
1518     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$4", "Wrong formula in C4.");
1519 
1520     // .. and back.
1521     m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
1522 
1523     aPos = ScAddress(2,2,0);
1524     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1525 
1526     aPos = ScAddress(2,3,0);
1527     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1528 
1529     // Delete row 1 which will delete the value cell (A1).
1530     m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1531 
1532     aPos = ScAddress(2,1,0);
1533     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
1534     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1535     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1536     aPos = ScAddress(2,2,0);
1537     pFC = m_pDoc->GetFormulaCell(aPos);
1538     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1539     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1540 
1541     // Clear all and start over.
1542     clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
1543 
1544     // Test range updates
1545 
1546     // Fill B2:C3 with values.
1547     m_pDoc->SetValue(ScAddress(1,1,0), 1);
1548     m_pDoc->SetValue(ScAddress(1,2,0), 2);
1549     m_pDoc->SetValue(ScAddress(2,1,0), 3);
1550     m_pDoc->SetValue(ScAddress(2,2,0), 4);
1551 
1552     m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
1553     m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
1554 
1555     aPos = ScAddress(0,5,0);
1556     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1557 
1558     aPos = ScAddress(0,6,0);
1559     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1560 
1561     // Insert a row at row 1.
1562     m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1563 
1564     aPos = ScAddress(0,6,0);
1565     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B3:C4)", "Wrong formula in A7.");
1566 
1567     aPos = ScAddress(0,7,0);
1568     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$3:$C$4)", "Wrong formula in A8.");
1569 
1570     // ... and back.
1571     m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1572 
1573     aPos = ScAddress(0,5,0);
1574     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1575 
1576     aPos = ScAddress(0,6,0);
1577     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1578 
1579     // Insert columns B:C to shift only the value range.
1580     m_pDoc->InsertCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1581 
1582     aPos = ScAddress(0,5,0);
1583     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(D2:E3)", "Wrong formula in A6.");
1584 
1585     aPos = ScAddress(0,6,0);
1586     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($D$2:$E$3)", "Wrong formula in A7.");
1587 
1588     // ... and back.
1589     m_pDoc->DeleteCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1590 
1591     aPos = ScAddress(0,5,0);
1592     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1593 
1594     aPos = ScAddress(0,6,0);
1595     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1596 
1597     // Insert rows 5:6 to shift the formula cells only.
1598     m_pDoc->InsertRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1599 
1600     aPos = ScAddress(0,7,0);
1601     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A8.");
1602 
1603     aPos = ScAddress(0,8,0);
1604     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A9.");
1605 
1606     // ... and back.
1607     m_pDoc->DeleteRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1608 
1609     aPos = ScAddress(0,5,0);
1610     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1611 
1612     aPos = ScAddress(0,6,0);
1613     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1614 
1615     // Check the values of the formula cells in A6:A7.
1616     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1617     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1618 
1619     // Insert cells over B1:B2 to partially shift value range.
1620     m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
1621 
1622     // Check the values of the formula cells in A6:A7 again.
1623     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1624     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1625 
1626     // ... and shift them back.
1627     m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
1628 
1629     // The formula cell results should be back too.
1630     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1631     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1632 
1633     // Delete rows 2:3 to completely remove the referenced range.
1634     m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),2,0));
1635 
1636     // Both A4 and A5 should show #REF! errors.
1637     pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
1638     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1639     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1640 
1641     pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
1642     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1643     CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1644 
1645     m_pDoc->DeleteTab(0);
1646 }
1647 
testFormulaRefUpdateRange()1648 void Test::testFormulaRefUpdateRange()
1649 {
1650     m_pDoc->InsertTab(0, "Formula");
1651 
1652     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1653 
1654     setExpandRefs(false);
1655 
1656     // Set values to B2:C5.
1657     m_pDoc->SetValue(ScAddress(1,1,0), 1);
1658     m_pDoc->SetValue(ScAddress(1,2,0), 2);
1659     m_pDoc->SetValue(ScAddress(1,3,0), 3);
1660     m_pDoc->SetValue(ScAddress(1,4,0), 4);
1661     m_pDoc->SetValue(ScAddress(2,1,0), 5);
1662     m_pDoc->SetValue(ScAddress(2,2,0), 6);
1663     m_pDoc->SetValue(ScAddress(2,3,0), 7);
1664     m_pDoc->SetValue(ScAddress(2,4,0), 8);
1665 
1666     // Set formula cells to A7 and A8.
1667     m_pDoc->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
1668     m_pDoc->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
1669 
1670     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(B2:C5)", "Wrong formula in A7.");
1671 
1672     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($B$2:$C$5)", "Wrong formula in A8.");
1673 
1674     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1675     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1676 
1677     // Delete row 3. This should shrink the range references by one row.
1678     m_pDoc->DeleteRow(ScRange(0,2,0,m_pDoc->MaxCol(),2,0));
1679 
1680     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(B2:C4)", "Wrong formula in A6.");
1681 
1682     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($B$2:$C$4)", "Wrong formula in A7.");
1683 
1684     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1685     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1686 
1687     // Delete row 4 - bottom of range
1688     m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
1689 
1690     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(B2:C3)", "Wrong formula in A5.");
1691 
1692     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($B$2:$C$3)", "Wrong formula in A6.");
1693 
1694     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1695     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1696 
1697     // Delete row 2 - top of range
1698     m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1699 
1700     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,3,0), "SUM(B2:C2)", "Wrong formula in A4.");
1701 
1702     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM($B$2:$C$2)", "Wrong formula in A5.");
1703 
1704     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1705     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1706 
1707     // Clear the range and start over.
1708     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1709 
1710     // Fill C2:F3 with values.
1711     m_pDoc->SetValue(ScAddress(2,1,0), 1);
1712     m_pDoc->SetValue(ScAddress(3,1,0), 2);
1713     m_pDoc->SetValue(ScAddress(4,1,0), 3);
1714     m_pDoc->SetValue(ScAddress(5,1,0), 4);
1715     m_pDoc->SetValue(ScAddress(2,2,0), 5);
1716     m_pDoc->SetValue(ScAddress(3,2,0), 6);
1717     m_pDoc->SetValue(ScAddress(4,2,0), 7);
1718     m_pDoc->SetValue(ScAddress(5,2,0), 8);
1719 
1720     // Set formulas to A2 and A3.
1721     m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
1722     m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
1723 
1724     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:F3)", "Wrong formula in A2.");
1725 
1726     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$F$3)", "Wrong formula in A3.");
1727 
1728     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1729     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1730 
1731     // Delete column D.
1732     m_pDoc->DeleteCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
1733 
1734     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:E3)", "Wrong formula in A2.");
1735 
1736     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$E$3)", "Wrong formula in A3.");
1737 
1738     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1739     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1740 
1741     // Delete column E - the right edge of reference range.
1742     m_pDoc->DeleteCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
1743 
1744     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:D3)", "Wrong formula in A2.");
1745 
1746     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$D$3)", "Wrong formula in A3.");
1747 
1748     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1749     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1750 
1751     // Delete column C - the left edge of reference range.
1752     m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1753 
1754     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:C3)", "Wrong formula in A2.");
1755 
1756     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$C$3)", "Wrong formula in A3.");
1757 
1758     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1759     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1760 
1761     // Clear the range and start over.
1762     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1763 
1764     // Disable expansion of range reference on insertion in adjacent areas.
1765     setExpandRefs(false);
1766 
1767     // Fill C2:D3 with values.
1768     m_pDoc->SetValue(ScAddress(2,1,0), 1);
1769     m_pDoc->SetValue(ScAddress(3,1,0), 2);
1770     m_pDoc->SetValue(ScAddress(2,2,0), 3);
1771     m_pDoc->SetValue(ScAddress(3,2,0), 4);
1772 
1773     // Set formulas at A5 and A6.
1774     m_pDoc->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
1775     m_pDoc->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
1776 
1777     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
1778 
1779     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
1780 
1781     // Insert a column at column C. This should simply shift the reference without expansion.
1782     m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1783 
1784     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(D2:E3)", "Wrong formula in A5.");
1785 
1786     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($D$2:$E$3)", "Wrong formula in A6.");
1787 
1788     // Shift it back.
1789     m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1790 
1791     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
1792 
1793     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
1794 
1795     // Insert at column D. This should expand the reference by one column length.
1796     m_pDoc->InsertCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
1797 
1798     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
1799 
1800     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
1801 
1802     // Insert at column F. No expansion should occur since the edge expansion is turned off.
1803     m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
1804 
1805     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
1806 
1807     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
1808 
1809     // Insert at row 2. No expansion should occur with edge expansion turned off.
1810     m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1811 
1812     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(C3:E4)", "Wrong formula in A6.");
1813 
1814     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($C$3:$E$4)", "Wrong formula in A7.");
1815 
1816     // Insert at row 4 to expand the reference range.
1817     m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
1818 
1819     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(C3:E5)", "Wrong formula in A7.");
1820 
1821     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($C$3:$E$5)", "Wrong formula in A8.");
1822 
1823     // Insert at row 6. No expansion with edge expansion turned off.
1824     m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
1825 
1826     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM(C3:E5)", "Wrong formula in A8.");
1827 
1828     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM($C$3:$E$5)", "Wrong formula in A9.");
1829 
1830     // Clear the range and start over.
1831     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1832 
1833     // Turn edge expansion on.
1834     setExpandRefs(true);
1835 
1836     // Fill C6:D7 with values.
1837     m_pDoc->SetValue(ScAddress(2,5,0), 1);
1838     m_pDoc->SetValue(ScAddress(2,6,0), 2);
1839     m_pDoc->SetValue(ScAddress(3,5,0), 3);
1840     m_pDoc->SetValue(ScAddress(3,6,0), 4);
1841 
1842     // Set formulas at A2 and A3.
1843     m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
1844     m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
1845 
1846     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:D7)", "Wrong formula in A2.");
1847 
1848     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$D$7)", "Wrong formula in A3.");
1849 
1850     // Insert at column E. This should expand the reference range by one column.
1851     m_pDoc->InsertCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
1852 
1853     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:E7)", "Wrong formula in A2.");
1854 
1855     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$E$7)", "Wrong formula in A3.");
1856 
1857     // Insert at column C to edge-expand the reference range.
1858     m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1859 
1860     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F7)", "Wrong formula in A2.");
1861 
1862     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$7)", "Wrong formula in A3.");
1863 
1864     // Insert at row 8 to edge-expand.
1865     m_pDoc->InsertRow(ScRange(0,7,0,m_pDoc->MaxCol(),7,0));
1866 
1867     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F8)", "Wrong formula in A2.");
1868 
1869     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$8)", "Wrong formula in A3.");
1870 
1871     // Insert at row 6 to edge-expand.
1872     m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
1873 
1874     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F9)", "Wrong formula in A2.");
1875 
1876     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$9)", "Wrong formula in A3.");
1877 
1878     m_pDoc->InsertTab(1, "StickyRange");
1879 
1880     // A3:A18 all possible combinations of relative and absolute addressing,
1881     // leaving one row above and below unreferenced.
1882     ScAddress aPos(0,2,1);
1883     m_pDoc->SetString( aPos, "=B2:B1048575");
1884     aPos.IncRow();
1885     m_pDoc->SetString( aPos, "=B2:B$1048575");
1886     aPos.IncRow();
1887     m_pDoc->SetString( aPos, "=B2:$B1048575");
1888     aPos.IncRow();
1889     m_pDoc->SetString( aPos, "=B2:$B$1048575");
1890     aPos.IncRow();
1891     m_pDoc->SetString( aPos, "=B$2:B1048575");
1892     aPos.IncRow();
1893     m_pDoc->SetString( aPos, "=B$2:B$1048575");
1894     aPos.IncRow();
1895     m_pDoc->SetString( aPos, "=B$2:$B1048575");
1896     aPos.IncRow();
1897     m_pDoc->SetString( aPos, "=B$2:$B$1048575");
1898     aPos.IncRow();
1899     m_pDoc->SetString( aPos, "=$B2:B1048575");
1900     aPos.IncRow();
1901     m_pDoc->SetString( aPos, "=$B2:B$1048575");
1902     aPos.IncRow();
1903     m_pDoc->SetString( aPos, "=$B2:$B1048575");
1904     aPos.IncRow();
1905     m_pDoc->SetString( aPos, "=$B2:$B$1048575");
1906     aPos.IncRow();
1907     m_pDoc->SetString( aPos, "=$B$2:B1048575");
1908     aPos.IncRow();
1909     m_pDoc->SetString( aPos, "=$B$2:B$1048575");
1910     aPos.IncRow();
1911     m_pDoc->SetString( aPos, "=$B$2:$B1048575");
1912     aPos.IncRow();
1913     m_pDoc->SetString( aPos, "=$B$2:$B$1048575");
1914     aPos.IncRow();
1915     // A19 reference to two cells on one row.
1916     m_pDoc->SetString( aPos, "=B1048575:C1048575");
1917     aPos.IncRow();
1918 
1919     // Insert 2 rows in the middle to shift bottom reference down and make it
1920     // sticky.
1921     m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
1922 
1923     // A3:A18 must not result in #REF! anywhere.
1924     aPos.Set(0,2,1);
1925     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:B1048576", "Wrong reference in A3 after insertion.");
1926     aPos.IncRow();
1927     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:B$1048576", "Wrong reference in A4 after insertion.");
1928     aPos.IncRow();
1929     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:$B1048576", "Wrong reference in A5 after insertion.");
1930     aPos.IncRow();
1931     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:$B$1048576", "Wrong reference in A6 after insertion.");
1932     aPos.IncRow();
1933     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:B1048576", "Wrong reference in A7 after insertion.");
1934     aPos.IncRow();
1935     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:B$1048576", "Wrong reference in A8 after insertion.");
1936     aPos.IncRow();
1937     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:$B1048576", "Wrong reference in A9 after insertion.");
1938     aPos.IncRow();
1939     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:$B$1048576", "Wrong reference in A10 after insertion.");
1940     aPos.IncRow();
1941     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:B1048576", "Wrong reference in A11 after insertion.");
1942     aPos.IncRow();
1943     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:B$1048576", "Wrong reference in A12 after insertion.");
1944     aPos.IncRow();
1945     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:$B1048576", "Wrong reference in A13 after insertion.");
1946     aPos.IncRow();
1947     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:$B$1048576", "Wrong reference in A14 after insertion.");
1948     aPos.IncRow();
1949     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:B1048576", "Wrong reference in A15 after insertion.");
1950     aPos.IncRow();
1951     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:B$1048576", "Wrong reference in A16 after insertion.");
1952     aPos.IncRow();
1953     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:$B1048576", "Wrong reference in A17 after insertion.");
1954     aPos.IncRow();
1955     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:$B$1048576", "Wrong reference in A18 after insertion.");
1956     aPos.IncRow();
1957 
1958     // A19 reference to one row shifted out should be #REF!
1959     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B#REF!:C#REF!", "Wrong reference in A19 after insertion.");
1960     // A19 enter reference to last row.
1961     m_pDoc->SetString( aPos, "=B1048576:C1048576");
1962     aPos.IncRow();
1963 
1964     // Delete row 1 to shift top reference up, bottom reference stays sticky.
1965     m_pDoc->DeleteRow(ScRange(0,0,1,m_pDoc->MaxCol(),0,1));
1966 
1967     // Check sticky bottom references and display of entire column references,
1968     // now in A2:A17.
1969     aPos.Set(0,1,1);
1970     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A2 after deletion.");
1971     aPos.IncRow();
1972     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:B$1048576", "Wrong reference in A3 after deletion.");
1973     aPos.IncRow();
1974     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A4 after deletion.");
1975     aPos.IncRow();
1976     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
1977     aPos.IncRow();
1978     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:B1048576", "Wrong reference in A6 after deletion.");
1979     aPos.IncRow();
1980     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A7 after deletion.");
1981     aPos.IncRow();
1982     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
1983     aPos.IncRow();
1984     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A9 after deletion.");
1985     aPos.IncRow();
1986     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A10 after deletion.");
1987     aPos.IncRow();
1988     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
1989     aPos.IncRow();
1990     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A12 after deletion.");
1991     aPos.IncRow();
1992     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
1993     aPos.IncRow();
1994     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
1995     aPos.IncRow();
1996     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A15 after deletion.");
1997     aPos.IncRow();
1998     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
1999     aPos.IncRow();
2000     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A17 after deletion.");
2001     aPos.IncRow();
2002 
2003     // A18 reference to one last row should be shifted up.
2004     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1048575:C1048575", "Wrong reference in A18 after deletion.");
2005     aPos.IncRow();
2006 
2007     // Insert 4 rows in the middle.
2008     m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+3, 1));
2009     // Delete 2 rows in the middle.
2010     m_pDoc->DeleteRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
2011 
2012     // References in A2:A17 must still be the same.
2013     aPos.Set(0,1,1);
2014     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A2 after deletion.");
2015     aPos.IncRow();
2016     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:B$1048576", "Wrong reference in A3 after deletion.");
2017     aPos.IncRow();
2018     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A4 after deletion.");
2019     aPos.IncRow();
2020     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
2021     aPos.IncRow();
2022     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:B1048576", "Wrong reference in A6 after deletion.");
2023     aPos.IncRow();
2024     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A7 after deletion.");
2025     aPos.IncRow();
2026     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
2027     aPos.IncRow();
2028     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A9 after deletion.");
2029     aPos.IncRow();
2030     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A10 after deletion.");
2031     aPos.IncRow();
2032     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
2033     aPos.IncRow();
2034     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A12 after deletion.");
2035     aPos.IncRow();
2036     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
2037     aPos.IncRow();
2038     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
2039     aPos.IncRow();
2040     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A15 after deletion.");
2041     aPos.IncRow();
2042     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2043     aPos.IncRow();
2044     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A17 after deletion.");
2045     aPos.IncRow();
2046 
2047     // Enter values in B1 and B1048576 (last row).
2048     m_pDoc->SetValue( 1,0,1, 1.0);
2049     m_pDoc->SetValue( 1,m_pDoc->MaxRow(),1, 2.0);
2050     // Sticky reference including last row.
2051     m_pDoc->SetString( 2,0,1, "=SUM(B:B)");
2052     // Reference to last row.
2053     CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxRow() changed, adapt unit test.", 1048575, int(m_pDoc->MaxRow()));
2054     m_pDoc->SetString( 2,1,1, "=SUM(B1048576:C1048576)");
2055     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 3.0, m_pDoc->GetValue(2,0,1));
2056     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C2.", 2.0, m_pDoc->GetValue(2,1,1));
2057     // Delete last row.
2058     m_pDoc->DeleteRow( ScRange( 0, m_pDoc->MaxRow(), 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2059     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 1.0, m_pDoc->GetValue(2,0,1));
2060     CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C2 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,1,1));
2061 
2062     // Enter values in A23 and AMJ23 (last column).
2063     m_pDoc->SetValue( 0,22,1, 1.0);
2064     m_pDoc->SetValue( m_pDoc->MaxCol(),22,1, 2.0);
2065     // C3 with sticky reference including last column.
2066     m_pDoc->SetString( 2,2,1, "=SUM(23:23)");
2067     // C4 with reference to last column.
2068     CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxCol() changed, adapt unit test.", 1023, int(m_pDoc->MaxCol()));
2069     m_pDoc->SetString( 2,3,1, "=SUM(AMJ22:AMJ23)");
2070     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 3.0, m_pDoc->GetValue(2,2,1));
2071     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C4.", 2.0, m_pDoc->GetValue(2,3,1));
2072     // Delete last column.
2073     m_pDoc->DeleteCol( ScRange( m_pDoc->MaxCol(), 0, 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2074     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 1.0, m_pDoc->GetValue(2,2,1));
2075     CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C4 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,3,1));
2076 
2077     m_pDoc->DeleteTab(1);
2078 
2079     m_pDoc->DeleteTab(0);
2080 }
2081 
testFormulaRefUpdateSheets()2082 void Test::testFormulaRefUpdateSheets()
2083 {
2084     m_pDoc->InsertTab(0, "Sheet1");
2085     m_pDoc->InsertTab(1, "Sheet2");
2086 
2087     OUString aName;
2088     m_pDoc->GetName(0, aName);
2089     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2090     m_pDoc->GetName(1, aName);
2091     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2092 
2093     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2094 
2095     // Set values to B2:C3 on sheet Sheet1.
2096     m_pDoc->SetValue(ScAddress(1,1,0), 1);
2097     m_pDoc->SetValue(ScAddress(1,2,0), 2);
2098     m_pDoc->SetValue(ScAddress(2,1,0), 3);
2099     m_pDoc->SetValue(ScAddress(2,2,0), 4);
2100 
2101     // Set formulas to B2 and B3 on sheet Sheet2.
2102     m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
2103     m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
2104 
2105     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2106 
2107     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2108 
2109     // Swap the sheets.
2110     m_pDoc->MoveTab(0, 1);
2111     m_pDoc->GetName(0, aName);
2112     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2113     m_pDoc->GetName(1, aName);
2114     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2115 
2116     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2117 
2118     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2119 
2120     // Swap back.
2121     m_pDoc->MoveTab(0, 1);
2122     m_pDoc->GetName(0, aName);
2123     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2124     m_pDoc->GetName(1, aName);
2125     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2126 
2127     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2128 
2129     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2130 
2131     // Insert a new sheet between the two.
2132     m_pDoc->InsertTab(1, "Temp");
2133 
2134     m_pDoc->GetName(1, aName);
2135     CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName);
2136     m_pDoc->GetName(2, aName);
2137     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2138 
2139     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2140 
2141     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2142 
2143     // Move the last sheet (Sheet2) to the first position.
2144     m_pDoc->MoveTab(2, 0);
2145 
2146     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2147 
2148     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2149 
2150     // Move back.
2151     m_pDoc->MoveTab(0, 2);
2152 
2153     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2154 
2155     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2156 
2157     // Move the "Temp" sheet to the last position.
2158     m_pDoc->MoveTab(1, 2);
2159 
2160     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2161 
2162     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2163 
2164     // Move back.
2165     m_pDoc->MoveTab(2, 1);
2166 
2167     // Delete the temporary sheet.
2168     m_pDoc->DeleteTab(1);
2169 
2170     m_pDoc->GetName(1, aName);
2171     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2172 
2173     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2174 
2175     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2176 
2177     // Insert a new sheet before the first one.
2178     m_pDoc->InsertTab(0, "Temp");
2179 
2180     m_pDoc->GetName(1, aName);
2181     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2182     m_pDoc->GetName(2, aName);
2183     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2184 
2185     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2186 
2187     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2188 
2189     // Delete the temporary sheet.
2190     m_pDoc->DeleteTab(0);
2191 
2192     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2193 
2194     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2195 
2196     // Append a bunch of sheets.
2197     m_pDoc->InsertTab(2, "Temp1");
2198     m_pDoc->InsertTab(3, "Temp2");
2199     m_pDoc->InsertTab(4, "Temp3");
2200 
2201     // Move these tabs around. This shouldn't affects the first 2 sheets.
2202     m_pDoc->MoveTab(2, 4);
2203     m_pDoc->MoveTab(3, 2);
2204 
2205     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2206 
2207     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2208 
2209     // Delete the temp sheets.
2210     m_pDoc->DeleteTab(4);
2211     m_pDoc->DeleteTab(3);
2212     m_pDoc->DeleteTab(2);
2213 
2214     // Delete Sheet1.
2215     m_pDoc->DeleteTab(0);
2216     m_pDoc->GetName(0, aName);
2217     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2218 
2219     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(#REF!.B2:C3)", "Wrong formula in Sheet2.B2.");
2220 
2221     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2222 
2223     m_pDoc->DeleteTab(0);
2224 }
2225 
testFormulaRefUpdateInsertRows()2226 void Test::testFormulaRefUpdateInsertRows()
2227 {
2228     setExpandRefs(false);
2229 
2230     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2231     m_pDoc->InsertTab(0, "Formula");
2232 
2233     // Insert raw values in B2:B4.
2234     m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
2235     m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
2236     m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
2237 
2238     // Insert a formula in B5 to sum up B2:B4.
2239     m_pDoc->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
2240 
2241     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2242 
2243     // Insert rows over rows 1:2.
2244     ScMarkData aMark(MAXROW, MAXCOL);
2245     aMark.SelectOneTable(0);
2246     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2247     rFunc.InsertCells(ScRange(0,0,0,m_pDoc->MaxCol(),1,0), &aMark, INS_INSROWS_BEFORE, false, true);
2248 
2249     // The raw data should have shifted to B4:B6.
2250     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2251     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2252     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,5,0)));
2253 
2254     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,6,0), "SUM(B4:B6)", "Wrong formula!");
2255 
2256     // Clear and start over.
2257     clearSheet(m_pDoc, 0);
2258 
2259     // Set raw values in A4:A6.
2260     m_pDoc->SetValue(ScAddress(0,3,0), 1.0);
2261     m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
2262     m_pDoc->SetValue(ScAddress(0,5,0), 3.0);
2263 
2264     // Set formula in A3 to reference A4:A6.
2265     m_pDoc->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
2266 
2267     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2268 
2269     // Insert 3 rows over 2:4.  This should push A3:A6 to A6:A9.
2270     rFunc.InsertCells(ScRange(0,1,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
2271     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
2272     CPPUNIT_ASSERT(pFC);
2273     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula cell should not be an error.", 0, static_cast<int>(pFC->GetErrCode()));
2274     ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2275 
2276     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "MAX(A7:A9)", "Wrong formula!");
2277 
2278     m_pDoc->DeleteTab(0);
2279 }
2280 
testFormulaRefUpdateSheetsDelete()2281 void Test::testFormulaRefUpdateSheetsDelete()
2282 {
2283     m_pDoc->InsertTab(0, "Sheet1");
2284     m_pDoc->InsertTab(1, "Sheet2");
2285     m_pDoc->InsertTab(2, "Sheet3");
2286     m_pDoc->InsertTab(3, "Sheet4");
2287 
2288     m_pDoc->SetString(ScAddress(4,1,0), "=SUM(Sheet2.A4:Sheet4.A4)");
2289     m_pDoc->SetString(ScAddress(4,2,0), "=SUM($Sheet2.A4:$Sheet4.A4)");
2290     m_pDoc->DeleteTab(1);
2291 
2292     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,1,0), "SUM(Sheet3.A4:Sheet4.A4)", "Wrong Formula");
2293     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,2,0), "SUM($Sheet3.A4:$Sheet4.A4)", "Wrong Formula");
2294 
2295     m_pDoc->InsertTab(1, "Sheet2");
2296 
2297     m_pDoc->SetString(ScAddress(5,1,3), "=SUM(Sheet1.A5:Sheet3.A5)");
2298     m_pDoc->SetString(ScAddress(5,2,3), "=SUM($Sheet1.A5:$Sheet3.A5)");
2299     m_pDoc->DeleteTab(2);
2300 
2301     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(5,1,2), "SUM(Sheet1.A5:Sheet2.A5)", "Wrong Formula");
2302     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(5,2,2), "SUM($Sheet1.A5:$Sheet2.A5)", "Wrong Formula");
2303 
2304     m_pDoc->InsertTab(2, "Sheet3");
2305 
2306     m_pDoc->SetString(ScAddress(6,1,3), "=SUM(Sheet1.A6:Sheet3.A6)");
2307     m_pDoc->SetString(ScAddress(6,2,3), "=SUM($Sheet1.A6:$Sheet3.A6)");
2308     m_pDoc->DeleteTabs(0,3);
2309 
2310     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(6,1,0), "SUM(#REF!.A6:#REF!.A6)", "Wrong Formula");
2311     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(6,2,0), "SUM($#REF!.A6:$#REF!.A6)", "Wrong Formula");
2312 
2313     m_pDoc->InsertTab(0, "Sheet1");
2314     m_pDoc->InsertTab(1, "Sheet2");
2315     m_pDoc->InsertTab(2, "Sheet3");
2316 
2317     m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.A2:Sheet3.A2");
2318     m_pDoc->SetString(ScAddress(2,1,1), "=SUM(Sheet1.A1:Sheet2.A1");
2319     m_pDoc->SetString(ScAddress(3,1,1), "=SUM(Sheet2.A3:Sheet4.A3");
2320 
2321     m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.A2:$Sheet3.A2");
2322     m_pDoc->SetString(ScAddress(2,2,1), "=SUM($Sheet1.A1:$Sheet2.A1");
2323     m_pDoc->SetString(ScAddress(3,2,1), "=SUM($Sheet2.A3:$Sheet4.A3");
2324 
2325     m_pDoc->DeleteTab(2);
2326 
2327     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.A2:Sheet2.A2)", "Wrong Formula");
2328 
2329     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,1), "SUM(Sheet1.A1:Sheet2.A1)", "Wrong Formula");
2330 
2331     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,1), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2332 
2333     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.A2:$Sheet2.A2)", "Wrong Formula");
2334 
2335     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,1), "SUM($Sheet1.A1:$Sheet2.A1)", "Wrong Formula");
2336 
2337     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,1), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2338 
2339     m_pDoc->DeleteTab(0);
2340 
2341     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet2.A2:Sheet2.A2)", "Wrong Formula");
2342 
2343     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "SUM(Sheet2.A1:Sheet2.A1)", "Wrong Formula");
2344 
2345     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2346 
2347     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet2.A2:$Sheet2.A2)", "Wrong Formula");
2348 
2349     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,0), "SUM($Sheet2.A1:$Sheet2.A1)", "Wrong Formula");
2350 
2351     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,0), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2352 
2353     m_pDoc->DeleteTab(0);
2354     m_pDoc->DeleteTab(0);
2355 }
2356 
testFormulaRefUpdateInsertColumns()2357 void Test::testFormulaRefUpdateInsertColumns()
2358 {
2359     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2360     setExpandRefs(false);
2361 
2362     m_pDoc->InsertTab(0, "Formula");
2363 
2364     // Set named range for B2 with absolute column and relative same row.
2365     const ScAddress aNamePos(0,1,0);
2366     bool bInserted = m_pDoc->InsertNewRangeName("RowRelativeRange", aNamePos, "$Formula.$B2");
2367     CPPUNIT_ASSERT(bInserted);
2368 
2369     // Set named range for entire absolute column B.
2370     bInserted = m_pDoc->InsertNewRangeName("EntireColumn", aNamePos, "$B:$B");
2371     CPPUNIT_ASSERT(bInserted);
2372 
2373     // Set named range for entire absolute row 2.
2374     bInserted = m_pDoc->InsertNewRangeName("EntireRow", aNamePos, "$2:$2");
2375     CPPUNIT_ASSERT(bInserted);
2376 
2377     // Set values in B1:B3.
2378     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
2379     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
2380     m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
2381 
2382     // Reference them in B4.
2383     m_pDoc->SetString(ScAddress(1,3,0), "=SUM(B1:B3)");
2384     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2385 
2386     // Use named range in C2 to reference B2.
2387     m_pDoc->SetString(ScAddress(2,1,0), "=RowRelativeRange");
2388     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
2389 
2390     // Use named range in C3 to reference column B, values in B1,B2,B3,B4
2391     m_pDoc->SetString(ScAddress(2,2,0), "=SUM(EntireColumn)");
2392     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2,2,0)));
2393 
2394     // Use named range in C4 to reference row 2, values in B2 and C2.
2395     m_pDoc->SetString(ScAddress(2,3,0), "=SUM(EntireRow)");
2396     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,3,0)));
2397 
2398     // Insert columns over A:B.
2399     ScMarkData aMark(MAXROW, MAXCOL);
2400     aMark.SelectOneTable(0);
2401     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2402     rFunc.InsertCells(ScRange(0,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
2403 
2404     // Now, the original column B has moved to column D.
2405     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,3,0), "SUM(D1:D3)", "Wrong formula in D4 after column insertion.");
2406 
2407     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
2408 
2409     // Check that the named reference points to the moved cell, now D2.
2410     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("ROWRELATIVERANGE");
2411     CPPUNIT_ASSERT(pName);
2412     OUString aSymbol;
2413     pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2414     CPPUNIT_ASSERT_EQUAL(OUString("$Formula.$D2"), aSymbol);
2415 
2416     // Check that the formula using the name, now in E2, still has the same result.
2417     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,1,0), "RowRelativeRange", "Wrong formula in E2 after column insertion.");
2418 
2419     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,1,0)));
2420 
2421     // Check that the named column reference points to the moved column, now D.
2422     pName = m_pDoc->GetRangeName()->findByUpperName("ENTIRECOLUMN");
2423     CPPUNIT_ASSERT(pName);
2424     pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2425     CPPUNIT_ASSERT_EQUAL(OUString("$D:$D"), aSymbol);
2426 
2427     // Check that the formula using the name, now in E3, still has the same result.
2428     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,2,0), "SUM(EntireColumn)", "Wrong formula in E3 after column insertion.");
2429 
2430     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(4,2,0)));
2431 
2432     // Check that the named row reference still points to the same entire row
2433     // and does not have a #REF! error due to inserted columns.
2434     pName = m_pDoc->GetRangeName()->findByUpperName("ENTIREROW");
2435     CPPUNIT_ASSERT(pName);
2436     pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2437     CPPUNIT_ASSERT_EQUAL(OUString("$2:$2"), aSymbol);
2438 
2439     // Check that the formula using the name, now in E4, still has the same result.
2440     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,3,0), "SUM(EntireRow)", "Wrong formula in E4 after column insertion.");
2441 
2442     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(4,3,0)));
2443 
2444     m_pDoc->DeleteTab(0);
2445 }
2446 
testFormulaRefUpdateMove()2447 void Test::testFormulaRefUpdateMove()
2448 {
2449     m_pDoc->InsertTab(0, "Sheet1");
2450 
2451     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2452 
2453     // Set value to B4:B6.
2454     m_pDoc->SetValue(ScAddress(1,3,0), 1);
2455     m_pDoc->SetValue(ScAddress(1,4,0), 2);
2456     m_pDoc->SetValue(ScAddress(1,5,0), 3);
2457 
2458     // Set formulas to A9:A12 that references B4:B6.
2459     m_pDoc->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
2460     m_pDoc->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
2461     m_pDoc->SetString(ScAddress(0,10,0), "=B5");
2462     m_pDoc->SetString(ScAddress(0,11,0), "=$B$6");
2463 
2464     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2465     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2466     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2467     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2468 
2469     // Move B4:B6 to D4 (two columns to the right).
2470     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2471     bool bMoved = rFunc.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
2472     CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved);
2473 
2474     // The results of the formula cells that reference the moved range should remain the same.
2475     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2476     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2477     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2478     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2479 
2480     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM(D4:D6)", "Wrong formula.");
2481     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM($D$4:$D$6)", "Wrong formula.");
2482     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "D5", "Wrong formula.");
2483     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "$D$6", "Wrong formula.");
2484 
2485     // Move A9:A12 to B10:B13.
2486     bMoved = rFunc.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
2487     CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved);
2488 
2489     // The results of these formula cells should still stay the same.
2490     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,9,0));
2491     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,10,0));
2492     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2493     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2494 
2495     // Displayed formulas should stay the same since the referenced range hasn't moved.
2496     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2497     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2498     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,11,0), "D5", "Wrong formula.");
2499     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2500 
2501     // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
2502     // where it is.
2503     bMoved = rFunc.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
2504     CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved);
2505 
2506     // Only the values of B10 and B11 should be updated.
2507     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,9,0));
2508     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,10,0));
2509     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2510     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2511 
2512     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2513     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2514     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,11,0), "E5", "Wrong formula.");
2515     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2516 
2517     m_pDoc->DeleteTab(0);
2518 }
2519 
testFormulaRefUpdateMoveUndo()2520 void Test::testFormulaRefUpdateMoveUndo()
2521 {
2522     m_pDoc->InsertTab(0, "Test");
2523 
2524     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2525 
2526     // Set values in A1:A4.
2527     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2528     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
2529     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
2530     m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
2531 
2532     // Set formulas with single cell references in A6:A8.
2533     m_pDoc->SetString(ScAddress(0,5,0), "=A1");
2534     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2535     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "A1", "Wrong formula.");
2536 
2537     m_pDoc->SetString(ScAddress(0,6,0), "=A1+A2+A3");
2538     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2539     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2540 
2541     m_pDoc->SetString(ScAddress(0,7,0), "=A1+A3+A4");
2542     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2543     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2544 
2545     // Set formulas with range references in A10:A12.
2546     m_pDoc->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
2547     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2548     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2549 
2550     m_pDoc->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
2551     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2552     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2553 
2554     m_pDoc->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
2555     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2556     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2557 
2558     // Move A1:A3 to C1:C3. Note that A4 remains.
2559     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2560     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
2561     CPPUNIT_ASSERT(bMoved);
2562 
2563     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2564     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "C1", "Wrong formula.");
2565 
2566     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2567     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "C1+C2+C3", "Wrong formula.");
2568 
2569     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2570     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "C1+C3+A4", "Wrong formula.");
2571 
2572     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2573     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(C1:C2)", "Wrong formula.");
2574 
2575     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2576     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(C1:C3)", "Wrong formula.");
2577 
2578     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2579     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2580 
2581     // Undo the move.
2582     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2583     CPPUNIT_ASSERT(pUndoMgr);
2584     pUndoMgr->Undo();
2585 
2586     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2587     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "A1", "Wrong formula.");
2588 
2589     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2590     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2591 
2592     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2593     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2594 
2595     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2596     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2597 
2598     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2599     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2600 
2601     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2602     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)","Wrong formula." );
2603 
2604     // Make sure the broadcasters are still valid by changing the value of A1.
2605     m_pDoc->SetValue(ScAddress(0,0,0), 20);
2606 
2607     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2608     CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2609     CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2610 
2611     CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2612     CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2613     CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2614 
2615     m_pDoc->DeleteTab(0);
2616 }
2617 
testFormulaRefUpdateMoveUndo2()2618 void Test::testFormulaRefUpdateMoveUndo2()
2619 {
2620     m_pDoc->InsertTab(0, "Test");
2621 
2622     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2623 
2624     std::vector<std::vector<const char*>> aData = {
2625         { "1", "2", "=A2*10",      "=SUM(A1:B1)" },
2626         { "3", "4", "=SUM(A2:B2)", "=SUM(A2:B2)" },
2627         { "=SUM(A1:B1)" },
2628     };
2629 
2630     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2631 
2632     std::vector<std::vector<const char*>> aCheckInitial = {
2633         { "1",     "2",    "30",     "3" },
2634         { "3",     "4",     "7",     "7" },
2635         { "3", nullptr, nullptr, nullptr },
2636     };
2637 
2638     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2639     CPPUNIT_ASSERT(bGood);
2640 
2641     // D1:D2 should be grouped.
2642     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
2643     CPPUNIT_ASSERT(pFC);
2644     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2645 
2646     // Drag A1:B1 into A2:B2 thereby overwriting the old A2:B2 content.
2647     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2648     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,1,0,0), ScAddress(0,1,0), true, true, false, true);
2649     CPPUNIT_ASSERT(bMoved);
2650 
2651     std::vector<std::vector<const char*>> aCheckAfter = {
2652         { nullptr, nullptr,    "10",     "3" },
2653         {     "1",     "2",     "3",     "3" },
2654         {     "3", nullptr, nullptr, nullptr },
2655     };
2656 
2657     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:B1 moved to A2:B2");
2658     CPPUNIT_ASSERT(bGood);
2659 
2660     // Undo the move.
2661     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2662     CPPUNIT_ASSERT(pUndoMgr);
2663     pUndoMgr->Undo();
2664 
2665     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2666     CPPUNIT_ASSERT(bGood);
2667 
2668     // D1:D2 should be grouped.
2669     pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
2670     CPPUNIT_ASSERT(pFC);
2671     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2672 
2673     // Redo and check.
2674     pUndoMgr->Redo();
2675 
2676     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2677     CPPUNIT_ASSERT(bGood);
2678 
2679     m_pDoc->DeleteTab(0);
2680 }
2681 
testFormulaRefUpdateMoveUndo3NonShared()2682 void Test::testFormulaRefUpdateMoveUndo3NonShared()
2683 {
2684     m_pDoc->InsertTab(0, "Test");
2685 
2686     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2687 
2688     std::vector<std::vector<const char*>> aData = {
2689         { "10",       nullptr,  nullptr },
2690         { "=A1",      nullptr,  nullptr },
2691         { "=A2+A1",   nullptr,  nullptr },
2692     };
2693 
2694     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2695 
2696     std::vector<std::vector<const char*>> aCheckInitial = {
2697         { "10", nullptr,  nullptr },
2698         { "10", nullptr,  nullptr },
2699         { "20", nullptr,  nullptr },
2700     };
2701 
2702     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2703     CPPUNIT_ASSERT(bGood);
2704 
2705     // Drag A2:A3 into C2:C3.
2706     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2707     bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,2,0), ScAddress(2,1,0), true, true, false, true);
2708     CPPUNIT_ASSERT(bMoved);
2709 
2710     std::vector<std::vector<const char*>> aCheckAfter = {
2711         { "10",    nullptr, nullptr},
2712         { nullptr, nullptr, "10" },
2713         { nullptr, nullptr, "20" },
2714     };
2715 
2716     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A3 moved to C2:C3");
2717     CPPUNIT_ASSERT(bGood);
2718 
2719     // Undo the move.
2720     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2721     CPPUNIT_ASSERT(pUndoMgr);
2722     pUndoMgr->Undo();
2723 
2724     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2725     CPPUNIT_ASSERT(bGood);
2726 
2727     // Redo and check.
2728     pUndoMgr->Redo();
2729 
2730     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2731     CPPUNIT_ASSERT(bGood);
2732 
2733     m_pDoc->DeleteTab(0);
2734 }
2735 
testFormulaRefUpdateMoveUndo3Shared()2736 void Test::testFormulaRefUpdateMoveUndo3Shared()
2737 {
2738     m_pDoc->InsertTab(0, "Test");
2739 
2740     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2741 
2742     std::vector<std::vector<const char*>> aData = {
2743         { "10",       nullptr,  nullptr },
2744         { "=A1",      nullptr,  nullptr },
2745         { "=A2+$A$1", nullptr,  nullptr },
2746         { "=A3+$A$1", nullptr,  nullptr },
2747     };
2748 
2749     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2750 
2751     std::vector<std::vector<const char*>> aCheckInitial = {
2752         { "10", nullptr,  nullptr },
2753         { "10", nullptr,  nullptr },
2754         { "20", nullptr,  nullptr },
2755         { "30", nullptr,  nullptr },
2756     };
2757 
2758     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2759     CPPUNIT_ASSERT(bGood);
2760 
2761     // A3:A4 should be grouped.
2762     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2763     CPPUNIT_ASSERT(pFC);
2764     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2765 
2766     // Drag A2:A4 into C2:C4.
2767     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2768     bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,3,0), ScAddress(2,1,0), true, true, false, true);
2769     CPPUNIT_ASSERT(bMoved);
2770 
2771     std::vector<std::vector<const char*>> aCheckAfter = {
2772         { "10",    nullptr, nullptr},
2773         { nullptr, nullptr, "10" },
2774         { nullptr, nullptr, "20" },
2775         { nullptr, nullptr, "30" },
2776     };
2777 
2778     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A4 moved to C2:C4");
2779     CPPUNIT_ASSERT(bGood);
2780 
2781     // C3:C4 should be grouped.
2782     pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
2783     CPPUNIT_ASSERT(pFC);
2784     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2785 
2786     // Undo the move.
2787     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2788     CPPUNIT_ASSERT(pUndoMgr);
2789     pUndoMgr->Undo();
2790 
2791     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2792     CPPUNIT_ASSERT(bGood);
2793 
2794     // A3:A4 should be grouped.
2795     pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2796     CPPUNIT_ASSERT(pFC);
2797     CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2798 
2799     // Redo and check.
2800     pUndoMgr->Redo();
2801 
2802     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2803     CPPUNIT_ASSERT(bGood);
2804 
2805     m_pDoc->DeleteTab(0);
2806 }
2807 
testFormulaRefUpdateMoveUndoDependents()2808 void Test::testFormulaRefUpdateMoveUndoDependents()
2809 {
2810     m_pDoc->InsertTab(0, "Test");
2811 
2812     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2813     std::vector<std::vector<const char*>> aData = {
2814         { "1"           },
2815         { "22"          },
2816         { "3"           },
2817         { "4"           },
2818         { "5"           },
2819         { "=SUM(C1:C5)" },
2820         { "=C6"         },
2821     };
2822 
2823     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData);
2824 
2825     std::vector<std::vector<const char*>> aCheckInitial = {
2826         { "1"   },
2827         { "22"  },
2828         { "3"   },
2829         { "4"   },
2830         { "5"   },
2831         { "35"  },
2832         { "35"  },
2833     };
2834 
2835     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2836     CPPUNIT_ASSERT(bGood);
2837 
2838     // Drag C2 into D2.
2839     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2840     bool bMoved = rFunc.MoveBlock(ScRange(2, 1, 0, 2, 1, 0), ScAddress(3, 1, 0), true, true, false, true);
2841     CPPUNIT_ASSERT(bMoved);
2842 
2843     std::vector<std::vector<const char*>> aCheckAfter = {
2844         { "1"     },
2845         { nullptr },
2846         { "3"     },
2847         { "4"     },
2848         { "5"     },
2849         { "13"    },
2850         { "13"    },
2851     };
2852 
2853     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "C2 moved to D2");
2854     CPPUNIT_ASSERT(bGood);
2855 
2856     // Undo the move.
2857     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2858     CPPUNIT_ASSERT(pUndoMgr);
2859     pUndoMgr->Undo();
2860 
2861     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2862     CPPUNIT_ASSERT(bGood);
2863 
2864     // Redo and check.
2865     pUndoMgr->Redo();
2866 
2867     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2868     CPPUNIT_ASSERT(bGood);
2869 
2870     m_pDoc->DeleteTab(0);
2871 }
2872 
testFormulaRefUpdateMoveUndo4()2873 void Test::testFormulaRefUpdateMoveUndo4()
2874 {
2875     m_pDoc->InsertTab(0, "Test");
2876 
2877     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2878     std::vector<std::vector<const char*>> aData = {
2879         { "1",  nullptr,  "=B1", "=A1" },
2880         { "2",  nullptr,  "=B2", "=A2" },
2881     };
2882 
2883     ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2884 
2885     std::vector<std::vector<const char*>> aCheckInitial = {
2886         { "1",  nullptr,  "0", "1" },
2887         { "2",  nullptr,  "0", "2" },
2888     };
2889 
2890     bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2891     CPPUNIT_ASSERT(bGood);
2892 
2893     // Drag A1:A2 into B1:B2.
2894     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2895     bool bMoved = rFunc.MoveBlock(ScRange(0, 0, 0, 0, 1, 0), ScAddress(1, 0, 0), true, true, false, true);
2896     CPPUNIT_ASSERT(bMoved);
2897 
2898     std::vector<std::vector<const char*>> aCheckAfter = {
2899         { nullptr, "1", "1", "1" },
2900         { nullptr, "2", "2", "2" },
2901     };
2902 
2903     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:A2 moved to B1:B2");
2904     CPPUNIT_ASSERT(bGood);
2905 
2906     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
2907     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
2908     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
2909     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
2910 
2911     // Undo the move.
2912     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2913     CPPUNIT_ASSERT(pUndoMgr);
2914     pUndoMgr->Undo();
2915 
2916     bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2917     CPPUNIT_ASSERT(bGood);
2918 
2919     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
2920     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
2921     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "A1", "Wrong formula"); // D1
2922     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "A2", "Wrong formula"); // D2
2923 
2924     // Redo and check.
2925     pUndoMgr->Redo();
2926 
2927     bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2928     CPPUNIT_ASSERT(bGood);
2929 
2930     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
2931     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
2932     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
2933     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
2934 
2935     m_pDoc->DeleteTab(0);
2936 }
2937 
testFormulaRefUpdateMoveToSheet()2938 void Test::testFormulaRefUpdateMoveToSheet()
2939 {
2940     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2941 
2942     m_pDoc->InsertTab(0, "Sheet1");
2943     m_pDoc->InsertTab(1, "Sheet2");
2944 
2945     // Set values to A1:A2 on Sheet1, and B1:B2 to reference them.
2946     m_pDoc->SetValue(ScAddress(0,0,0), 11);
2947     m_pDoc->SetValue(ScAddress(0,1,0), 12);
2948     m_pDoc->SetString(ScAddress(1,0,0), "=A1");
2949     m_pDoc->SetString(ScAddress(1,1,0), "=A2");
2950 
2951     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A1", "Wrong formula");
2952     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A2", "Wrong formula");
2953 
2954     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
2955     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(1,1,0)));
2956 
2957     // Move A1:A2 on Sheet1 to B3:B4 on Sheet2.
2958     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2959     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(1,2,1), true, true, false, true);
2960     CPPUNIT_ASSERT(bMoved);
2961 
2962     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
2963     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
2964 
2965     // Undo and check again.
2966     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2967     pUndoMgr->Undo();
2968 
2969     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A1", "Wrong formula");
2970     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A2", "Wrong formula");
2971 
2972     // Redo and check.
2973     pUndoMgr->Redo();
2974 
2975     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
2976     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
2977 
2978     m_pDoc->DeleteTab(1);
2979     m_pDoc->DeleteTab(0);
2980 }
2981 
testFormulaRefUpdateDeleteContent()2982 void Test::testFormulaRefUpdateDeleteContent()
2983 {
2984     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2985 
2986     m_pDoc->InsertTab(0, "Test");
2987 
2988     // Set value in B2.
2989     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
2990     // Set formula in C2 to reference B2.
2991     m_pDoc->SetString(ScAddress(2,1,0), "=B2");
2992 
2993     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
2994 
2995     // Delete B2.
2996     ScDocFunc& rFunc = getDocShell().GetDocFunc();
2997     ScMarkData aMark(MAXROW, MAXCOL);
2998     aMark.SetMarkArea(ScAddress(1,1,0));
2999     rFunc.DeleteContents(aMark, InsertDeleteFlags::CONTENTS, true, true);
3000 
3001     CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3002     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3003 
3004     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3005     CPPUNIT_ASSERT(pUndoMgr);
3006 
3007     // Undo and check the result of C2.
3008     pUndoMgr->Undo();
3009     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3010     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3011 
3012     // Redo and check.
3013     pUndoMgr->Redo();
3014     CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3015     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3016 
3017     m_pDoc->DeleteTab(0);
3018 }
3019 
testFormulaRefUpdateDeleteAndShiftLeft()3020 void Test::testFormulaRefUpdateDeleteAndShiftLeft()
3021 {
3022     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3023 
3024     m_pDoc->InsertTab(0, "Test");
3025 
3026     // Insert 1,2,3,4,5 in C1:G1.
3027     for (SCCOL i = 0; i <= 4; ++i)
3028         m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3029 
3030     // Insert formula in H1.
3031     ScAddress aPos(7,0,0);
3032     m_pDoc->SetString(aPos, "=SUM(C1:G1)");
3033 
3034     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3035 
3036     // Delete columns D:E (middle of the reference).
3037     ScMarkData aMark(MAXROW, MAXCOL);
3038     aMark.SelectOneTable(0);
3039     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3040     bool bDeleted = rFunc.DeleteCells(ScRange(3,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3041     CPPUNIT_ASSERT(bDeleted);
3042 
3043     aPos.IncCol(-2);
3044     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3045     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3046 
3047     // Undo and check.
3048     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3049     CPPUNIT_ASSERT(pUndo);
3050 
3051     pUndo->Undo();
3052     aPos.IncCol(2);
3053     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3054     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3055 
3056     // Delete columns C:D (left end of the reference).
3057     bDeleted = rFunc.DeleteCells(ScRange(2,0,0,3,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3058     CPPUNIT_ASSERT(bDeleted);
3059 
3060     aPos.IncCol(-2);
3061     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3062     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3063 
3064     // Undo and check again.
3065     pUndo->Undo();
3066     aPos.IncCol(2);
3067     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3068     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3069 
3070     // Delete columns B:E (overlaps on the left).
3071     bDeleted = rFunc.DeleteCells(ScRange(1,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3072     CPPUNIT_ASSERT(bDeleted);
3073 
3074     aPos.IncCol(-4);
3075     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3076     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:C1)", "Wrong formula!");
3077 
3078     // Undo and check again.
3079     pUndo->Undo();
3080     aPos.IncCol(4);
3081     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3082     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3083 
3084     // Start over with a new scenario.
3085     clearSheet(m_pDoc, 0);
3086 
3087     // Insert 1,2,3,4,5,6 into C1:H1.
3088     for (SCCOL i = 0; i <= 5; ++i)
3089         m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3090 
3091     // Set formula in B1.
3092     aPos = ScAddress(1,0,0);
3093     m_pDoc->SetString(aPos, "=SUM(C1:H1)");
3094     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3095 
3096     // Delete columns F:H (right end of the reference).
3097     bDeleted = rFunc.DeleteCells(ScRange(5,0,0,7,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3098     CPPUNIT_ASSERT(bDeleted);
3099 
3100     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3101     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3102 
3103     // Undo and check.
3104     pUndo->Undo();
3105     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3106     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:H1)", "Wrong formula!");
3107 
3108     // Delete columns G:I (overlaps on the right).
3109     bDeleted = rFunc.DeleteCells(ScRange(6,0,0,8,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3110     CPPUNIT_ASSERT(bDeleted);
3111 
3112     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3113     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:F1)", "Wrong formula!");
3114 
3115     // Undo and check again.
3116     pUndo->Undo();
3117     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3118     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:H1)", "Wrong formula!");
3119 
3120     m_pDoc->DeleteTab(0);
3121 }
3122 
testFormulaRefUpdateDeleteAndShiftLeft2()3123 void Test::testFormulaRefUpdateDeleteAndShiftLeft2()
3124 {
3125     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3126 
3127     m_pDoc->InsertTab(0, "Test");
3128 
3129     std::vector<std::vector<const char*>> aData = {
3130         { "1", "=COUNT($A$1:$A$4)", "=COUNT(A1)" },
3131         { "2", "=COUNT($A$1:$A$4)", "=COUNT(A2)" },
3132         { "3", "=COUNT($A$1:$A$4)", "=COUNT(A3)" },
3133         { "4", "=COUNT($A$1:$A$4)", "=COUNT(A4)" },
3134     };
3135 
3136     insertRangeData(m_pDoc, ScAddress(), aData);
3137 
3138     auto funcCheckOriginal = [&]()
3139     {
3140         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0))); // A1
3141         CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0))); // A2
3142         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0))); // A3
3143         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0))); // A4
3144 
3145         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,0,0))); // B1
3146         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3147         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0))); // B3
3148         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0))); // B4
3149 
3150         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,0,0))); // C1
3151         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3152         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,2,0))); // C3
3153         CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,3,0))); // C4
3154     };
3155 
3156     auto funcCheckDeleted = [&]()
3157     {
3158         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,0,0))); // A1
3159         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,1,0))); // A2
3160         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,2,0))); // A3
3161         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,3,0))); // A4
3162 
3163         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,0,0))); // B1
3164         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,1,0))); // B2
3165         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,2,0))); // B3
3166         CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,3,0))); // B4
3167     };
3168 
3169     funcCheckOriginal();
3170 
3171     // Delete Column A.
3172     ScMarkData aMark(MAXROW, MAXCOL);
3173     aMark.SelectOneTable(0);
3174     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3175     bool bDeleted = rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3176     CPPUNIT_ASSERT(bDeleted);
3177 
3178     funcCheckDeleted();
3179 
3180     // Undo and check.
3181     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3182     CPPUNIT_ASSERT(pUndo);
3183 
3184     pUndo->Undo();
3185     funcCheckOriginal();
3186 
3187     // Redo and check.
3188     pUndo->Redo();
3189     funcCheckDeleted();
3190 
3191     m_pDoc->DeleteTab(0);
3192 }
3193 
testFormulaRefUpdateDeleteAndShiftUp()3194 void Test::testFormulaRefUpdateDeleteAndShiftUp()
3195 {
3196     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3197 
3198     m_pDoc->InsertTab(0, "Test");
3199 
3200     // Insert 1,2,3,4,5 in A3:A7.
3201     for (SCROW i = 0; i <= 4; ++i)
3202         m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3203 
3204     // Insert formula in A8.
3205     ScAddress aPos(0,7,0);
3206     m_pDoc->SetString(aPos, "=SUM(A3:A7)");
3207 
3208     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3209 
3210     // Delete rows 4:5 (middle of the reference).
3211     ScMarkData aMark(MAXROW, MAXCOL);
3212     aMark.SelectOneTable(0);
3213     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3214     bool bDeleted = rFunc.DeleteCells(ScRange(0,3,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3215     CPPUNIT_ASSERT(bDeleted);
3216 
3217     aPos.IncRow(-2);
3218     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3219     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3220 
3221     // Undo and check.
3222     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3223     CPPUNIT_ASSERT(pUndo);
3224 
3225     pUndo->Undo();
3226     aPos.IncRow(2);
3227     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3228     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3229 
3230     // Delete rows 3:4 (top end of the reference).
3231     bDeleted = rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),3,0), &aMark, DelCellCmd::CellsUp, true);
3232     CPPUNIT_ASSERT(bDeleted);
3233 
3234     aPos.IncRow(-2);
3235     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3236     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3237 
3238     // Undo and check again.
3239     pUndo->Undo();
3240     aPos.IncRow(2);
3241     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3242     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3243 
3244     // Delete rows 2:5 (overlaps on the top).
3245     bDeleted = rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3246     CPPUNIT_ASSERT(bDeleted);
3247 
3248     aPos.IncRow(-4);
3249     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3250     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A2:A3)", "Wrong formula!");
3251 
3252     // Undo and check again.
3253     pUndo->Undo();
3254     aPos.IncRow(4);
3255     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3256     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3257 
3258     // Start over with a new scenario.
3259     clearSheet(m_pDoc, 0);
3260 
3261     // Insert 1,2,3,4,5,6 into A3:A8.
3262     for (SCROW i = 0; i <= 5; ++i)
3263         m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3264 
3265     // Set formula in B1.
3266     aPos = ScAddress(0,1,0);
3267     m_pDoc->SetString(aPos, "=SUM(A3:A8)");
3268     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3269 
3270     // Delete rows 6:8 (bottom end of the reference).
3271     bDeleted = rFunc.DeleteCells(ScRange(0,5,0,m_pDoc->MaxCol(),7,0), &aMark, DelCellCmd::CellsUp, true);
3272     CPPUNIT_ASSERT(bDeleted);
3273 
3274     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3275     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3276 
3277     // Undo and check.
3278     pUndo->Undo();
3279     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3280     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A8)", "Wrong formula!");
3281 
3282     // Delete rows 7:9 (overlaps on the bottom).
3283     bDeleted = rFunc.DeleteCells(ScRange(0,6,0,m_pDoc->MaxCol(),8,0), &aMark, DelCellCmd::CellsUp, true);
3284     CPPUNIT_ASSERT(bDeleted);
3285 
3286     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3287     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A6)", "Wrong formula!");
3288 
3289     // Undo and check again.
3290     pUndo->Undo();
3291     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3292     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A8)", "Wrong formula!");
3293 
3294     m_pDoc->DeleteTab(0);
3295 }
3296 
testFormulaRefUpdateName()3297 void Test::testFormulaRefUpdateName()
3298 {
3299     m_pDoc->InsertTab(0, "Formula");
3300 
3301     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3302 
3303     // Fill C2:C5 with values.
3304     m_pDoc->SetValue(ScAddress(2,1,0), 1);
3305     m_pDoc->SetValue(ScAddress(2,2,0), 2);
3306     m_pDoc->SetValue(ScAddress(2,3,0), 3);
3307     m_pDoc->SetValue(ScAddress(2,4,0), 4);
3308 
3309     // Add a named expression that references the immediate left cell.
3310     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
3311     CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
3312     ScRangeData* pName = new ScRangeData(
3313         m_pDoc, "ToLeft", "RC[-1]", ScAddress(2,1,0),
3314         ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
3315 
3316     bool bInserted = pGlobalNames->insert(pName);
3317     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3318 
3319     // Insert formulas in D2:D5 using the named expression.
3320     m_pDoc->SetString(ScAddress(3,1,0), "=ToLeft");
3321     m_pDoc->SetString(ScAddress(3,2,0), "=ToLeft");
3322     m_pDoc->SetString(ScAddress(3,3,0), "=ToLeft");
3323     m_pDoc->SetString(ScAddress(3,4,0), "=ToLeft");
3324 
3325     // Make sure the results are correct.
3326     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3327     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3328     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3329     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3330 
3331     // Push cells in column C down by one cell.
3332     m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
3333 
3334     // Make sure the results change accordingly.
3335     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(3,1,0));
3336     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,2,0));
3337     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,3,0));
3338     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,4,0));
3339 
3340     // Move cells back.
3341     m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
3342 
3343     // Make sure the results are back as well.
3344     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3345     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3346     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3347     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3348 
3349     // Fill B10:B12 with values.
3350     m_pDoc->SetValue(ScAddress(1,9,0), 10);
3351     m_pDoc->SetValue(ScAddress(1,10,0), 11);
3352     m_pDoc->SetValue(ScAddress(1,11,0), 12);
3353 
3354     // Insert a new named expression that references these values as absolute range.
3355     pName = new ScRangeData(
3356         m_pDoc, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3357     bInserted = pGlobalNames->insert(pName);
3358     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3359 
3360     // Set formula at C8 that references this named expression.
3361     m_pDoc->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
3362     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3363 
3364     // Shift B10:B12 to right by 2 columns.
3365     m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
3366 
3367     // This should shift the absolute range B10:B12 that MyRange references.
3368     pName = pGlobalNames->findByUpperName("MYRANGE");
3369     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3370     OUString aExpr;
3371     pName->GetSymbol(aExpr);
3372     CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr);
3373 
3374     // This move shouldn't affect the value of C8.
3375     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,7,0));
3376     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3377     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3378 
3379     // Update the value of D10 and make sure C8 gets updated.
3380     m_pDoc->SetValue(ScAddress(3,9,0), 20);
3381     CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3382 
3383     // Insert a new sheet before the current.
3384     m_pDoc->InsertTab(0, "New");
3385     OUString aName;
3386     m_pDoc->GetName(1, aName);
3387     CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3388 
3389     pName = pGlobalNames->findByUpperName("MYRANGE");
3390     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3391 
3392     m_pDoc->SetValue(ScAddress(3,9,1), 10);
3393     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
3394 
3395     // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
3396     m_pDoc->DeleteTab(0);
3397 
3398     aName.clear();
3399     m_pDoc->GetName(0, aName);
3400     CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3401 
3402     pName = pGlobalNames->findByUpperName("MYRANGE");
3403     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3404 
3405     m_pDoc->SetValue(ScAddress(3,9,0), 11);
3406     CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3407 
3408     // Clear all and start over.
3409     clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
3410     pGlobalNames->clear();
3411 
3412     pName = new ScRangeData(
3413         m_pDoc, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3414     bInserted = pGlobalNames->insert(pName);
3415     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3416     pName->GetSymbol(aExpr);
3417     CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3418 
3419     // Insert range of cells to shift right. The range partially overlaps the named range.
3420     m_pDoc->InsertCol(ScRange(2,4,0,3,8,0));
3421 
3422     // This should not alter the range.
3423     pName->GetSymbol(aExpr);
3424     CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3425 
3426     m_pDoc->DeleteTab(0);
3427 }
3428 
testFormulaRefUpdateNameMove()3429 void Test::testFormulaRefUpdateNameMove()
3430 {
3431     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3432 
3433     m_pDoc->InsertTab(0, "Test");
3434 
3435     // Set values to B2:B4.
3436     m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
3437     m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
3438     m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
3439 
3440     // Set named range for B2:B4.
3441     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
3442     CPPUNIT_ASSERT(bInserted);
3443 
3444     // Set formula in A10.
3445     m_pDoc->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
3446     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3447 
3448     ScRangeData* pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3449     CPPUNIT_ASSERT(pData);
3450     OUString aSymbol;
3451     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3452     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3453 
3454     // Move B2:B4 to D3.
3455     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3456     bool bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3457     CPPUNIT_ASSERT(bMoved);
3458 
3459     // The named range should have moved as well.
3460     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3461     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3462 
3463     // The value of A10 should remain unchanged.
3464     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3465 
3466     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3467     CPPUNIT_ASSERT(pUndoMgr);
3468 
3469     // Undo and check.
3470     pUndoMgr->Undo();
3471 
3472     pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3473     CPPUNIT_ASSERT(pData);
3474     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3475     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3476     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3477 
3478     // Redo and check.
3479     pUndoMgr->Redo();
3480 
3481     pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3482     CPPUNIT_ASSERT(pData);
3483     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3484     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3485     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3486 
3487     // Undo again to bring it back to the initial condition, and clear the undo buffer.
3488     pUndoMgr->Undo();
3489     pUndoMgr->Clear();
3490 
3491     // Add an identical formula to A11 and make a formula group over A10:A11.
3492     m_pDoc->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
3493     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,0));
3494     CPPUNIT_ASSERT(pFC);
3495     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
3496     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
3497 
3498     // Move B2:B4 to D3 again.
3499     bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3500     CPPUNIT_ASSERT(bMoved);
3501 
3502     // Values of A10 and A11 should remain the same.
3503     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3504     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
3505 
3506     // Clear and start over.
3507     clearSheet(m_pDoc, 0);
3508     m_pDoc->GetRangeName()->clear();
3509 
3510     // Set value to B2.
3511     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3512 
3513     // Define B2 as 'MyCell'.
3514     bInserted = m_pDoc->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
3515     CPPUNIT_ASSERT(bInserted);
3516 
3517     // Set formula to B3 that references B2 via MyCell.
3518     m_pDoc->SetString(ScAddress(1,2,0), "=MyCell*2");
3519     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3520 
3521     // Move B2 to D2.
3522     bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,1,0), ScAddress(3,1,0), true, true, false, true);
3523     CPPUNIT_ASSERT(bMoved);
3524 
3525     // Value in B3 should remain unchanged.
3526     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3527 
3528     m_pDoc->DeleteTab(0);
3529 }
3530 
testFormulaRefUpdateNameExpandRef()3531 void Test::testFormulaRefUpdateNameExpandRef()
3532 {
3533     setExpandRefs(true);
3534 
3535     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3536 
3537     m_pDoc->InsertTab(0, "Test");
3538 
3539     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
3540     CPPUNIT_ASSERT(bInserted);
3541 
3542     // Set values to A1:A3.
3543     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3544     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
3545     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
3546 
3547     m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
3548     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
3549 
3550     // Insert a new row at row 4, which should expand the named range to A1:A4.
3551     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3552     ScMarkData aMark(MAXROW, MAXCOL);
3553     aMark.SelectOneTable(0);
3554     rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
3555     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3556     CPPUNIT_ASSERT(pName);
3557     OUString aSymbol;
3558     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3559     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3560 
3561     // Make sure the listening area has been expanded as well.  Note the
3562     // formula cell has been pushed downward by one cell.
3563     m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
3564     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3565 
3566     // Insert a new column at column 2, which should not expand the named
3567     // range as it is only one column wide.
3568     rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
3569     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3570     CPPUNIT_ASSERT(pName);
3571     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3572     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3573 
3574     // Make sure the referenced area has not changed.
3575     m_pDoc->SetValue(ScAddress(0,3,0), 2.0);
3576     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3577     m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
3578     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3579 
3580     // Clear the document and start over.
3581     m_pDoc->GetRangeName()->clear();
3582     clearSheet(m_pDoc, 0);
3583 
3584     // Set values to B4:B6.
3585     m_pDoc->SetValue(ScAddress(1,3,0), 1.0);
3586     m_pDoc->SetValue(ScAddress(1,4,0), 2.0);
3587     m_pDoc->SetValue(ScAddress(1,5,0), 3.0);
3588 
3589     bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
3590     CPPUNIT_ASSERT(bInserted);
3591 
3592     // Set formula to A1.
3593     m_pDoc->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
3594     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,0,0));
3595 
3596     // Insert rows over 3:5 which should expand the range by 3 rows.
3597     rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),4,0), &aMark, INS_INSROWS_BEFORE, false, true);
3598 
3599     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3600     CPPUNIT_ASSERT(pName);
3601 
3602     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3603     CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
3604 
3605     // Clear the document and start over.
3606     m_pDoc->GetRangeName()->clear();
3607     clearSheet(m_pDoc, 0);
3608 
3609     // Set values to A1:A3.
3610     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3611     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
3612     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
3613 
3614     // Name A1:A3 'MyData'.
3615     bInserted = m_pDoc->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
3616     CPPUNIT_ASSERT(bInserted);
3617 
3618     // Set formulas to C1:C2 and E1.
3619     m_pDoc->SetString(ScAddress(2,0,0), "=SUM(MyData)");
3620     m_pDoc->SetString(ScAddress(2,1,0), "=SUM(MyData)");
3621     m_pDoc->SetString(ScAddress(4,0,0), "=SUM(MyData)");
3622 
3623     // C1:C2 should be shared.
3624     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
3625     CPPUNIT_ASSERT(pFC);
3626     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
3627     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
3628 
3629     // E1 should not be shared.
3630     pFC = m_pDoc->GetFormulaCell(ScAddress(4,0,0));
3631     CPPUNIT_ASSERT(pFC);
3632     CPPUNIT_ASSERT(!pFC->IsShared());
3633 
3634     // Check the results.
3635     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,0,0)));
3636     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3637     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(4,0,0)));
3638 
3639     // Insert a new row at row 3.  This should expand MyData to A1:A4.
3640     rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, INS_INSROWS_BEFORE, false, true);
3641 
3642     // Set new value to A3.
3643     m_pDoc->SetValue(ScAddress(0,2,0), 4.0);
3644 
3645     // Check the results again.
3646     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
3647     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3648     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(4,0,0)));
3649 
3650     m_pDoc->DeleteTab(0);
3651 }
3652 
testFormulaRefUpdateNameExpandRef2()3653 void Test::testFormulaRefUpdateNameExpandRef2()
3654 {
3655     setExpandRefs(true);
3656 
3657     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3658 
3659     m_pDoc->InsertTab(0, "Test");
3660 
3661     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$B$3");
3662     CPPUNIT_ASSERT(bInserted);
3663 
3664     // Insert a new row at row 4, which should expand the named range to A1:A4.
3665     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3666     ScMarkData aMark(MAXROW, MAXCOL);
3667     aMark.SelectOneTable(0);
3668 
3669     // Insert a new column at column 3, which should expand the named
3670     rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
3671     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3672     CPPUNIT_ASSERT(pName);
3673     OUString aSymbol;
3674     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3675     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$C$3"), aSymbol);
3676 
3677     m_pDoc->DeleteTab(0);
3678 }
3679 
testFormulaRefUpdateNameDeleteRow()3680 void Test::testFormulaRefUpdateNameDeleteRow()
3681 {
3682     m_pDoc->InsertTab(0, "Test");
3683 
3684     // Insert a new name 'MyRange' to reference B2:B4.
3685     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$2:$B$4");
3686     CPPUNIT_ASSERT(bInserted);
3687 
3688     const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3689     CPPUNIT_ASSERT(pName);
3690 
3691     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
3692     const ScTokenArray* pCode = pName->GetCode();
3693     OUString aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3694     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3695 
3696     // Insert a new name 'MyAddress' to reference $B$3. Note absolute row.
3697     bInserted = m_pDoc->InsertNewRangeName("MyAddress", ScAddress(0,0,0), "$B$3");
3698     CPPUNIT_ASSERT(bInserted);
3699 
3700     const ScRangeData* pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3701     CPPUNIT_ASSERT(pName2);
3702 
3703     sc::TokenStringContext aCxt2(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
3704     const ScTokenArray* pCode2 = pName2->GetCode();
3705     OUString aExpr2 = pCode2->CreateString(m_pDoc, aCxt2, ScAddress(0,0,0));
3706     CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3707 
3708     ScDocFunc& rFunc = getDocShell().GetDocFunc();
3709 
3710     // Delete row 3.
3711     ScMarkData aMark(MAXROW, MAXCOL);
3712     aMark.SelectOneTable(0);
3713     rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3714 
3715     // The reference in the 'MyRange' name should get updated to B2:B3.
3716     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3717     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
3718 
3719     // The reference in the 'MyAddress' name should get updated to $B$#REF!.
3720     aExpr2 = pCode2->CreateString(m_pDoc, aCxt2, ScAddress(0,0,0));
3721     CPPUNIT_ASSERT_EQUAL(OUString("$B$#REF!"), aExpr2);
3722 
3723     // Delete row 3 again.
3724     rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3725     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3726     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
3727 
3728     // Undo and check.
3729     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3730     CPPUNIT_ASSERT(pUndoMgr);
3731 
3732     pUndoMgr->Undo();
3733 
3734     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3735     CPPUNIT_ASSERT(pName);
3736     pCode = pName->GetCode();
3737 
3738     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3739     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
3740 
3741     // Undo again and check.
3742     pUndoMgr->Undo();
3743 
3744     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3745     CPPUNIT_ASSERT(pName);
3746     pCode = pName->GetCode();
3747 
3748     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3749     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3750 
3751     // Delete row 2-3.
3752     rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3753 
3754     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3755     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
3756 
3757     // Undo and check.
3758     pUndoMgr->Undo();
3759 
3760     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3761     CPPUNIT_ASSERT(pName);
3762     pCode = pName->GetCode();
3763 
3764     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3765     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3766 
3767     pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3768     CPPUNIT_ASSERT(pName2);
3769     pCode2 = pName2->GetCode();
3770 
3771     aExpr2 = pCode2->CreateString(m_pDoc, aCxt2, ScAddress(0,0,0));
3772     CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3773 
3774     m_pDoc->InsertTab(1, "test2");
3775 
3776     ScMarkData aMark2(MAXROW, MAXCOL);
3777     aMark2.SelectOneTable(1);
3778     rFunc.DeleteCells(ScRange(0,2,1,m_pDoc->MaxCol(),2,1), &aMark2, DelCellCmd::CellsUp, true);
3779 
3780     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3781     CPPUNIT_ASSERT(pName);
3782     pCode = pName->GetCode();
3783 
3784     aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3785     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3786 
3787     pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3788     CPPUNIT_ASSERT(pName2);
3789     pCode2 = pName2->GetCode();
3790 
3791     // Deleting a range the 'MyAddress' name points into due to its implicit
3792     // relative sheet reference to the sheet where used does not invalidate
3793     // the named expression because when updating the sheet reference is
3794     // relative to its base position on sheet 0 (same for the 'MyRange' range,
3795     // which is the reason why it is not updated either).
3796     // This is a tad confusing...
3797     aExpr2 = pCode2->CreateString(m_pDoc, aCxt2, ScAddress(0,0,0));
3798     CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3799 
3800     m_pDoc->DeleteTab(1);
3801     m_pDoc->DeleteTab(0);
3802 }
3803 
testFormulaRefUpdateNameCopySheet()3804 void Test::testFormulaRefUpdateNameCopySheet()
3805 {
3806     m_pDoc->InsertTab(0, "Test");
3807     m_pDoc->InsertTab(1, "Test2");
3808 
3809     bool bInserted = m_pDoc->InsertNewRangeName("RED", ScAddress(0,0,0), "$Test.$B$2");
3810     CPPUNIT_ASSERT(bInserted);
3811     bInserted = m_pDoc->InsertNewRangeName("BLUE", ScAddress(0,0,0), "$Test.$B$3");
3812     CPPUNIT_ASSERT(bInserted);
3813     m_pDoc->SetValue(1, 1, 0, 1);
3814     m_pDoc->SetValue(1, 2, 0, 2);
3815 
3816     // insert formula into Test2 that is =RED+BLUE
3817     m_pDoc->SetString(ScAddress(2,2,1), "=RED+BLUE");
3818 
3819     double nVal = m_pDoc->GetValue(2, 2, 1);
3820     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3821     m_pDoc->CopyTab(1, 0);
3822 
3823     nVal = m_pDoc->GetValue(2, 2, 2);
3824     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3825 
3826     nVal = m_pDoc->GetValue(2, 2, 0);
3827     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3828 
3829     m_pDoc->SetValue(1, 1, 1, 3);
3830 
3831     nVal = m_pDoc->GetValue(2, 2, 2);
3832     CPPUNIT_ASSERT_EQUAL(5.0, nVal);
3833 
3834     nVal = m_pDoc->GetValue(2, 2, 0);
3835     CPPUNIT_ASSERT_EQUAL(5.0, nVal);
3836 
3837     m_pDoc->DeleteTab(2);
3838     m_pDoc->DeleteTab(1);
3839     m_pDoc->DeleteTab(0);
3840 
3841     m_pDoc->InsertTab(0, "Test1");
3842     // Global name referencing sheet Test1.
3843     bInserted = m_pDoc->InsertNewRangeName("sheetnumber", ScAddress(0,0,0), "$Test1.$A$1");
3844     CPPUNIT_ASSERT(bInserted);
3845     m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
3846     m_pDoc->SetString(ScAddress(1,0,0), "=sheetnumber");
3847     nVal = m_pDoc->GetValue(1,0,0);
3848     CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet number should be 1", 1.0, nVal);
3849 
3850     // Copy sheet after.
3851     m_pDoc->CopyTab(0, 1);
3852     nVal = m_pDoc->GetValue(1,0,1);
3853     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 2", 2.0, nVal);
3854     nVal = m_pDoc->GetValue(1,0,0);
3855     CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 1", 1.0, nVal);
3856     const ScRangeData* pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
3857     CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
3858 
3859     // Copy sheet before, shifting following now two sheets.
3860     m_pDoc->CopyTab(0, 0);
3861     nVal = m_pDoc->GetValue(1,0,0);
3862     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
3863     pName = m_pDoc->GetRangeName(0)->findByUpperName("SHEETNUMBER");
3864     CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
3865     nVal = m_pDoc->GetValue(1,0,1);
3866     CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 2", 2.0, nVal);
3867     pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
3868     CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should not exist", !pName);
3869     nVal = m_pDoc->GetValue(1,0,2);
3870     CPPUNIT_ASSERT_EQUAL_MESSAGE("Old sheet number should be 3", 3.0, nVal);
3871     pName = m_pDoc->GetRangeName(2)->findByUpperName("SHEETNUMBER");
3872     CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should exist", pName);
3873 
3874     m_pDoc->DeleteTab(2);
3875     m_pDoc->DeleteTab(1);
3876     m_pDoc->DeleteTab(0);
3877 
3878     m_pDoc->InsertTab(0, "Test2");
3879     // Local name referencing sheet Test2.
3880     bInserted = m_pDoc->GetRangeName(0)->insert( new ScRangeData( m_pDoc, "localname", "$Test2.$A$1"));
3881     CPPUNIT_ASSERT(bInserted);
3882     m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
3883     m_pDoc->SetString(ScAddress(1,0,0), "=localname");
3884     nVal = m_pDoc->GetValue(1,0,0);
3885     CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 1", 1.0, nVal);
3886 
3887     // Insert sheet before and shift sheet with local name.
3888     m_pDoc->InsertTab(0, "Test1");
3889     pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
3890     CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
3891     nVal = m_pDoc->GetValue(1,0,1);
3892     CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 2", 2.0, nVal);
3893 
3894     // Copy sheet before, shifting following now two sheets.
3895     m_pDoc->CopyTab(1, 0);
3896     pName = m_pDoc->GetRangeName(0)->findByUpperName("LOCALNAME");
3897     CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
3898     nVal = m_pDoc->GetValue(1,0,0);
3899     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
3900     pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
3901     CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should not exist", !pName);
3902     pName = m_pDoc->GetRangeName(2)->findByUpperName("LOCALNAME");
3903     CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
3904     nVal = m_pDoc->GetValue(1,0,2);
3905     CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 3", 3.0, nVal);
3906 
3907     m_pDoc->DeleteTab(2);
3908     m_pDoc->DeleteTab(1);
3909     m_pDoc->DeleteTab(0);
3910     m_pDoc->SetRangeName(nullptr);
3911 
3912     // Test nested names during copying sheet.
3913 
3914     m_pDoc->InsertTab(0, "Test2");
3915     ScAddress aPos(0,0,0);
3916     bInserted = m_pDoc->InsertNewRangeName( "global", aPos, "$Test2.$A$1");
3917     CPPUNIT_ASSERT(bInserted);
3918     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local", aPos, "$Test2.$A$2");
3919     CPPUNIT_ASSERT(bInserted);
3920     bInserted = m_pDoc->InsertNewRangeName( "global_global", aPos, "global*100");
3921     CPPUNIT_ASSERT(bInserted);
3922     bInserted = m_pDoc->InsertNewRangeName( "global_local", aPos, "local*1000");
3923     CPPUNIT_ASSERT(bInserted);
3924     bInserted = m_pDoc->InsertNewRangeName( "global_unused", aPos, "$Test2.$A$1");
3925     CPPUNIT_ASSERT(bInserted);
3926     bInserted = m_pDoc->InsertNewRangeName( "global_unused_noref", aPos, "42");
3927     CPPUNIT_ASSERT(bInserted);
3928     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_global", aPos, "global*10000");
3929     CPPUNIT_ASSERT(bInserted);
3930     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_local", aPos, "local*100000");
3931     CPPUNIT_ASSERT(bInserted);
3932     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused", aPos, "$Test2.$A$2");
3933     CPPUNIT_ASSERT(bInserted);
3934     bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused_noref", aPos, "23");
3935     CPPUNIT_ASSERT(bInserted);
3936 
3937     m_pDoc->SetString(aPos, "=SHEET()");
3938     aPos.IncRow();
3939     m_pDoc->SetString(aPos, "=A1*10+SHEET()");
3940     aPos.IncRow();
3941     m_pDoc->SetString(aPos, "=global_global");
3942     aPos.IncRow();
3943     m_pDoc->SetString(aPos, "=global_local");
3944     aPos.IncRow();
3945     m_pDoc->SetString(aPos, "=local_global");
3946     aPos.IncRow();
3947     m_pDoc->SetString(aPos, "=local_local");
3948 
3949     testFormulaRefUpdateNameCopySheetCheckTab( 0, false);
3950 
3951     // Copy sheet after.
3952     m_pDoc->CopyTab(0, 1);
3953     testFormulaRefUpdateNameCopySheetCheckTab( 0, false);
3954     testFormulaRefUpdateNameCopySheetCheckTab( 1, true);
3955 
3956     // Copy sheet before, shifting following now two sheets.
3957     m_pDoc->CopyTab(1, 0);
3958     testFormulaRefUpdateNameCopySheetCheckTab( 0, true);
3959     testFormulaRefUpdateNameCopySheetCheckTab( 1, false);
3960     testFormulaRefUpdateNameCopySheetCheckTab( 2, true);
3961 
3962     m_pDoc->DeleteTab(2);
3963     m_pDoc->DeleteTab(1);
3964     m_pDoc->DeleteTab(0);
3965 }
3966 
testFormulaRefUpdateNameCopySheetCheckTab(SCTAB nTab,bool bCheckNames)3967 void Test::testFormulaRefUpdateNameCopySheetCheckTab( SCTAB nTab, bool bCheckNames )
3968 {
3969     if (bCheckNames)
3970     {
3971         const ScRangeData* pName;
3972         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL");
3973         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL should exist", pName);
3974         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("LOCAL");
3975         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL should exist", pName);
3976         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_GLOBAL");
3977         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_GLOBAL should exist", pName);
3978         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_LOCAL");
3979         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_LOCAL should exist", pName);
3980         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED");
3981         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED should exist", pName);
3982         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED_NOREF");
3983         CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED_NOREF should not exist", !pName);
3984         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_GLOBAL");
3985         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_GLOBAL should exist", pName);
3986         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_LOCAL");
3987         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_LOCAL should exist", pName);
3988         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED");
3989         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED should exist", pName);
3990         pName = m_pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED_NOREF");
3991         CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED_NOREF should exist", pName);
3992     }
3993 
3994     ScAddress aPos(0,0,0);
3995     aPos.SetRow(0);
3996     aPos.SetTab(nTab);
3997     int nSheet = nTab + 1;
3998     CPPUNIT_ASSERT_EQUAL( 1.0 * nSheet, m_pDoc->GetValue(aPos));
3999     aPos.IncRow();
4000     CPPUNIT_ASSERT_EQUAL( 11.0 * nSheet, m_pDoc->GetValue(aPos));
4001     aPos.IncRow();
4002     CPPUNIT_ASSERT_EQUAL( 100.0 * nSheet, m_pDoc->GetValue(aPos));
4003     aPos.IncRow();
4004     CPPUNIT_ASSERT_EQUAL( 11000.0 * nSheet, m_pDoc->GetValue(aPos));
4005     aPos.IncRow();
4006     CPPUNIT_ASSERT_EQUAL( 10000.0 * nSheet, m_pDoc->GetValue(aPos));
4007     aPos.IncRow();
4008     CPPUNIT_ASSERT_EQUAL( 1100000.0 * nSheet, m_pDoc->GetValue(aPos));
4009 }
4010 
testFormulaRefUpdateNameDelete()4011 void Test::testFormulaRefUpdateNameDelete()
4012 {
4013     m_pDoc->InsertTab(0, "Test");
4014 
4015     // Insert a new name 'MyRange' to reference B1
4016     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$1");
4017     CPPUNIT_ASSERT(bInserted);
4018 
4019     const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4020     CPPUNIT_ASSERT(pName);
4021 
4022     m_pDoc->DeleteCol(1, 0, 3, 0, 0, 1);
4023     const ScTokenArray* pCode = pName->GetCode();
4024     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4025     OUString aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
4026     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$1"), aExpr);
4027 
4028     m_pDoc->DeleteTab(0);
4029 }
4030 
testFormulaRefUpdateValidity()4031 void Test::testFormulaRefUpdateValidity()
4032 {
4033     struct {
4034 
4035         bool checkList( std::vector<ScTypedStrData>& rList )
4036         {
4037             double aExpected[] = { 1.0, 2.0, 3.0 }; // must be sorted.
4038             size_t nCheckSize = SAL_N_ELEMENTS(aExpected);
4039 
4040             if (rList.size() != nCheckSize)
4041             {
4042                 cerr << "List size is not what is expected." << endl;
4043                 return false;
4044             }
4045 
4046             std::sort(rList.begin(), rList.end(), ScTypedStrData::LessCaseSensitive());
4047 
4048             for (size_t i = 0; i < nCheckSize; ++i)
4049             {
4050                 if (aExpected[i] != rList[i].GetValue())
4051                 {
4052                     cerr << "Incorrect value at position " << i
4053                         << ": expected=" << aExpected[i] << ", actual=" << rList[i].GetValue() << endl;
4054                     return false;
4055                 }
4056             }
4057 
4058             return true;
4059         }
4060 
4061     } aCheck;
4062 
4063     setExpandRefs(false);
4064     setCalcAsShown(m_pDoc, true);
4065 
4066     m_pDoc->InsertTab(0, "Formula");
4067 
4068     // Set values in C2:C4.
4069     m_pDoc->SetValue(ScAddress(2,1,0), 1.0);
4070     m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
4071     m_pDoc->SetValue(ScAddress(2,3,0), 3.0);
4072 
4073     // Set validity in A2.
4074     ScValidationData aData(
4075         SC_VALID_LIST, ScConditionMode::Equal, "C2:C4", "", m_pDoc, ScAddress(0,1,0), "", "",
4076         m_pDoc->GetGrammar(), m_pDoc->GetGrammar());
4077 
4078     sal_uLong nIndex = m_pDoc->AddValidationEntry(aData);
4079     SfxUInt32Item aItem(ATTR_VALIDDATA, nIndex);
4080 
4081     ScPatternAttr aNewAttrs(
4082         std::make_unique<SfxItemSet>(*m_pDoc->GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{}));
4083     aNewAttrs.GetItemSet().Put(aItem);
4084 
4085     m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
4086 
4087     const ScValidationData* pData = m_pDoc->GetValidationEntry(nIndex);
4088     CPPUNIT_ASSERT(pData);
4089 
4090     // Make sure the list is correct.
4091     std::vector<ScTypedStrData> aList;
4092     pData->FillSelectionList(aList, ScAddress(0,1,0));
4093     bool bGood = aCheck.checkList(aList);
4094     CPPUNIT_ASSERT_MESSAGE("Initial list is incorrect.", bGood);
4095 
4096     ScDocFunc& rFunc = getDocShell().GetDocFunc();
4097     ScMarkData aMark(MAXROW, MAXCOL);
4098     aMark.SelectOneTable(0);
4099 
4100     // Insert a new column at Column B, to move the list from C2:C4 to D2:D4.
4101     bool bInserted = rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, true, true);
4102     CPPUNIT_ASSERT_MESSAGE("Column insertion failed.", bInserted);
4103     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4104     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4105     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,3,0)));
4106 
4107     // Check the list values again.
4108     aList.clear();
4109     pData->FillSelectionList(aList, ScAddress(0,1,0));
4110     bGood = aCheck.checkList(aList);
4111     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after column insertion.", bGood);
4112 
4113     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4114     CPPUNIT_ASSERT(pUndoMgr);
4115 
4116     // Undo and check the list content again.  The list moves back to C2:C4 after the undo.
4117     pUndoMgr->Undo();
4118     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4119     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4120     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4121 
4122     aList.clear();
4123     pData->FillSelectionList(aList, ScAddress(0,1,0));
4124     bGood = aCheck.checkList(aList);
4125     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of column insertion.", bGood);
4126 
4127     // Move C2:C4 to E5:E7.
4128     bool bMoved = rFunc.MoveBlock(ScRange(2,1,0,2,3,0), ScAddress(4,4,0), false, true, false, true);
4129     CPPUNIT_ASSERT(bMoved);
4130     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4,4,0)));
4131     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,5,0)));
4132     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(4,6,0)));
4133 
4134     // Check the list again after the move.
4135     aList.clear();
4136     pData->FillSelectionList(aList, ScAddress(0,1,0));
4137     bGood = aCheck.checkList(aList);
4138     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after moving C2:C4 to E5:E7.", bGood);
4139 
4140     // Undo the move and check.  The list should be back to C2:C4.
4141     pUndoMgr->Undo();
4142     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4143     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4144     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4145 
4146     aList.clear();
4147     pData->FillSelectionList(aList, ScAddress(0,1,0));
4148     bGood = aCheck.checkList(aList);
4149     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of the move.", bGood);
4150 
4151     m_pDoc->DeleteTab(0);
4152 }
4153 
testTokenArrayRefUpdateMove()4154 void Test::testTokenArrayRefUpdateMove()
4155 {
4156     m_pDoc->InsertTab(0, "Sheet1");
4157     m_pDoc->InsertTab(1, "Sheet2");
4158 
4159     ScAddress aPos(0,0,0); // A1
4160 
4161     sc::TokenStringContext aCxt(m_pDoc, m_pDoc->GetGrammar());
4162 
4163     // Emulate cell movement from Sheet1.C3 to Sheet2.C3.
4164     sc::RefUpdateContext aRefCxt(*m_pDoc);
4165     aRefCxt.meMode = URM_MOVE;
4166     aRefCxt.maRange = ScAddress(2,2,1); // C3 on Sheet2.
4167     aRefCxt.mnTabDelta = -1;
4168 
4169     std::vector<OUString> aTests = {
4170         "B1*C1",
4171         "SUM(B1:C1)",
4172         "$Sheet1.B1",
4173         "SUM(Sheet1.B1:Sheet2.B1)"
4174     };
4175 
4176     // Since C3 is not referenced in any of the above formulas, moving C3 from
4177     // Sheet1 to Sheet2 should NOT change the displayed formula string at all.
4178 
4179     for (const OUString& aTest : aTests)
4180     {
4181         ScCompiler aComp(m_pDoc, aPos, m_pDoc->GetGrammar());
4182         std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aTest));
4183 
4184         OUString aStr = pArray->CreateString(m_pDoc, aCxt, aPos);
4185 
4186         CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4187 
4188         // This formula cell isn't moving its position. The displayed formula
4189         // string should not change.
4190         pArray->AdjustReferenceOnMove(aRefCxt, aPos, aPos);
4191 
4192         aStr = pArray->CreateString(m_pDoc, aCxt, aPos);
4193         CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4194     }
4195 
4196     m_pDoc->DeleteTab(1);
4197     m_pDoc->DeleteTab(0);
4198 }
4199 
testMultipleOperations()4200 void Test::testMultipleOperations()
4201 {
4202     m_pDoc->InsertTab(0, "MultiOp");
4203 
4204     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4205 
4206     // Insert the reference formula at top row.
4207     m_pDoc->SetValue(ScAddress(0,0,0), 1);
4208     m_pDoc->SetString(ScAddress(1,0,0), "=A1*10");
4209     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4210 
4211     // Insert variable inputs in A3:A5.
4212     m_pDoc->SetValue(ScAddress(0,2,0), 2);
4213     m_pDoc->SetValue(ScAddress(0,3,0), 3);
4214     m_pDoc->SetValue(ScAddress(0,4,0), 4);
4215 
4216     // Set multiple operations range.
4217     ScTabOpParam aParam;
4218     aParam.aRefFormulaCell = ScRefAddress(1,0,0);
4219     aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4220     aParam.aRefColCell = ScRefAddress(0,0,0);
4221     ScMarkData aMark(MAXROW, MAXCOL);
4222     aMark.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
4223     m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4224     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(1,2,0));
4225     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,3,0));
4226     CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,4,0));
4227 
4228     // Clear A3:B5.
4229     clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
4230 
4231     // This time, use indirect reference formula cell.
4232     m_pDoc->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
4233     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4234 
4235     // Insert variable inputs in A3:A5.
4236     m_pDoc->SetValue(ScAddress(0,2,0), 3);
4237     m_pDoc->SetValue(ScAddress(0,3,0), 4);
4238     m_pDoc->SetValue(ScAddress(0,4,0), 5);
4239 
4240     // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
4241     aParam.aRefFormulaCell.Set(2,0,0,false,false,false);
4242     aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4243     m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4244     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,2,0));
4245     CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,3,0));
4246     CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(1,4,0));
4247 
4248     m_pDoc->DeleteTab(0);
4249 }
4250 
testFuncCOLUMN()4251 void Test::testFuncCOLUMN()
4252 {
4253     m_pDoc->InsertTab(0, "Formula");
4254     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4255 
4256     m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
4257     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4258 
4259     m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4260     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4261 
4262     // Move the formula cell with COLUMN() function to change its value.
4263     m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4264     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(6,10,0)));
4265 
4266     // The cell that references the moved cell should update its value as well.
4267     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4268 
4269     // Move the column in the other direction.
4270     m_pDoc->DeleteCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4271 
4272     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4273 
4274     // The cell that references the moved cell should update its value as well.
4275     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4276 
4277     m_pDoc->DeleteTab(0);
4278 }
4279 
testFuncCOUNT()4280 void Test::testFuncCOUNT()
4281 {
4282     m_pDoc->InsertTab(0, "Formula");
4283     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4284 
4285     m_pDoc->SetValue(ScAddress(0,0,0), 2); // A1
4286     m_pDoc->SetValue(ScAddress(0,1,0), 4); // A2
4287     m_pDoc->SetValue(ScAddress(0,2,0), 6); // A3
4288 
4289     ScAddress aPos(1,0,0);
4290     m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
4291     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
4292 
4293     aPos.IncRow();
4294     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
4295     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
4296 
4297     aPos.IncRow();
4298     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
4299     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
4300 
4301     aPos.IncRow();
4302     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
4303     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
4304 
4305     // Matrix in C1.
4306     ScMarkData aMark(MAXROW, MAXCOL);
4307     aMark.SelectOneTable(0);
4308     m_pDoc->InsertMatrixFormula(2, 0, 2, 0, aMark, "=COUNT(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4309     // Check that the #VALUE! error of "a" not found in "b" is not counted.
4310     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4311 
4312     // Matrix in C3.
4313     m_pDoc->InsertMatrixFormula(2, 2, 2, 2, aMark, "=COUNTA(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4314     // Check that the #VALUE! error of "a" not found in "b" is counted.
4315     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4316 
4317     m_pDoc->DeleteTab(0);
4318 }
4319 
testFuncCOUNTBLANK()4320 void Test::testFuncCOUNTBLANK()
4321 {
4322     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4323     m_pDoc->InsertTab(0, "Formula");
4324 
4325     const char* aData[][4] = {
4326         { "1", nullptr, "=B1", "=\"\"" },
4327         { "2", nullptr, "=B2", "=\"\"" },
4328         { "A", nullptr, "=B3", "=\"\"" },
4329         { "B", nullptr, "=B4", "=D3" },
4330         {   nullptr, nullptr, "=B5", "=D4" },
4331         { "=COUNTBLANK(A1:A5)", "=COUNTBLANK(B1:B5)", "=COUNTBLANK(C1:C5)", "=COUNTBLANK(D1:D5)" }
4332     };
4333 
4334     ScAddress aPos(0,0,0);
4335     ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4336     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4337 
4338     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4339     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,5,0)));
4340     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,5,0)));
4341     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(3,5,0)));
4342 
4343     // Test single cell reference cases.
4344 
4345     clearSheet(m_pDoc, 0);
4346 
4347     const char* aData2[][2] = {
4348         { "1",     "=COUNTBLANK(A1)" },
4349         { "A",     "=COUNTBLANK(A2)" },
4350         {   nullptr,     "=COUNTBLANK(A3)" },
4351         { "=\"\"", "=COUNTBLANK(A4)" },
4352         { "=A4"  , "=COUNTBLANK(A5)" },
4353     };
4354 
4355     aRange = insertRangeData(m_pDoc, aPos, aData2, SAL_N_ELEMENTS(aData2));
4356     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4357 
4358     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4359     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4360     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4361     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
4362     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
4363 
4364     m_pDoc->DeleteTab(0);
4365 }
4366 
testFuncROW()4367 void Test::testFuncROW()
4368 {
4369     m_pDoc->InsertTab(0, "Formula");
4370     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4371 
4372     m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
4373     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4374 
4375     m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4376     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4377 
4378     // Insert 2 new rows at row 4.
4379     m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4380     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(5,12,0)));
4381 
4382     // The cell that references the moved cell should update its value as well.
4383     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4384 
4385     // Delete 2 rows to move it back.
4386     m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4387 
4388     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4389 
4390     // The cell that references the moved cell should update its value as well.
4391     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4392 
4393     // Clear sheet and start over.
4394     clearSheet(m_pDoc, 0);
4395 
4396     m_pDoc->SetString(ScAddress(0,1,0), "=ROW(A5)");
4397     m_pDoc->SetString(ScAddress(1,1,0), "=ROW(B5)");
4398     m_pDoc->SetString(ScAddress(1,2,0), "=ROW(B6)");
4399     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4400     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4401     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4402 
4403     // B2:B3 should be shared.
4404     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
4405     CPPUNIT_ASSERT(pFC);
4406     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
4407     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
4408 
4409     // Insert a new row at row 4.
4410     ScDocFunc& rFunc = getDocShell().GetDocFunc();
4411     ScMarkData aMark(MAXROW, MAXCOL);
4412     aMark.SelectOneTable(0);
4413     rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
4414     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "ROW(A6)", "Wrong formula!");
4415     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "ROW(B6)", "Wrong formula!");
4416     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "ROW(B7)", "Wrong formula!");
4417 
4418     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4419     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4420     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4421 
4422     m_pDoc->DeleteTab(0);
4423 }
4424 
testFuncSUM()4425 void Test::testFuncSUM()
4426 {
4427     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4428                             m_pDoc->InsertTab (0, "foo"));
4429 
4430     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
4431 
4432     // Single argument case.
4433     m_pDoc->SetValue(ScAddress(0,0,0), 1);
4434     m_pDoc->SetValue(ScAddress(0,1,0), 1);
4435     m_pDoc->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
4436     m_pDoc->CalcAll();
4437     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4438 
4439     // Multiple argument case.
4440     m_pDoc->SetValue(ScAddress(0,0,0), 1);
4441     m_pDoc->SetValue(ScAddress(0,1,0), 22);
4442     m_pDoc->SetValue(ScAddress(0,2,0), 4);
4443     m_pDoc->SetValue(ScAddress(0,3,0), 5);
4444     m_pDoc->SetValue(ScAddress(0,4,0), 6);
4445 
4446     m_pDoc->SetValue(ScAddress(1,0,0), 3);
4447     m_pDoc->SetValue(ScAddress(1,1,0), 4);
4448     m_pDoc->SetValue(ScAddress(1,2,0), 5);
4449     m_pDoc->SetValue(ScAddress(1,3,0), 6);
4450     m_pDoc->SetValue(ScAddress(1,4,0), 7);
4451 
4452     m_pDoc->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
4453     m_pDoc->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
4454     m_pDoc->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
4455     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(ScAddress(3,0,0)));
4456     CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4457     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4458 
4459     // Clear and start over.
4460     clearRange(m_pDoc, ScRange(0,0,0,3,m_pDoc->MaxRow(),0));
4461 
4462     // SUM needs to take the first error in case the range contains an error.
4463     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4464     m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
4465     m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
4466     m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
4467     CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4468 
4469     // Set #DIV/0! error to A3. A4 should also inherit this error.
4470     m_pDoc->SetString(ScAddress(0,2,0), "=1/0");
4471     FormulaError nErr = m_pDoc->GetErrCode(ScAddress(0,2,0));
4472     CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell should have a division by zero error.",
4473                            int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4474     nErr = m_pDoc->GetErrCode(ScAddress(0,3,0));
4475     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM should have also inherited a div-by-zero error.",
4476                            int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4477 
4478     // Set #NA! to A2. A4 should now inherit this error.
4479     m_pDoc->SetString(ScAddress(0,1,0), "=NA()");
4480     nErr = m_pDoc->GetErrCode(ScAddress(0,1,0));
4481     CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr != FormulaError::NONE);
4482     CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should have inherited the same error as A2.",
4483                            static_cast<int>(nErr), static_cast<int>(m_pDoc->GetErrCode(ScAddress(0,3,0))));
4484 
4485     m_pDoc->DeleteTab(0);
4486 }
4487 
testFuncPRODUCT()4488 void Test::testFuncPRODUCT()
4489 {
4490     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4491 
4492     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab(0, "foo"));
4493 
4494     ScAddress aPos(3, 0, 0);
4495     m_pDoc->SetValue(0, 0, 0, 3.0); // A1
4496     m_pDoc->SetString(aPos, "=PRODUCT(A1)");
4497     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 3.0, m_pDoc->GetValue(aPos));
4498     m_pDoc->SetValue(0, 0, 0, -3.0); // A1
4499     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -3.0, m_pDoc->GetValue(aPos));
4500     m_pDoc->SetString(aPos, "=PRODUCT(B1)");
4501     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos));
4502     m_pDoc->SetValue(1, 0, 0, 10.0); // B1
4503     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 10.0, m_pDoc->GetValue(aPos));
4504 
4505     m_pDoc->SetString(aPos, "=PRODUCT(A1:C3)");
4506     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -30.0, m_pDoc->GetValue(aPos));
4507     m_pDoc->SetValue(1, 1, 0, -1.0); // B2
4508     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 30.0, m_pDoc->GetValue(aPos));
4509     m_pDoc->SetValue(2, 0, 0, 4.0); // C1
4510     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 120.0, m_pDoc->GetValue(aPos));
4511     m_pDoc->SetValue(0, 1, 0, -2.0); // A2
4512     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -240.0, m_pDoc->GetValue(aPos));
4513     m_pDoc->SetValue(2, 1, 0, 8.0); // C2
4514     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -1920.0, m_pDoc->GetValue(aPos));
4515     m_pDoc->SetValue(0, 2, 0, 0.2); // A3
4516     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -384.0, m_pDoc->GetValue(aPos), 10e-4);
4517     m_pDoc->SetValue(1, 2, 0, -0.25); // B3
4518     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 96.0, m_pDoc->GetValue(aPos), 10e-4);
4519     m_pDoc->SetValue(2, 2, 0, -0.125); // C3
4520     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -12.0, m_pDoc->GetValue(aPos), 10e-4);
4521     m_pDoc->SetValue(2, 2, 0, 0.0); // C3
4522     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos), 10e-4);
4523 
4524     m_pDoc->SetString(aPos, "=PRODUCT({2;3;4})");
4525     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 24.0, m_pDoc->GetValue(aPos));
4526     m_pDoc->SetString(aPos, "=PRODUCT({2;-2;2})");
4527     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -8.0, m_pDoc->GetValue(aPos));
4528     m_pDoc->SetString(aPos, "=PRODUCT({8;0.125;-1})");
4529     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -1.0, m_pDoc->GetValue(aPos));
4530 
4531     m_pDoc->SetString(aPos, "=PRODUCT({2;3};{4;5})");
4532     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 120.0, m_pDoc->GetValue(aPos));
4533     m_pDoc->SetString(aPos, "=PRODUCT({10;-8};{3;-1};{15;30};{7})");
4534     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 756000.0, m_pDoc->GetValue(aPos));
4535     m_pDoc->SetString(aPos, "=PRODUCT({10;-0.1;8};{0.125;4;0.25;2};{0.5};{1};{-1})");
4536     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 1.0, m_pDoc->GetValue(aPos));
4537 
4538     m_pDoc->DeleteTab(0);
4539 }
4540 
testFuncSUMPRODUCT()4541 void Test::testFuncSUMPRODUCT()
4542 {
4543     m_pDoc->InsertTab(0, "Test");
4544 
4545     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4546 
4547     ScAddress aPos(0,0,0);
4548     m_pDoc->SetString(aPos, "=SUMPRODUCT(B1:B3;C1:C3)");
4549     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
4550     m_pDoc->SetValue(ScAddress(2,0,0),  1.0); // C1
4551     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
4552     m_pDoc->SetValue(ScAddress(1,0,0),  1.0); // B1
4553     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
4554     m_pDoc->SetValue(ScAddress(1,1,0),  2.0); // B2
4555     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
4556     m_pDoc->SetValue(ScAddress(2,1,0),  3.0); // C2
4557     CPPUNIT_ASSERT_EQUAL(7.0,  m_pDoc->GetValue(aPos));
4558     m_pDoc->SetValue(ScAddress(2,2,0), -2.0); // C3
4559     CPPUNIT_ASSERT_EQUAL(7.0,  m_pDoc->GetValue(aPos));
4560     m_pDoc->SetValue(ScAddress(1,2,0),  5.0); // B3
4561     CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc->GetValue(aPos));
4562 
4563     // Force an error in C2 and test ForcedArray matrix error propagation.
4564     m_pDoc->SetString( 2, 1, 0, "=1/0");
4565     FormulaError nError = m_pDoc->GetErrCode(aPos);
4566     CPPUNIT_ASSERT_MESSAGE("Formula result should be a propagated error", nError != FormulaError::NONE);
4567 
4568     // Test ForceArray propagation of SUMPRODUCT parameters to ABS and + operator.
4569     // => ABS({-3,4})*({-3,4}+{-3,4}) => {3,4}*{-6,8} => {-18,32} => 14
4570     m_pDoc->SetValue(ScAddress(4,0,0), -3.0); // E1
4571     m_pDoc->SetValue(ScAddress(4,1,0),  4.0); // E2
4572     // Non-intersecting formula in F3.
4573     m_pDoc->SetString(ScAddress(5,2,0), "=SUMPRODUCT(ABS(E1:E2);E1:E2+E1:E2)");
4574     CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(ScAddress(5,2,0)));
4575 
4576     m_pDoc->DeleteTab(0);
4577 }
4578 
testFuncSUMXMY2()4579 void Test::testFuncSUMXMY2()
4580 {
4581     m_pDoc->InsertTab(0, "Test SumXMY2");
4582 
4583     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4584 
4585     ScAddress aPos(0,0,0);
4586     m_pDoc->SetString(aPos, "=SUMXMY2(B1:B3;C1:C3)");
4587     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
4588     m_pDoc->SetValue(ScAddress(1,0,0),  1.0); // B1
4589     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
4590     m_pDoc->SetValue(ScAddress(1,1,0),  2.0); // B2
4591     CPPUNIT_ASSERT_EQUAL(5.0,  m_pDoc->GetValue(aPos));
4592     m_pDoc->SetValue(ScAddress(1,2,0),  3.0); // B3
4593     CPPUNIT_ASSERT_EQUAL(14.0,  m_pDoc->GetValue(aPos));
4594     m_pDoc->SetValue(ScAddress(2,0,0), -1.0); // C1
4595     CPPUNIT_ASSERT_EQUAL(17.0,  m_pDoc->GetValue(aPos));
4596     m_pDoc->SetValue(ScAddress(2,1,0),  3.0); // C2
4597     CPPUNIT_ASSERT_EQUAL(14.0,  m_pDoc->GetValue(aPos));
4598     m_pDoc->SetValue(ScAddress(2,2,0),  1.0); // C3
4599     CPPUNIT_ASSERT_EQUAL(9.0,  m_pDoc->GetValue(aPos));
4600 
4601     double result = 0.0;
4602     m_pDoc->SetString(0, 4, 0, "=SUMXMY2({2;3;4};{4;3;2})");
4603     m_pDoc->GetValue(0, 4, 0, result);
4604     ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of SUMXMY2 with inline arrays failed", 8.0, result);
4605 
4606     m_pDoc->DeleteTab(0);
4607 }
4608 
testFuncMIN()4609 void Test::testFuncMIN()
4610 {
4611     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4612     m_pDoc->InsertTab(0, "Formula");
4613 
4614     // A1:A2
4615     m_pDoc->SetString(ScAddress(0,0,0), "a");
4616     m_pDoc->SetString(ScAddress(0,1,0), "b");
4617 
4618     // B1:B2
4619     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
4620     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
4621 
4622     // Matrix in C1:C2.
4623     ScMarkData aMark(MAXROW, MAXCOL);
4624     aMark.SelectOneTable(0);
4625     m_pDoc->InsertMatrixFormula(2, 0, 2, 1, aMark, "=MIN(IF(A1:A2=\"c\";B1:B2))");
4626 
4627     // Formula cell in C1:C2 should be a 1x2 matrix array.
4628     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
4629     CPPUNIT_ASSERT(pFC);
4630     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be an array.", ScMatrixMode::Formula, pFC->GetMatrixFlag());
4631 
4632     SCCOL nCols;
4633     SCROW nRows;
4634     pFC->GetMatColsRows(nCols, nRows);
4635     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
4636     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRows);
4637 
4638     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C1 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,0,0))));
4639     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C2 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,1,0))));
4640 
4641     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4642     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4643 
4644     // Inline array input (A4).
4645     m_pDoc->SetString(ScAddress(0,3,0), "=MIN({-2;4;3})");
4646     CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4647 
4648     // Add more values to B3:B4.
4649     m_pDoc->SetValue(ScAddress(1,2,0),  20.0);
4650     m_pDoc->SetValue(ScAddress(1,3,0), -20.0);
4651 
4652     // Get the MIN of B1:B4.
4653     m_pDoc->SetString(ScAddress(2,4,0), "=MIN(B1:B4)");
4654     CPPUNIT_ASSERT_EQUAL(-20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
4655 
4656     m_pDoc->DeleteTab(0);
4657 }
4658 
testFuncN()4659 void Test::testFuncN()
4660 {
4661     OUString const aTabName("foo");
4662     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4663                             m_pDoc->InsertTab (0, aTabName));
4664 
4665     double result;
4666 
4667     // Clear the area first.
4668     clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
4669 
4670     // Put values to reference.
4671     double val = 0;
4672     m_pDoc->SetValue(0, 0, 0, val);
4673     m_pDoc->SetString(0, 2, 0, "Text");
4674     val = 1;
4675     m_pDoc->SetValue(0, 3, 0, val);
4676     val = -1;
4677     m_pDoc->SetValue(0, 4, 0, val);
4678     val = 12.3;
4679     m_pDoc->SetValue(0, 5, 0, val);
4680     m_pDoc->SetString(0, 6, 0, "'12.3");
4681 
4682     // Cell references
4683     m_pDoc->SetString(1, 0, 0, "=N(A1)");
4684     m_pDoc->SetString(1, 1, 0, "=N(A2)");
4685     m_pDoc->SetString(1, 2, 0, "=N(A3)");
4686     m_pDoc->SetString(1, 3, 0, "=N(A4)");
4687     m_pDoc->SetString(1, 4, 0, "=N(A5)");
4688     m_pDoc->SetString(1, 5, 0, "=N(A6)");
4689     m_pDoc->SetString(1, 6, 0, "=N(A9)");
4690 
4691     // In-line values
4692     m_pDoc->SetString(1, 7, 0, "=N(0)");
4693     m_pDoc->SetString(1, 8, 0, "=N(1)");
4694     m_pDoc->SetString(1, 9, 0, "=N(-1)");
4695     m_pDoc->SetString(1, 10, 0, "=N(123)");
4696     m_pDoc->SetString(1, 11, 0, "=N(\"\")");
4697     m_pDoc->SetString(1, 12, 0, "=N(\"12\")");
4698     m_pDoc->SetString(1, 13, 0, "=N(\"foo\")");
4699 
4700     // Range references
4701     m_pDoc->SetString(2, 2, 0, "=N(A1:A8)");
4702     m_pDoc->SetString(2, 3, 0, "=N(A1:A8)");
4703     m_pDoc->SetString(2, 4, 0, "=N(A1:A8)");
4704     m_pDoc->SetString(2, 5, 0, "=N(A1:A8)");
4705 
4706     // Calculate and check the results.
4707     m_pDoc->CalcAll();
4708     double checks1[] = {
4709         0, 0,  0,    1, -1, 12.3, 0, // cell reference
4710         0, 1, -1, 123,  0,    0, 0   // in-line values
4711     };
4712     for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
4713     {
4714         m_pDoc->GetValue(1, i, 0, result);
4715         bool bGood = result == checks1[i];
4716         if (!bGood)
4717         {
4718             cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
4719             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4720         }
4721     }
4722     double checks2[] = {
4723         0, 1, -1, 12.3               // range references
4724     };
4725     for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
4726     {
4727         m_pDoc->GetValue(1, i+2, 0, result);
4728         bool bGood = result == checks2[i];
4729         if (!bGood)
4730         {
4731             cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
4732             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4733         }
4734     }
4735 
4736     m_pDoc->DeleteTab(0);
4737 }
4738 
testFuncCOUNTIF()4739 void Test::testFuncCOUNTIF()
4740 {
4741     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4742 
4743     // COUNTIF (test case adopted from OOo i#36381)
4744 
4745     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4746                             m_pDoc->InsertTab (0, "foo"));
4747 
4748     // Empty A1:A39 first.
4749     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
4750 
4751     // Raw data (rows 1 through 9)
4752     const char* aData[] = {
4753         "1999",
4754         "2000",
4755         "0",
4756         "0",
4757         "0",
4758         "2002",
4759         "2001",
4760         "X",
4761         "2002"
4762     };
4763 
4764     SCROW nRows = SAL_N_ELEMENTS(aData);
4765     for (SCROW i = 0; i < nRows; ++i)
4766         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
4767 
4768     printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
4769 
4770     // formulas and results
4771     static const struct {
4772         const char* pFormula; double fResult;
4773     } aChecks[] = {
4774         { "=COUNTIF(A1:A12;1999)",       1 },
4775         { "=COUNTIF(A1:A12;2002)",       2 },
4776         { "=COUNTIF(A1:A12;1998)",       0 },
4777         { "=COUNTIF(A1:A12;\">=1999\")", 5 },
4778         { "=COUNTIF(A1:A12;\">1999\")",  4 },
4779         { "=COUNTIF(A1:A12;\"<2001\")",  5 },
4780         { "=COUNTIF(A1:A12;\">0\")",     5 },
4781         { "=COUNTIF(A1:A12;\">=0\")",    8 },
4782         { "=COUNTIF(A1:A12;0)",          3 },
4783         { "=COUNTIF(A1:A12;\"X\")",      1 },
4784         { "=COUNTIF(A1:A12;)",           3 }
4785     };
4786 
4787     nRows = SAL_N_ELEMENTS(aChecks);
4788     for (SCROW i = 0; i < nRows; ++i)
4789     {
4790         SCROW nRow = 20 + i;
4791         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
4792     }
4793 
4794     for (SCROW i = 0; i < nRows; ++i)
4795     {
4796         double result;
4797         SCROW nRow = 20 + i;
4798         m_pDoc->GetValue(0, nRow, 0, result);
4799         bool bGood = result == aChecks[i].fResult;
4800         if (!bGood)
4801         {
4802             cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
4803                 << "  expected=" << aChecks[i].fResult << "  actual=" << result << endl;
4804             CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
4805         }
4806     }
4807 
4808     // Don't count empty strings when searching for a number.
4809 
4810     // Clear A1:A2.
4811     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
4812 
4813     m_pDoc->SetString(0, 0, 0, "=\"\"");
4814     m_pDoc->SetString(0, 1, 0, "=COUNTIF(A1;1)");
4815 
4816     double result = m_pDoc->GetValue(0, 1, 0);
4817     ASSERT_DOUBLES_EQUAL_MESSAGE("We shouldn't count empty string as valid number.", 0.0, result);
4818 
4819     // Another test case adopted from fdo#77039.
4820     clearSheet(m_pDoc, 0);
4821 
4822     // Set formula cells with blank results in A1:A4.
4823     for (SCROW i = 0; i <=3; ++i)
4824         m_pDoc->SetString(ScAddress(0,i,0), "=\"\"");
4825 
4826     // Insert formula into A5 to count all cells with empty strings.
4827     m_pDoc->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
4828 
4829     // We should correctly count with empty string key.
4830     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,4,0)));
4831 
4832     // Another test case adopted from tdf#99291, empty array elements should
4833     // not match empty cells, but cells with 0.
4834     clearSheet(m_pDoc, 0);
4835     ScMarkData aMark(MAXROW, MAXCOL);
4836     aMark.SelectOneTable(0);
4837     m_pDoc->InsertMatrixFormula(0,0, 0,1, aMark, "=COUNTIF(B1:B5;C1:C2)");
4838     // As we will be testing for 0.0 values, check that formulas are actually present.
4839     OUString aFormula;
4840     m_pDoc->GetFormula(0,0,0, aFormula);
4841     CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
4842     m_pDoc->GetFormula(0,1,0, aFormula);
4843     CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
4844     // The 0.0 results expected.
4845     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4846     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4847     // 0.0 in B2, 1.0 in B3 and B4
4848     m_pDoc->SetValue( ScAddress(1,1,0), 0.0);
4849     m_pDoc->SetValue( ScAddress(1,2,0), 1.0);
4850     m_pDoc->SetValue( ScAddress(1,3,0), 1.0);
4851     // Matched by 0.0 produced by empty cell in array, and 1.0 in C2.
4852     m_pDoc->SetValue( ScAddress(2,1,0), 1.0);
4853     CPPUNIT_ASSERT_EQUAL_MESSAGE("One cell with 0.0",  1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
4854     CPPUNIT_ASSERT_EQUAL_MESSAGE("Two cells with 1.0", 2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4855 
4856     m_pDoc->DeleteTab(0);
4857 }
4858 
testFuncIF()4859 void Test::testFuncIF()
4860 {
4861     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4862 
4863     m_pDoc->InsertTab(0, "Formula");
4864 
4865     m_pDoc->SetString(ScAddress(0,0,0), "=IF(B1=2;\"two\";\"not two\")");
4866     CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
4867     m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
4868     CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
4869     m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
4870     CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
4871 
4872     // Test nested IF in array/matrix if the nested IF condition is a scalar.
4873     ScMarkData aMark(MAXROW, MAXCOL);
4874     aMark.SelectOneTable(0);
4875     m_pDoc->InsertMatrixFormula(0,2, 1,2, aMark, "=IF({1;0};IF(1;23);42)");
4876     // Results must be 23 and 42.
4877     CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4878     CPPUNIT_ASSERT_EQUAL(42.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4879 
4880     // Test nested IF in array/matrix if nested IF conditions are range
4881     // references, data in A5:C8, matrix formula in D4 so there is no
4882     // implicit intersection between formula and ranges.
4883     {
4884         const char* aData[][3] = {
4885             { "1", "1", "16" },
4886             { "0", "1", "32" },
4887             { "1", "0", "64" },
4888             { "0", "0", "128" }
4889         };
4890         ScAddress aPos(0,4,0);
4891         ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4892         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4893     }
4894     m_pDoc->InsertMatrixFormula(3,3, 3,3, aMark, "=SUM(IF(A5:A8;IF(B5:B8;C5:C8;0);0))");
4895     // Result must be 16, only the first row matches all criteria.
4896     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(3,3,0)));
4897 
4898     // A11:B11
4899     // Test nested IF in array/matrix if the nested IF has no Else path.
4900     m_pDoc->InsertMatrixFormula(0,10, 1,10, aMark, "=IF(IF({1;0};12);34;56)");
4901     // Results must be 34 and 56.
4902     CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(0,10,0)));
4903     CPPUNIT_ASSERT_EQUAL(56.0, m_pDoc->GetValue(ScAddress(1,10,0)));
4904 
4905     m_pDoc->DeleteTab(0);
4906 }
4907 
testFuncCHOOSE()4908 void Test::testFuncCHOOSE()
4909 {
4910     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4911 
4912     m_pDoc->InsertTab(0, "Formula");
4913 
4914     m_pDoc->SetString(ScAddress(0,0,0), "=CHOOSE(B1;\"one\";\"two\";\"three\")");
4915     FormulaError nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
4916     CPPUNIT_ASSERT_MESSAGE("Formula result should be an error since B1 is still empty.", nError != FormulaError::NONE);
4917     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
4918     CPPUNIT_ASSERT_EQUAL(OUString("one"), m_pDoc->GetString(ScAddress(0,0,0)));
4919     m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
4920     CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
4921     m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
4922     CPPUNIT_ASSERT_EQUAL(OUString("three"), m_pDoc->GetString(ScAddress(0,0,0)));
4923     m_pDoc->SetValue(ScAddress(1,0,0), 4.0);
4924     nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
4925     CPPUNIT_ASSERT_MESSAGE("Formula result should be an error due to out-of-bound input..", nError != FormulaError::NONE);
4926 
4927     m_pDoc->DeleteTab(0);
4928 }
4929 
testFuncIFERROR()4930 void Test::testFuncIFERROR()
4931 {
4932     // IFERROR/IFNA (fdo#56124)
4933 
4934     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4935                             m_pDoc->InsertTab (0, "foo"));
4936 
4937     // Empty A1:A39 first.
4938     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
4939 
4940     // Raw data (rows 1 through 12)
4941     const char* aData[] = {
4942         "1",
4943         "e",
4944         "=SQRT(4)",
4945         "=SQRT(-2)",
4946         "=A4",
4947         "=1/0",
4948         "=NA()",
4949         "bar",
4950         "4",
4951         "gee",
4952         "=1/0",
4953         "23"
4954     };
4955 
4956     SCROW nRows = SAL_N_ELEMENTS(aData);
4957     for (SCROW i = 0; i < nRows; ++i)
4958         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
4959 
4960     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
4961 
4962     // formulas and results
4963     static const struct {
4964         const char* pFormula; const char* pResult;
4965     } aChecks[] = {
4966         { "=IFERROR(A1;9)",                         "1" },
4967         { "=IFERROR(A2;9)",                         "e" },
4968         { "=IFERROR(A3;9)",                         "2" },
4969         { "=IFERROR(A4;-7)",                       "-7" },
4970         { "=IFERROR(A5;-7)",                       "-7" },
4971         { "=IFERROR(A6;-7)",                       "-7" },
4972         { "=IFERROR(A7;-7)",                       "-7" },
4973         { "=IFNA(A6;9)",                      "#DIV/0!" },
4974         { "=IFNA(A7;-7)",                          "-7" },
4975         { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)",    "4" },
4976         { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
4977         { "{=IFERROR(3*A11:A12;1998)}[0]",       "1998" },  // um... this is not the correct way to insert a
4978         { "{=IFERROR(3*A11:A12;1998)}[1]",         "69" }   // matrix formula, just a place holder, see below
4979     };
4980 
4981     nRows = SAL_N_ELEMENTS(aChecks);
4982     for (SCROW i = 0; i < nRows-2; ++i)
4983     {
4984         SCROW nRow = 20 + i;
4985         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
4986     }
4987 
4988     // Create a matrix range in last two rows of the range above, actual data
4989     // of the placeholders.
4990     ScMarkData aMark(MAXROW, MAXCOL);
4991     aMark.SelectOneTable(0);
4992     m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)");
4993 
4994     m_pDoc->CalcAll();
4995 
4996     for (SCROW i = 0; i < nRows; ++i)
4997     {
4998         SCROW nRow = 20 + i;
4999         OUString aResult = m_pDoc->GetString(0, nRow, 0);
5000         CPPUNIT_ASSERT_EQUAL_MESSAGE(
5001             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
5002     }
5003 
5004     const SCCOL nCols = 3;
5005     const char* aData2[][nCols] = {
5006         { "1", "2",    "3" },
5007         { "4", "=1/0", "6" },
5008         { "7", "8",    "9" }
5009     };
5010     const char* aCheck2[][nCols] = {
5011         { "1", "2",    "3" },
5012         { "4", "Error","6" },
5013         { "7", "8",    "9" }
5014     };
5015 
5016     // Data in C1:E3
5017     ScAddress aPos(2,0,0);
5018     ScRange aRange = insertRangeData(m_pDoc, aPos, aData2, SAL_N_ELEMENTS(aData2));
5019     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
5020 
5021     // Array formula in F4:H6
5022     const SCROW nElems2 = SAL_N_ELEMENTS(aCheck2);
5023     const SCCOL nStartCol = aPos.Col() + nCols;
5024     const SCROW nStartRow = aPos.Row() + nElems2;
5025     m_pDoc->InsertMatrixFormula( nStartCol, nStartRow, nStartCol+nCols-1, nStartRow+nElems2-1, aMark,
5026             "=IFERROR(C1:E3;\"Error\")");
5027 
5028     m_pDoc->CalcAll();
5029 
5030     for (SCCOL nCol = nStartCol; nCol < nStartCol + nCols; ++nCol)
5031     {
5032         for (SCROW nRow = nStartRow; nRow < nStartRow + nElems2; ++nRow)
5033         {
5034             OUString aResult = m_pDoc->GetString( nCol, nRow, 0);
5035             CPPUNIT_ASSERT_EQUAL_MESSAGE( "IFERROR array result",
5036                     OUString::createFromAscii( aCheck2[nRow-nStartRow][nCol-nStartCol]), aResult);
5037         }
5038     }
5039 
5040     m_pDoc->DeleteTab(0);
5041 }
5042 
testFuncSHEET()5043 void Test::testFuncSHEET()
5044 {
5045     OUString const aTabName1("test1");
5046     OUString const aTabName2("test2");
5047     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5048                             m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
5049 
5050     m_pDoc->SetString(0, 0, 0, "=SHEETS()");
5051     m_pDoc->CalcFormulaTree(false, false);
5052     double original;
5053     m_pDoc->GetValue(0, 0, 0, original);
5054 
5055     CPPUNIT_ASSERT_EQUAL_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
5056                            static_cast<SCTAB>(original), m_pDoc->GetTableCount());
5057 
5058     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5059                             m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
5060 
5061     double modified;
5062     m_pDoc->GetValue(0, 0, 0, modified);
5063     ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
5064                            1.0, modified - original);
5065 
5066     SCTAB nTabCount = m_pDoc->GetTableCount();
5067     m_pDoc->DeleteTab(--nTabCount);
5068 
5069     m_pDoc->GetValue(0, 0, 0, modified);
5070     ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
5071                            0.0, modified - original);
5072 
5073     m_pDoc->DeleteTab(--nTabCount);
5074 }
5075 
testFuncNOW()5076 void Test::testFuncNOW()
5077 {
5078     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5079                             m_pDoc->InsertTab (0, "foo"));
5080 
5081     double val = 1;
5082     m_pDoc->SetValue(0, 0, 0, val);
5083     m_pDoc->SetString(0, 1, 0, "=IF(A1>0;NOW();0");
5084     double now1;
5085     m_pDoc->GetValue(0, 1, 0, now1);
5086     CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
5087 
5088     val = 0;
5089     m_pDoc->SetValue(0, 0, 0, val);
5090     m_pDoc->CalcFormulaTree(false, false);
5091     double zero;
5092     m_pDoc->GetValue(0, 1, 0, zero);
5093     ASSERT_DOUBLES_EQUAL_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", 0.0, zero);
5094 
5095     val = 1;
5096     m_pDoc->SetValue(0, 0, 0, val);
5097     m_pDoc->CalcFormulaTree(false, false);
5098     double now2;
5099     m_pDoc->GetValue(0, 1, 0, now2);
5100     CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
5101 
5102     m_pDoc->DeleteTab(0);
5103 }
5104 
testFuncNUMBERVALUE()5105 void Test::testFuncNUMBERVALUE()
5106 {
5107     // NUMBERVALUE fdo#57180
5108 
5109     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5110                             m_pDoc->InsertTab (0, "foo"));
5111 
5112     // Empty A1:A39 first.
5113     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5114 
5115     // Raw data (rows 1 through 6)
5116     const char* aData[] = {
5117         "1ag9a9b9",
5118         "1ag34 5g g6  78b9%%",
5119         "1 234d56E-2",
5120         "d4",
5121         "54.4",
5122         "1a2b3e1%"
5123     };
5124 
5125     SCROW nRows = SAL_N_ELEMENTS(aData);
5126     for (SCROW i = 0; i < nRows; ++i)
5127         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5128 
5129     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
5130 
5131     // formulas and results
5132     static const struct {
5133         const char* pFormula; const char* pResult;
5134     } aChecks[] = {
5135         { "=NUMBERVALUE(A1;\"b\";\"ag\")",  "199.9" },
5136         { "=NUMBERVALUE(A2;\"b\";\"ag\")",  "134.56789" },
5137         { "=NUMBERVALUE(A2;\"b\";\"g\")",   "#VALUE!" },
5138         { "=NUMBERVALUE(A3;\"d\")",         "12.3456" },
5139         { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
5140         { "=NUMBERVALUE(A4;)",              "Err:502" },
5141         { "=NUMBERVALUE(A5;)",              "Err:502" },
5142         { "=NUMBERVALUE(A6;\"b\";\"a\")",   "1.23" }
5143     };
5144 
5145     nRows = SAL_N_ELEMENTS(aChecks);
5146     for (SCROW i = 0; i < nRows; ++i)
5147     {
5148         SCROW nRow = 20 + i;
5149         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5150     }
5151     m_pDoc->CalcAll();
5152 
5153     for (SCROW i = 0; i < nRows; ++i)
5154     {
5155         SCROW nRow = 20 + i;
5156         OUString aResult = m_pDoc->GetString(0, nRow, 0);
5157         CPPUNIT_ASSERT_EQUAL_MESSAGE(
5158             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
5159     }
5160 
5161     m_pDoc->DeleteTab(0);
5162 }
5163 
testFuncLEN()5164 void Test::testFuncLEN()
5165 {
5166     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5167 
5168     m_pDoc->InsertTab(0, "Formula");
5169 
5170     // Leave A1:A3 empty, and insert an array of LEN in B1:B3 that references
5171     // these empty cells.
5172 
5173     ScMarkData aMark(MAXROW, MAXCOL);
5174     aMark.SelectOneTable(0);
5175     m_pDoc->InsertMatrixFormula(1, 0, 1, 2, aMark, "=LEN(A1:A3)");
5176 
5177     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
5178     CPPUNIT_ASSERT(pFC);
5179     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be a matrix origin.",
5180                            ScMatrixMode::Formula, pFC->GetMatrixFlag());
5181 
5182     // This should be a 1x3 matrix.
5183     SCCOL nCols = -1;
5184     SCROW nRows = -1;
5185     pFC->GetMatColsRows(nCols, nRows);
5186     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
5187     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRows);
5188 
5189     // LEN value should be 0 for an empty cell.
5190     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5191     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
5192     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,2,0)));
5193 
5194     m_pDoc->DeleteTab(0);
5195 }
5196 
testFuncLOOKUP()5197 void Test::testFuncLOOKUP()
5198 {
5199     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5200 
5201     m_pDoc->InsertTab(0, "Test");
5202 
5203     // Raw data
5204     const char* aData[][2] = {
5205         { "=CONCATENATE(\"A\")", "1" },
5206         { "=CONCATENATE(\"B\")", "2" },
5207         { "=CONCATENATE(\"C\")", "3" },
5208         { nullptr, nullptr } // terminator
5209     };
5210 
5211     // Insert raw data into A1:B3.
5212     for (SCROW i = 0; aData[i][0]; ++i)
5213     {
5214         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5215         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5216     }
5217 
5218     const char* aData2[][2] = {
5219         { "A", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5220         { "B", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5221         { "C", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5222         { nullptr, nullptr } // terminator
5223     };
5224 
5225     // Insert check formulas into A5:B7.
5226     for (SCROW i = 0; aData2[i][0]; ++i)
5227     {
5228         m_pDoc->SetString(0, i+4, 0, OUString::createFromAscii(aData2[i][0]));
5229         m_pDoc->SetString(1, i+4, 0, OUString::createFromAscii(aData2[i][1]));
5230     }
5231 
5232     printRange(m_pDoc, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
5233 
5234     // Values for B5:B7 should be 1, 2, and 3.
5235     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,4,0))));
5236     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,5,0))));
5237     CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,6,0))));
5238 
5239     ASSERT_DOUBLES_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
5240     ASSERT_DOUBLES_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,5,0)));
5241     ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,6,0)));
5242 
5243     m_pDoc->DeleteTab(0);
5244 }
5245 
testFuncLOOKUParrayWithError()5246 void Test::testFuncLOOKUParrayWithError()
5247 {
5248     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5249     m_pDoc->InsertTab(0, "Test");
5250 
5251     std::vector<std::vector<const char*>> aData = {
5252         { "x", "y", "z" },
5253         { "a", "b", "c" }
5254     };
5255     insertRangeData(m_pDoc, ScAddress(2,1,0), aData);               // C2:E3
5256     m_pDoc->SetString(0,0,0, "=LOOKUP(2;1/(C2:E2<>\"\");C3:E3)");   // A1
5257 
5258     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last column.", OUString("c"), m_pDoc->GetString(0,0,0));
5259     m_pDoc->SetString(4,1,0, "");                                   // E2
5260     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for second last column.", OUString("b"), m_pDoc->GetString(0,0,0));
5261 
5262     m_pDoc->SetString(6,1,0, "one");                                // G2
5263     m_pDoc->SetString(6,5,0, "two");                                // G6
5264     // Creates an interim array {1,#DIV/0!,#DIV/0!,#DIV/0!,1,#DIV/0!,#DIV/0!,#DIV/0!}
5265     m_pDoc->SetString(7,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(G2:G9)));G2:G9)"); // H9
5266     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last row.", OUString("two"), m_pDoc->GetString(7,8,0));
5267 
5268     // Lookup on empty range.
5269     m_pDoc->SetString(9,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(I2:I9)));I2:I9)"); // J9
5270     CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find no match.", OUString("#N/A"), m_pDoc->GetString(9,8,0));
5271 
5272     m_pDoc->DeleteTab(0);
5273 }
5274 
testFuncVLOOKUP()5275 void Test::testFuncVLOOKUP()
5276 {
5277     // VLOOKUP
5278 
5279     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5280                             m_pDoc->InsertTab (0, "foo"));
5281 
5282     // Clear A1:F40.
5283     clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
5284 
5285     // Raw data
5286     const char* aData[][2] = {
5287         { "Key", "Val" },
5288         {  "10",   "3" },
5289         {  "20",   "4" },
5290         {  "30",   "5" },
5291         {  "40",   "6" },
5292         {  "50",   "7" },
5293         {  "60",   "8" },
5294         {  "70",   "9" },
5295         {   "B",  "10" },
5296         {   "B",  "11" },
5297         {   "C",  "12" },
5298         {   "D",  "13" },
5299         {   "E",  "14" },
5300         {   "F",  "15" },
5301         { nullptr, nullptr } // terminator
5302     };
5303 
5304     // Insert raw data into A1:B14.
5305     for (SCROW i = 0; aData[i][0]; ++i)
5306     {
5307         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5308         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5309     }
5310 
5311     printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
5312 
5313     // Formula data
5314     static const struct {
5315         const char* pLookup; const char* pFormula; const char* pRes;
5316     } aChecks[] = {
5317         { "Lookup",  "Formula", nullptr },
5318         { "12",      "=VLOOKUP(D2;A2:B14;2;1)",     "3" },
5319         { "29",      "=VLOOKUP(D3;A2:B14;2;1)",     "4" },
5320         { "31",      "=VLOOKUP(D4;A2:B14;2;1)",     "5" },
5321         { "45",      "=VLOOKUP(D5;A2:B14;2;1)",     "6" },
5322         { "56",      "=VLOOKUP(D6;A2:B14;2;1)",     "7" },
5323         { "65",      "=VLOOKUP(D7;A2:B14;2;1)",     "8" },
5324         { "78",      "=VLOOKUP(D8;A2:B14;2;1)",     "9" },
5325         { "Andy",    "=VLOOKUP(D9;A2:B14;2;1)",  "#N/A" },
5326         { "Bruce",   "=VLOOKUP(D10;A2:B14;2;1)",   "11" },
5327         { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)",   "12" },
5328         { "David",   "=VLOOKUP(D12;A2:B14;2;1)",   "13" },
5329         { "Edward",  "=VLOOKUP(D13;A2:B14;2;1)",   "14" },
5330         { "Frank",   "=VLOOKUP(D14;A2:B14;2;1)",   "15" },
5331         { "Henry",   "=VLOOKUP(D15;A2:B14;2;1)",   "15" },
5332         { "100",     "=VLOOKUP(D16;A2:B14;2;1)",    "9" },
5333         { "1000",    "=VLOOKUP(D17;A2:B14;2;1)",    "9" },
5334         { "Zena",    "=VLOOKUP(D18;A2:B14;2;1)",   "15" }
5335     };
5336 
5337     // Insert formula data into D1:E18.
5338     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5339     {
5340         m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
5341         m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
5342     }
5343     m_pDoc->CalcAll();
5344     printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
5345 
5346     // Verify results.
5347     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5348     {
5349         if (i == 0)
5350             // Skip the header row.
5351             continue;
5352 
5353         OUString aRes = m_pDoc->GetString(4, i, 0);
5354         bool bGood = aRes.equalsAscii(aChecks[i].pRes);
5355         if (!bGood)
5356         {
5357             cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
5358                 << "'  expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
5359             CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
5360         }
5361     }
5362 
5363     // Clear the sheet and start over.
5364     clearSheet(m_pDoc, 0);
5365 
5366     // Lookup on sorted data interspersed with empty cells.
5367 
5368     // A1:B8 is the search range.
5369     m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
5370     m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
5371     m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
5372     m_pDoc->SetString(ScAddress(1,2,0), "One");
5373     m_pDoc->SetString(ScAddress(1,4,0), "Two");
5374     m_pDoc->SetString(ScAddress(1,7,0), "Four");
5375 
5376     // D1:D5 contain match values.
5377     m_pDoc->SetValue(ScAddress(3,0,0), 1.0);
5378     m_pDoc->SetValue(ScAddress(3,1,0), 2.0);
5379     m_pDoc->SetValue(ScAddress(3,2,0), 3.0);
5380     m_pDoc->SetValue(ScAddress(3,3,0), 4.0);
5381     m_pDoc->SetValue(ScAddress(3,4,0), 5.0);
5382 
5383     // E1:E5 contain formulas.
5384     m_pDoc->SetString(ScAddress(4,0,0), "=VLOOKUP(D1;$A$1:$B$8;2)");
5385     m_pDoc->SetString(ScAddress(4,1,0), "=VLOOKUP(D2;$A$1:$B$8;2)");
5386     m_pDoc->SetString(ScAddress(4,2,0), "=VLOOKUP(D3;$A$1:$B$8;2)");
5387     m_pDoc->SetString(ScAddress(4,3,0), "=VLOOKUP(D4;$A$1:$B$8;2)");
5388     m_pDoc->SetString(ScAddress(4,4,0), "=VLOOKUP(D5;$A$1:$B$8;2)");
5389     m_pDoc->CalcAll();
5390 
5391     // Check the formula results in E1:E5.
5392     CPPUNIT_ASSERT_EQUAL(OUString("One"), m_pDoc->GetString(ScAddress(4,0,0)));
5393     CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,1,0)));
5394     CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,2,0)));
5395     CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,3,0)));
5396     CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,4,0)));
5397 
5398     // Start over again.
5399     clearSheet(m_pDoc, 0);
5400 
5401     // Set A,B,...,G to A1:A7.
5402     m_pDoc->SetString(ScAddress(0,0,0), "A");
5403     m_pDoc->SetString(ScAddress(0,1,0), "B");
5404     m_pDoc->SetString(ScAddress(0,2,0), "C");
5405     m_pDoc->SetString(ScAddress(0,3,0), "D");
5406     m_pDoc->SetString(ScAddress(0,4,0), "E");
5407     m_pDoc->SetString(ScAddress(0,5,0), "F");
5408     m_pDoc->SetString(ScAddress(0,6,0), "G");
5409 
5410     // Set the formula in C1.
5411     m_pDoc->SetString(ScAddress(2,0,0), "=VLOOKUP(\"C\";A1:A16;1)");
5412     CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc->GetString(ScAddress(2,0,0)));
5413 
5414 
5415     // A21:E24, test position dependent implicit intersection as argument to a
5416     // scalar value parameter in a function that has a ReferenceOrForceArray
5417     // type parameter somewhere else and formula is not in array mode,
5418     // VLOOKUP(Value;ReferenceOrForceArray;...)
5419     const char* aData2[][5] = {
5420         { "1", "one",   "3", "=VLOOKUP(C21:C24;A21:B24;2;0)", "three" },
5421         { "2", "two",   "1", "=VLOOKUP(C21:C24;A21:B24;2;0)", "one"   },
5422         { "3", "three", "4", "=VLOOKUP(C21:C24;A21:B24;2;0)", "four"  },
5423         { "4", "four",  "2", "=VLOOKUP(C21:C24;A21:B24;2;0)", "two"   }
5424     };
5425 
5426     ScAddress aPos2(0,20,0);
5427     ScRange aRange2 = insertRangeData(m_pDoc, aPos2, aData2, SAL_N_ELEMENTS(aData2));
5428     CPPUNIT_ASSERT_EQUAL(aPos2, aRange2.aStart);
5429 
5430     aPos2.SetCol(3);    // column D formula results
5431     for (size_t i=0; i < SAL_N_ELEMENTS(aData2); ++i)
5432     {
5433         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aData2[i][4]), m_pDoc->GetString(aPos2));
5434         aPos2.IncRow();
5435     }
5436 
5437     m_pDoc->DeleteTab(0);
5438 }
5439 
5440 struct StrStrCheck {
5441     const char* pVal;
5442     const char* pRes;
5443 };
5444 
5445 template<size_t DataSize, size_t FormulaSize, int Type>
runTestMATCH(ScDocument * pDoc,const char * aData[DataSize],const StrStrCheck aChecks[FormulaSize])5446 static void runTestMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
5447 {
5448     size_t nDataSize = DataSize;
5449     for (size_t i = 0; i < nDataSize; ++i)
5450         pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5451 
5452     for (size_t i = 0; i < FormulaSize; ++i)
5453     {
5454         pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
5455 
5456         OUStringBuffer aBuf;
5457         aBuf.append("=MATCH(B");
5458         aBuf.append(static_cast<sal_Int32>(i+1));
5459         aBuf.append(";A1:A");
5460         aBuf.append(static_cast<sal_Int32>(nDataSize));
5461         aBuf.append(";");
5462         aBuf.append(static_cast<sal_Int32>(Type));
5463         aBuf.append(")");
5464         OUString aFormula = aBuf.makeStringAndClear();
5465         pDoc->SetString(2, i, 0, aFormula);
5466     }
5467 
5468     pDoc->CalcAll();
5469     Test::printRange(pDoc, ScRange(0, 0, 0, 2, FormulaSize-1, 0), "MATCH");
5470 
5471     // verify the results.
5472     for (size_t i = 0; i < FormulaSize; ++i)
5473     {
5474         OUString aStr = pDoc->GetString(2, i, 0);
5475         if (!aStr.equalsAscii(aChecks[i].pRes))
5476         {
5477             cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
5478                 " criterion='" << aChecks[i].pVal << "'" << endl;
5479             CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
5480         }
5481     }
5482 }
5483 
5484 template<size_t DataSize, size_t FormulaSize, int Type>
runTestHorizontalMATCH(ScDocument * pDoc,const char * aData[DataSize],const StrStrCheck aChecks[FormulaSize])5485 static void runTestHorizontalMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
5486 {
5487     size_t nDataSize = DataSize;
5488     for (size_t i = 0; i < nDataSize; ++i)
5489         pDoc->SetString(i, 0, 0, OUString::createFromAscii(aData[i]));
5490 
5491     for (size_t i = 0; i < FormulaSize; ++i)
5492     {
5493         pDoc->SetString(i, 1, 0, OUString::createFromAscii(aChecks[i].pVal));
5494 
5495         // Assume we don't have more than 26 data columns...
5496         OUStringBuffer aBuf;
5497         aBuf.append("=MATCH(");
5498         aBuf.append(static_cast<sal_Unicode>('A'+i));
5499         aBuf.append("2;A1:");
5500         aBuf.append(static_cast<sal_Unicode>('A'+nDataSize));
5501         aBuf.append("1;");
5502         aBuf.append(static_cast<sal_Int32>(Type));
5503         aBuf.append(")");
5504         OUString aFormula = aBuf.makeStringAndClear();
5505         pDoc->SetString(i, 2, 0, aFormula);
5506     }
5507 
5508     pDoc->CalcAll();
5509     Test::printRange(pDoc, ScRange(0, 0, 0, FormulaSize-1, 2, 0), "MATCH");
5510 
5511     // verify the results.
5512     for (size_t i = 0; i < FormulaSize; ++i)
5513     {
5514         OUString aStr = pDoc->GetString(i, 2, 0);
5515         if (!aStr.equalsAscii(aChecks[i].pRes))
5516         {
5517             cerr << "column " << char('A'+i) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
5518                 " criterion='" << aChecks[i].pVal << "'" << endl;
5519             CPPUNIT_ASSERT_MESSAGE("Unexpected result for horizontal MATCH", false);
5520         }
5521     }
5522 }
5523 
testFuncMATCH()5524 void Test::testFuncMATCH()
5525 {
5526     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5527                             m_pDoc->InsertTab (0, "foo"));
5528 
5529     clearRange(m_pDoc, ScRange(0, 0, 0, 40, 40, 0));
5530     {
5531         // Ascending in-exact match
5532 
5533         // data range (A1:A9)
5534         const char* aData[] = {
5535             "1",
5536             "2",
5537             "3",
5538             "4",
5539             "5",
5540             "6",
5541             "7",
5542             "8",
5543             "9",
5544             "B",
5545             "B",
5546             "C",
5547         };
5548 
5549         // formula (B1:C12)
5550         static const StrStrCheck aChecks[] = {
5551             { "0.8",   "#N/A" },
5552             { "1.2",      "1" },
5553             { "2.3",      "2" },
5554             { "3.9",      "3" },
5555             { "4.1",      "4" },
5556             { "5.99",     "5" },
5557             { "6.1",      "6" },
5558             { "7.2",      "7" },
5559             { "8.569",    "8" },
5560             { "9.59",     "9" },
5561             { "10",       "9" },
5562             { "100",      "9" },
5563             { "Andy",  "#N/A" },
5564             { "Bruce",   "11" },
5565             { "Charlie", "12" }
5566         };
5567 
5568         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
5569         clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
5570         runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
5571         clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
5572     }
5573 
5574     {
5575         // Descending in-exact match
5576 
5577         // data range (A1:A9)
5578         const char* aData[] = {
5579             "D",
5580             "C",
5581             "B",
5582             "9",
5583             "8",
5584             "7",
5585             "6",
5586             "5",
5587             "4",
5588             "3",
5589             "2",
5590             "1"
5591         };
5592 
5593         // formula (B1:C12)
5594         static const StrStrCheck aChecks[] = {
5595             { "10",      "#N/A" },
5596             { "8.9",     "4" },
5597             { "7.8",     "5" },
5598             { "6.7",     "6" },
5599             { "5.5",     "7" },
5600             { "4.6",     "8" },
5601             { "3.3",     "9" },
5602             { "2.2",     "10" },
5603             { "1.1",     "11" },
5604             { "0.8",     "12" },
5605             { "0",       "12" },
5606             { "-2",      "12" },
5607             { "Andy",    "3" },
5608             { "Bruce",   "2" },
5609             { "Charlie", "1" },
5610             { "David", "#N/A" }
5611         };
5612 
5613         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
5614         clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
5615         runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
5616         clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
5617     }
5618 
5619     {
5620         // search range contains leading and trailing empty cell ranges.
5621 
5622         clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
5623 
5624         // A5:A8 contains sorted values.
5625         m_pDoc->SetValue(ScAddress(0,4,0), 1.0);
5626         m_pDoc->SetValue(ScAddress(0,5,0), 2.0);
5627         m_pDoc->SetValue(ScAddress(0,6,0), 3.0);
5628         m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
5629 
5630         // Find value 2 which is in A6.
5631         m_pDoc->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
5632         m_pDoc->CalcAll();
5633 
5634         CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc->GetString(ScAddress(1,0,0)));
5635     }
5636 
5637     {
5638         // Test the ReferenceOrForceArray parameter.
5639 
5640         clearRange(m_pDoc, ScRange(0,0,0,1,7,0));
5641 
5642         // B1:B5 contain numeric values.
5643         m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
5644         m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
5645         m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
5646         m_pDoc->SetValue(ScAddress(1,3,0), 4.0);
5647         m_pDoc->SetValue(ScAddress(1,4,0), 5.0);
5648 
5649         // Find string value "33" in concatenated array, no implicit
5650         // intersection is involved, array is forced.
5651         m_pDoc->SetString(ScAddress(0,5,0), "=MATCH(\"33\";B1:B5&B1:B5)");
5652         m_pDoc->CalcAll();
5653         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
5654     }
5655 
5656     m_pDoc->DeleteTab(0);
5657 }
5658 
testFuncCELL()5659 void Test::testFuncCELL()
5660 {
5661     OUString const aTabName("foo");
5662     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5663                             m_pDoc->InsertTab (0, aTabName));
5664 
5665     clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
5666 
5667     {
5668         const char* pContent = "Some random text";
5669         m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
5670         m_pDoc->SetValue(2, 0, 0, 1.2); // Set numeric value to C1;
5671 
5672         // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
5673         StrStrCheck aChecks[] = {
5674             { "=CELL(\"COL\";C10)",           "3" },
5675             { "=CELL(\"ROW\";C10)",          "10" },
5676             { "=CELL(\"SHEET\";C10)",         "1" },
5677             { "=CELL(\"ADDRESS\";C10)",   "$C$10" },
5678             { "=CELL(\"CONTENTS\";C10)", pContent },
5679             { "=CELL(\"COLOR\";C10)",         "0" },
5680             { "=CELL(\"TYPE\";C9)",           "b" },
5681             { "=CELL(\"TYPE\";C10)",          "l" },
5682             { "=CELL(\"TYPE\";C1)",           "v" },
5683             { "=CELL(\"PARENTHESES\";C10)",   "0" }
5684         };
5685 
5686         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5687             m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
5688         m_pDoc->CalcAll();
5689 
5690         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5691         {
5692             OUString aVal = m_pDoc->GetString(0, i, 0);
5693             CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
5694         }
5695     }
5696 
5697     m_pDoc->DeleteTab(0);
5698 }
5699 
5700 /** See also test case document fdo#44456 sheet cpearson */
testFuncDATEDIF()5701 void Test::testFuncDATEDIF()
5702 {
5703     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5704                             m_pDoc->InsertTab (0, "foo"));
5705 
5706     const char* aData[][5] = {
5707         { "2007-01-01", "2007-01-10",  "d",   "9", "=DATEDIF(A1;B1;C1)" } ,
5708         { "2007-01-01", "2007-01-31",  "m",   "0", "=DATEDIF(A2;B2;C2)" } ,
5709         { "2007-01-01", "2007-02-01",  "m",   "1", "=DATEDIF(A3;B3;C3)" } ,
5710         { "2007-01-01", "2007-02-28",  "m",   "1", "=DATEDIF(A4;B4;C4)" } ,
5711         { "2007-01-01", "2007-12-31",  "d", "364", "=DATEDIF(A5;B5;C5)" } ,
5712         { "2007-01-01", "2007-01-31",  "y",   "0", "=DATEDIF(A6;B6;C6)" } ,
5713         { "2007-01-01", "2008-07-01",  "d", "547", "=DATEDIF(A7;B7;C7)" } ,
5714         { "2007-01-01", "2008-07-01",  "m",  "18", "=DATEDIF(A8;B8;C8)" } ,
5715         { "2007-01-01", "2008-07-01", "ym",   "6", "=DATEDIF(A9;B9;C9)" } ,
5716         { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
5717         { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
5718         { "2007-01-01", "2007-01-31", "md",  "30", "=DATEDIF(A12;B12;C12)" } ,
5719         { "2007-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A13;B13;C13)" } ,
5720         { "2008-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A14;B14;C14)" } ,
5721         { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" }    // fail date1 > date2
5722     };
5723 
5724     clearRange( m_pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
5725     ScAddress aPos(0,0,0);
5726     ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
5727     CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
5728 
5729     m_pDoc->CalcAll();
5730 
5731     for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
5732     {
5733         OUString aVal = m_pDoc->GetString( 4, i, 0);
5734         //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
5735         CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
5736     }
5737 
5738     m_pDoc->DeleteTab(0);
5739 }
5740 
testFuncINDIRECT()5741 void Test::testFuncINDIRECT()
5742 {
5743     OUString aTabName("foo");
5744     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5745                             m_pDoc->InsertTab (0, aTabName));
5746     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
5747 
5748     bool bGood = m_pDoc->GetName(0, aTabName);
5749     CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
5750 
5751     OUString aTest = "Test", aRefErr = "#REF!";
5752     m_pDoc->SetString(0, 10, 0, aTest);
5753     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cell value.", aTest, m_pDoc->GetString(0,10,0));
5754 
5755     OUString aPrefix = "=INDIRECT(\"";
5756 
5757     OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
5758     m_pDoc->SetString(0, 0, 0, aFormula);
5759     aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
5760     m_pDoc->SetString(0, 1, 0, aFormula);
5761     aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
5762     m_pDoc->SetString(0, 2, 0, aFormula);
5763     aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
5764     m_pDoc->SetString(0, 3, 0, aFormula);
5765 
5766     m_pDoc->CalcAll();
5767     {
5768         // Default (for new documents) is to use current formula syntax
5769         // which is Calc A1
5770         const OUString* aChecks[] = {
5771             &aTest, &aRefErr, &aRefErr, &aTest
5772         };
5773 
5774         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5775         {
5776             OUString aVal = m_pDoc->GetString(0, i, 0);
5777             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5778         }
5779     }
5780 
5781     ScCalcConfig aConfig;
5782     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO );
5783     m_pDoc->SetCalcConfig(aConfig);
5784     m_pDoc->CalcAll();
5785     {
5786         // Explicit Calc A1 syntax
5787         const OUString* aChecks[] = {
5788             &aTest, &aRefErr, &aRefErr, &aTest
5789         };
5790 
5791         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5792         {
5793             OUString aVal = m_pDoc->GetString(0, i, 0);
5794             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5795         }
5796     }
5797 
5798     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 );
5799     m_pDoc->SetCalcConfig(aConfig);
5800     m_pDoc->CalcAll();
5801     {
5802         // Excel A1 syntax
5803         const OUString* aChecks[] = {
5804             &aRefErr, &aTest, &aRefErr, &aTest
5805         };
5806 
5807         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5808         {
5809             OUString aVal = m_pDoc->GetString(0, i, 0);
5810             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5811         }
5812     }
5813 
5814     aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
5815     m_pDoc->SetCalcConfig(aConfig);
5816     m_pDoc->CalcAll();
5817     {
5818         // Excel R1C1 syntax
5819         const OUString* aChecks[] = {
5820             &aRefErr, &aRefErr, &aTest, &aTest
5821         };
5822 
5823         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5824         {
5825             OUString aVal = m_pDoc->GetString(0, i, 0);
5826             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5827         }
5828     }
5829 
5830     m_pDoc->DeleteTab(0);
5831 }
5832 
5833 // Test case for tdf#83365 - Access across spreadsheet returns Err:504
5834 //
testFuncINDIRECT2()5835 void Test::testFuncINDIRECT2()
5836 {
5837     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5838                             m_pDoc->InsertTab (0, "foo"));
5839     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5840                             m_pDoc->InsertTab (1, "bar"));
5841     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5842                             m_pDoc->InsertTab (2, "baz"));
5843 
5844     m_pDoc->SetValue(0,0,0, 10.0);
5845     m_pDoc->SetValue(0,1,0, 10.0);
5846     m_pDoc->SetValue(0,2,0, 10.0);
5847 
5848     // Fill range bar.$A1:bar.$A10 with 1s
5849     for (SCROW i = 0; i < 10; ++i)
5850         m_pDoc->SetValue(0,i,1, 1.0);
5851 
5852     // Test range triplet (absolute, relative, relative) : (absolute, relative, relative)
5853     m_pDoc->SetString(0,0,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$1),1)");
5854 
5855     // Test range triplet (absolute, relative, relative) : (absolute, absolute, relative)
5856     m_pDoc->SetString(0,1,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$2),1)");
5857 
5858     // Test range triplet (absolute, relative, relative) : (absolute, absolute, absolute)
5859     m_pDoc->SetString(0,2,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$3),1)");
5860 
5861     // Test range triplet (absolute, absolute, relative) : (absolute, relative, relative)
5862     m_pDoc->SetString(0,3,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
5863 
5864     // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
5865     m_pDoc->SetString(0,4,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
5866 
5867     // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
5868     m_pDoc->SetString(0,5,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
5869 
5870     // Test range triplet (absolute, absolute, absolute) : (absolute, relative, relative)
5871     m_pDoc->SetString(0,6,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
5872 
5873     // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, relative)
5874     m_pDoc->SetString(0,7,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
5875 
5876     // Check indirect reference "bar.$A\"&foo.$A$1
5877     m_pDoc->SetString(0,8,2, "=COUNTIF(bar.$A$1:INDIRECT(\"bar.$A\"&foo.$A$1),1)");
5878 
5879     // This case should return illegal argument error because
5880     // they reference 2 different absolute sheets
5881     // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, absolute)
5882     m_pDoc->SetString(0,9,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
5883 
5884     m_pDoc->CalcAll();
5885 
5886     // Loop all formulas and check result = 10.0
5887     for (SCROW i = 0; i < 9; ++i)
5888         CPPUNIT_ASSERT_MESSAGE(OString("Failed to INDIRECT reference formula value: " +
5889                     OString::number(i)).getStr(), m_pDoc->GetValue(0,i,2) != 10.0);
5890 
5891     // Check formula cell error
5892     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,2));
5893     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
5894     CPPUNIT_ASSERT_MESSAGE("This formula cell should be an error.", pFC->GetErrCode() != FormulaError::NONE);
5895 
5896     m_pDoc->DeleteTab(2);
5897     m_pDoc->DeleteTab(1);
5898     m_pDoc->DeleteTab(0);
5899 }
5900 
5901 // Test for tdf#107724 do not propagate an array context from MATCH to INDIRECT
5902 // as INDIRECT returns ParamClass::Reference
testFunc_MATCH_INDIRECT()5903 void Test::testFunc_MATCH_INDIRECT()
5904 {
5905     CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab( 0, "foo"));
5906 
5907     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
5908 
5909     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
5910     ScRangeData* pRangeData = new ScRangeData( m_pDoc, "RoleAssignment", "$D$4:$D$13");
5911     pGlobalNames->insert(pRangeData);
5912 
5913     // D6: data to match, in 3rd row of named range.
5914     m_pDoc->SetString( 3,5,0, "Test1");
5915     // F15: Formula generating indirect reference of corner addresses taking
5916     // row+offset and column from named range, which are not in array context
5917     // thus don't create arrays of offsets.
5918     m_pDoc->SetString( 5,14,0, "=MATCH(\"Test1\";INDIRECT(ADDRESS(ROW(RoleAssignment)+1;COLUMN(RoleAssignment))&\":\"&ADDRESS(ROW(RoleAssignment)+ROWS(RoleAssignment)-1;COLUMN(RoleAssignment)));0)");
5919 
5920     // Match in 2nd row of range offset by 1 expected.
5921     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to not propagate array context from MATCH to INDIRECT",
5922             2.0, m_pDoc->GetValue(5,14,0));
5923 
5924     m_pDoc->DeleteTab(0);
5925 }
5926 
testFormulaDepTracking()5927 void Test::testFormulaDepTracking()
5928 {
5929     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
5930 
5931     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
5932 
5933     // B2 listens on D2.
5934     m_pDoc->SetString(1, 1, 0, "=D2");
5935     double val = -999.0; // dummy initial value
5936     m_pDoc->GetValue(1, 1, 0, val);
5937     ASSERT_DOUBLES_EQUAL_MESSAGE("Referencing an empty cell should yield zero.", 0.0, val);
5938 
5939     // Changing the value of D2 should trigger recalculation of B2.
5940     m_pDoc->SetValue(3, 1, 0, 1.1);
5941     m_pDoc->GetValue(1, 1, 0, val);
5942     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 1.1, val);
5943 
5944     // And again.
5945     m_pDoc->SetValue(3, 1, 0, 2.2);
5946     m_pDoc->GetValue(1, 1, 0, val);
5947     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 2.2, val);
5948 
5949     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
5950 
5951     // Now, let's test the range dependency tracking.
5952 
5953     // B2 listens on D2:E6.
5954     m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
5955     m_pDoc->GetValue(1, 1, 0, val);
5956     ASSERT_DOUBLES_EQUAL_MESSAGE("Summing an empty range should yield zero.", 0.0, val);
5957 
5958     // Set value to E3. This should trigger recalc on B2.
5959     m_pDoc->SetValue(4, 2, 0, 2.4);
5960     m_pDoc->GetValue(1, 1, 0, val);
5961     ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on single value change.", 2.4, val);
5962 
5963     // Set value to D5 to trigger recalc again.  Note that this causes an
5964     // addition of 1.2 + 2.4 which is subject to binary floating point
5965     // rounding error.  We need to use approxEqual to assess its value.
5966 
5967     m_pDoc->SetValue(3, 4, 0, 1.2);
5968     m_pDoc->GetValue(1, 1, 0, val);
5969     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
5970 
5971     // Change the value of D2 (boundary case).
5972     m_pDoc->SetValue(3, 1, 0, 1.0);
5973     m_pDoc->GetValue(1, 1, 0, val);
5974     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
5975 
5976     // Change the value of E6 (another boundary case).
5977     m_pDoc->SetValue(4, 5, 0, 2.0);
5978     m_pDoc->GetValue(1, 1, 0, val);
5979     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
5980 
5981     // Change the value of D6 (another boundary case).
5982     m_pDoc->SetValue(3, 5, 0, 3.0);
5983     m_pDoc->GetValue(1, 1, 0, val);
5984     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
5985 
5986     // Change the value of E2 (another boundary case).
5987     m_pDoc->SetValue(4, 1, 0, 0.4);
5988     m_pDoc->GetValue(1, 1, 0, val);
5989     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
5990 
5991     // Change the existing non-empty value cell (E2).
5992     m_pDoc->SetValue(4, 1, 0, 2.4);
5993     m_pDoc->GetValue(1, 1, 0, val);
5994     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
5995 
5996     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
5997 
5998     // Now, column-based dependency tracking.  We now switch to the R1C1
5999     // syntax which is easier to use for repeated relative references.
6000 
6001     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
6002 
6003     val = 0.0;
6004     for (SCROW nRow = 1; nRow <= 9; ++nRow)
6005     {
6006         // Static value in column 1.
6007         m_pDoc->SetValue(0, nRow, 0, ++val);
6008 
6009         // Formula in column 2 that references cell to the left.
6010         m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
6011 
6012         // Formula in column 3 that references cell to the left.
6013         m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
6014     }
6015 
6016     // Check formula values.
6017     val = 0.0;
6018     for (SCROW nRow = 1; nRow <= 9; ++nRow)
6019     {
6020         ++val;
6021         ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val, m_pDoc->GetValue(1, nRow, 0));
6022         ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val*2.0, m_pDoc->GetValue(2, nRow, 0));
6023     }
6024 
6025     // Intentionally insert a formula in column 1. This will break column 1's
6026     // uniformity of consisting only of static value cells.
6027     m_pDoc->SetString(0, 4, 0, "=R2C3");
6028     ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(0, 4, 0));
6029     ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(1, 4, 0));
6030     ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc->GetValue(2, 4, 0));
6031 
6032     m_pDoc->DeleteTab(0);
6033 }
6034 
testFormulaDepTracking2()6035 void Test::testFormulaDepTracking2()
6036 {
6037     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
6038 
6039     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6040 
6041     double val = 2.0;
6042     m_pDoc->SetValue(0, 0, 0, val);
6043     val = 4.0;
6044     m_pDoc->SetValue(1, 0, 0, val);
6045     val = 5.0;
6046     m_pDoc->SetValue(0, 1, 0, val);
6047     m_pDoc->SetString(2, 0, 0, "=A1/B1");
6048     m_pDoc->SetString(1, 1, 0, "=B1*C1");
6049 
6050     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
6051 
6052     clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
6053 
6054     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
6055 
6056     m_pDoc->DeleteTab(0);
6057 }
6058 
testFormulaDepTracking3()6059 void Test::testFormulaDepTracking3()
6060 {
6061     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6062 
6063     m_pDoc->InsertTab(0, "Formula");
6064 
6065     const char* pData[][4] = {
6066         { "1", "2", "=SUM(A1:B1)", "=SUM(C1:C3)" },
6067         { "3", "4", "=SUM(A2:B2)", nullptr },
6068         { "5", "6", "=SUM(A3:B3)", nullptr },
6069     };
6070 
6071     insertRangeData(m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
6072 
6073     // Check the initial formula results.
6074     CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
6075     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(ScAddress(2,1,0)));
6076     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,2,0)));
6077     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(3,0,0)));
6078 
6079     // Change B3 and make sure the change gets propagated to D1.
6080     ScDocFunc& rFunc = getDocShell().GetDocFunc();
6081     rFunc.SetValueCell(ScAddress(1,2,0), 60.0, false);
6082     CPPUNIT_ASSERT_EQUAL(65.0, m_pDoc->GetValue(ScAddress(2,2,0)));
6083     CPPUNIT_ASSERT_EQUAL(75.0, m_pDoc->GetValue(ScAddress(3,0,0)));
6084 
6085     m_pDoc->DeleteTab(0);
6086 }
6087 
testFormulaDepTrackingDeleteRow()6088 void Test::testFormulaDepTrackingDeleteRow()
6089 {
6090     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6091 
6092     m_pDoc->InsertTab(0, "Test");
6093 
6094     // Values in A1:A3.
6095     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
6096     m_pDoc->SetValue(ScAddress(0,1,0), 3.0);
6097     m_pDoc->SetValue(ScAddress(0,2,0), 5.0);
6098 
6099     // SUM(A1:A3) in A5.
6100     m_pDoc->SetString(ScAddress(0,4,0), "=SUM(A1:A3)");
6101 
6102     // A6 to reference A5.
6103     m_pDoc->SetString(ScAddress(0,5,0), "=A5*10");
6104     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
6105     CPPUNIT_ASSERT(pFC);
6106 
6107     // A4 should have a broadcaster with A5 listening to it.
6108     SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,4,0));
6109     CPPUNIT_ASSERT(pBC);
6110     SvtBroadcaster::ListenersType* pListeners = &pBC->GetAllListeners();
6111     CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should have one listener.", size_t(1), pListeners->size());
6112     const SvtListener* pListener = pListeners->at(0);
6113     CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 should be listening to A5.", static_cast<const ScFormulaCell*>(pListener), pFC);
6114 
6115     // Check initial values.
6116     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
6117     CPPUNIT_ASSERT_EQUAL(90.0, m_pDoc->GetValue(ScAddress(0,5,0)));
6118 
6119     // Delete row 2.
6120     ScDocFunc& rFunc = getDocShell().GetDocFunc();
6121     ScMarkData aMark(MAXROW, MAXCOL);
6122     aMark.SelectOneTable(0);
6123     rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
6124 
6125     pBC = m_pDoc->GetBroadcaster(ScAddress(0,3,0));
6126     CPPUNIT_ASSERT_MESSAGE("Broadcaster at A5 should have shifted to A4.", pBC);
6127     pListeners = &pBC->GetAllListeners();
6128     CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should have one listener.", size_t(1), pListeners->size());
6129     pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
6130     CPPUNIT_ASSERT(pFC);
6131     pListener = pListeners->at(0);
6132     CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should be listening to A4.", static_cast<const ScFormulaCell*>(pListener), pFC);
6133 
6134     // Check values after row deletion.
6135     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,3,0)));
6136     CPPUNIT_ASSERT_EQUAL(60.0, m_pDoc->GetValue(ScAddress(0,4,0)));
6137 
6138     m_pDoc->DeleteTab(0);
6139 }
6140 
testFormulaDepTrackingDeleteCol()6141 void Test::testFormulaDepTrackingDeleteCol()
6142 {
6143     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6144 
6145     m_pDoc->InsertTab(0, "Formula");
6146 
6147     const char* aData[][3] = {
6148         { "2", "=A1", "=B1" }, // not grouped
6149         { nullptr, nullptr, nullptr },           // empty row to separate the formula groups.
6150         { "3", "=A3", "=B3" }, // grouped
6151         { "4", "=A4", "=B4" }, // grouped
6152     };
6153 
6154     ScAddress aPos(0,0,0);
6155     ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6156     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
6157 
6158     // Check the initial values.
6159     for (SCCOL i = 0; i <= 2; ++i)
6160     {
6161         CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(i,0,0)));
6162         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(i,2,0)));
6163         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(i,3,0)));
6164     }
6165 
6166     // Make sure B3:B4 and C3:C4 are grouped.
6167     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
6168     CPPUNIT_ASSERT(pFC);
6169     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6170     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6171 
6172     pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
6173     CPPUNIT_ASSERT(pFC);
6174     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6175     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6176 
6177     // Delete column A.  A1, B1, A3:A4 and B3:B4 should all show #REF!.
6178     ScDocFunc& rFunc = getDocShell().GetDocFunc();
6179     ScMarkData aMark(MAXROW, MAXCOL);
6180     aMark.SelectOneTable(0);
6181     rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
6182 
6183     {
6184         // Expected output table content.  0 = empty cell
6185         std::vector<std::vector<const char*>> aOutputCheck = {
6186             { "#REF!", "#REF!" },
6187             { nullptr,  nullptr },
6188             { "#REF!", "#REF!" },
6189             { "#REF!", "#REF!" },
6190         };
6191 
6192         ScRange aCheckRange(0,0,0,1,3,0);
6193         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after deleting column A");
6194         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6195     }
6196 
6197     // Undo and check the result.
6198     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6199     CPPUNIT_ASSERT(pUndoMgr);
6200     pUndoMgr->Undo();
6201 
6202     {
6203         // Expected output table content.  0 = empty cell
6204         std::vector<std::vector<const char*>> aOutputCheck = {
6205             { "2", "2", "2" },
6206             { nullptr,  nullptr, nullptr },
6207             { "3", "3", "3" },
6208             { "4", "4", "4" },
6209         };
6210 
6211         ScRange aCheckRange(0,0,0,2,3,0);
6212         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after undo");
6213         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6214     }
6215 
6216     // Redo and check.
6217     pUndoMgr->Redo();
6218     {
6219         // Expected output table content.  0 = empty cell
6220         std::vector<std::vector<const char*>> aOutputCheck = {
6221             { "#REF!", "#REF!" },
6222             { nullptr, nullptr },
6223             { "#REF!", "#REF!" },
6224             { "#REF!", "#REF!" },
6225         };
6226 
6227         ScRange aCheckRange(0,0,0,1,3,0);
6228         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after redo");
6229         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6230     }
6231 
6232     // Undo and change the values in column A.
6233     pUndoMgr->Undo();
6234     m_pDoc->SetValue(ScAddress(0,0,0), 22.0);
6235     m_pDoc->SetValue(ScAddress(0,2,0), 23.0);
6236     m_pDoc->SetValue(ScAddress(0,3,0), 24.0);
6237 
6238     {
6239         // Expected output table content.  0 = empty cell
6240         std::vector<std::vector<const char*>> aOutputCheck = {
6241             { "22", "22", "22" },
6242             { nullptr, nullptr, nullptr },
6243             { "23", "23", "23" },
6244             { "24", "24", "24" },
6245         };
6246 
6247         ScRange aCheckRange(0,0,0,2,3,0);
6248         bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after undo & value change in column A");
6249         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6250     }
6251 
6252     m_pDoc->DeleteTab(0);
6253 }
6254 
testFormulaMatrixResultUpdate()6255 void Test::testFormulaMatrixResultUpdate()
6256 {
6257     m_pDoc->InsertTab(0, "Test");
6258 
6259     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6260 
6261     // Set a numeric value to A1.
6262     m_pDoc->SetValue(ScAddress(0,0,0), 11.0);
6263 
6264     ScMarkData aMark(MAXROW, MAXCOL);
6265     aMark.SelectOneTable(0);
6266     m_pDoc->InsertMatrixFormula(1, 0, 1, 0, aMark, "=A1");
6267     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
6268     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
6269     CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC);
6270     pFC->SetChanged(false); // Clear this flag to simulate displaying of formula cell value on screen.
6271 
6272     m_pDoc->SetString(ScAddress(0,0,0), "ABC");
6273     CPPUNIT_ASSERT_EQUAL(OUString("ABC"), m_pDoc->GetString(ScAddress(1,0,0)));
6274     pFC->SetChanged(false);
6275 
6276     // Put a new value into A1. The formula should update.
6277     m_pDoc->SetValue(ScAddress(0,0,0), 13.0);
6278     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(1,0,0)));
6279 
6280     m_pDoc->DeleteTab(0);
6281 }
6282 
testExternalRef()6283 void Test::testExternalRef()
6284 {
6285     ScDocShellRef xExtDocSh = new ScDocShell;
6286     xExtDocSh->SetIsInUcalc();
6287     OUString aExtDocName("file:///extdata.fake");
6288     OUString aExtSh1Name("Data1");
6289     OUString aExtSh2Name("Data2");
6290     OUString aExtSh3Name("Data3");
6291     SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6292     xExtDocSh->DoInitNew(pMed);
6293     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6294                            findLoadedDocShellByName(aExtDocName) != nullptr);
6295 
6296     // Populate the external source document.
6297     ScDocument& rExtDoc = xExtDocSh->GetDocument();
6298     rExtDoc.InsertTab(0, aExtSh1Name);
6299     rExtDoc.InsertTab(1, aExtSh2Name);
6300     rExtDoc.InsertTab(2, aExtSh3Name);
6301 
6302     OUString const name("Name");
6303     OUString const value("Value");
6304     OUString const andy("Andy");
6305     OUString const bruce("Bruce");
6306     OUString const charlie("Charlie");
6307     OUString const david("David");
6308     OUString const edward("Edward");
6309     OUString const frank("Frank");
6310     OUString const george("George");
6311     OUString const henry("Henry");
6312 
6313     // Sheet 1
6314     rExtDoc.SetString(0, 0, 0, name);
6315     rExtDoc.SetString(0, 1, 0, andy);
6316     rExtDoc.SetString(0, 2, 0, bruce);
6317     rExtDoc.SetString(0, 3, 0, charlie);
6318     rExtDoc.SetString(0, 4, 0, david);
6319     rExtDoc.SetString(1, 0, 0, value);
6320     double val = 10;
6321     rExtDoc.SetValue(1, 1, 0, val);
6322     val = 11;
6323     rExtDoc.SetValue(1, 2, 0, val);
6324     val = 12;
6325     rExtDoc.SetValue(1, 3, 0, val);
6326     val = 13;
6327     rExtDoc.SetValue(1, 4, 0, val);
6328 
6329     // Sheet 2 remains empty.
6330 
6331     // Sheet 3
6332     rExtDoc.SetString(0, 0, 2, name);
6333     rExtDoc.SetString(0, 1, 2, edward);
6334     rExtDoc.SetString(0, 2, 2, frank);
6335     rExtDoc.SetString(0, 3, 2, george);
6336     rExtDoc.SetString(0, 4, 2, henry);
6337     rExtDoc.SetString(1, 0, 2, value);
6338     val = 99;
6339     rExtDoc.SetValue(1, 1, 2, val);
6340     val = 98;
6341     rExtDoc.SetValue(1, 2, 2, val);
6342     val = 97;
6343     rExtDoc.SetValue(1, 3, 2, val);
6344     val = 96;
6345     rExtDoc.SetValue(1, 4, 2, val);
6346 
6347     // Test external references on the main document while the external
6348     // document is still in memory.
6349     m_pDoc->InsertTab(0, "Test Sheet");
6350     m_pDoc->SetString(0, 0, 0, "='file:///extdata.fake'#Data1.A1");
6351     OUString test = m_pDoc->GetString(0, 0, 0);
6352     CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test, name);
6353 
6354     // After the initial access to the external document, the external ref
6355     // manager should create sheet cache entries for *all* sheets from that
6356     // document.  Note that the doc may have more than 3 sheets but ensure
6357     // that the first 3 are what we expect.
6358     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
6359     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
6360     vector<OUString> aTabNames;
6361     pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
6362     CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
6363     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[0], aExtSh1Name);
6364     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[1], aExtSh2Name);
6365     CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[2], aExtSh3Name);
6366 
6367     m_pDoc->SetString(1, 0, 0, "='file:///extdata.fake'#Data1.B1");
6368     test = m_pDoc->GetString(1, 0, 0);
6369     CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test, value);
6370 
6371     m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#Data1.A2");
6372     m_pDoc->SetString(0, 2, 0, "='file:///extdata.fake'#Data1.A3");
6373     m_pDoc->SetString(0, 3, 0, "='file:///extdata.fake'#Data1.A4");
6374     m_pDoc->SetString(0, 4, 0, "='file:///extdata.fake'#Data1.A5");
6375     m_pDoc->SetString(0, 5, 0, "='file:///extdata.fake'#Data1.A6");
6376 
6377     {
6378         // Referencing an empty cell should display '0'.
6379         const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
6380         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6381         {
6382             test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
6383             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6384         }
6385     }
6386     m_pDoc->SetString(1, 1, 0, "='file:///extdata.fake'#Data1.B2");
6387     m_pDoc->SetString(1, 2, 0, "='file:///extdata.fake'#Data1.B3");
6388     m_pDoc->SetString(1, 3, 0, "='file:///extdata.fake'#Data1.B4");
6389     m_pDoc->SetString(1, 4, 0, "='file:///extdata.fake'#Data1.B5");
6390     m_pDoc->SetString(1, 5, 0, "='file:///extdata.fake'#Data1.B6");
6391     {
6392         double pChecks[] = { 10, 11, 12, 13, 0 };
6393         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6394         {
6395             m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
6396             ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected cell value.", pChecks[i], val);
6397         }
6398     }
6399 
6400     m_pDoc->SetString(2, 0, 0, "='file:///extdata.fake'#Data3.A1");
6401     m_pDoc->SetString(2, 1, 0, "='file:///extdata.fake'#Data3.A2");
6402     m_pDoc->SetString(2, 2, 0, "='file:///extdata.fake'#Data3.A3");
6403     m_pDoc->SetString(2, 3, 0, "='file:///extdata.fake'#Data3.A4");
6404     {
6405         const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
6406         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6407         {
6408             test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
6409             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6410         }
6411     }
6412 
6413     m_pDoc->SetString(3, 0, 0, "='file:///extdata.fake'#Data3.B1");
6414     m_pDoc->SetString(3, 1, 0, "='file:///extdata.fake'#Data3.B2");
6415     m_pDoc->SetString(3, 2, 0, "='file:///extdata.fake'#Data3.B3");
6416     m_pDoc->SetString(3, 3, 0, "='file:///extdata.fake'#Data3.B4");
6417     {
6418         const char* pChecks[] = { "Value", "99", "98", "97" };
6419         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6420         {
6421             test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
6422             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6423         }
6424     }
6425 
6426     // At this point, all accessed cell data from the external document should
6427     // have been cached.
6428     ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
6429         nFileId, aExtSh1Name, false);
6430     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != nullptr);
6431     ScRange aCachedRange = getCachedRange(pCacheTab);
6432     CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
6433                            aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
6434                            aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
6435 
6436     // Sheet2 is not referenced at all; the cache table shouldn't even exist.
6437     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
6438     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", !pCacheTab.get());
6439 
6440     // Sheet3's row 5 is not referenced; it should not be cached.
6441     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
6442     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != nullptr);
6443     aCachedRange = getCachedRange(pCacheTab);
6444     CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
6445                            aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
6446                            aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
6447 
6448     // Unload the external document shell.
6449     xExtDocSh->DoClose();
6450     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6451                            !findLoadedDocShellByName(aExtDocName));
6452 
6453     m_pDoc->DeleteTab(0);
6454 }
6455 
testExternalRangeName()6456 void Test::testExternalRangeName()
6457 {
6458     ScDocShellRef xExtDocSh = new ScDocShell;
6459     xExtDocSh->SetIsInUcalc();
6460     OUString const aExtDocName("file:///extdata.fake");
6461     OUString const aExtSh1Name("Data1");
6462     SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6463     xExtDocSh->DoInitNew(pMed);
6464     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6465                            findLoadedDocShellByName(aExtDocName) != nullptr);
6466 
6467     ScDocument& rExtDoc = xExtDocSh->GetDocument();
6468     rExtDoc.InsertTab(0, aExtSh1Name);
6469     rExtDoc.SetValue(0, 0, 0, 123.456);
6470 
6471     ScRangeName* pRangeName = rExtDoc.GetRangeName();
6472     ScRangeData* pRangeData = new ScRangeData(&rExtDoc, "ExternalName",
6473             "$Data1.$A$1");
6474     pRangeName->insert(pRangeData);
6475 
6476     m_pDoc->InsertTab(0, "Test Sheet");
6477     m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#ExternalName");
6478 
6479     double nVal = m_pDoc->GetValue(0, 1, 0);
6480     ASSERT_DOUBLES_EQUAL(123.456, nVal);
6481 
6482     xExtDocSh->DoClose();
6483     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6484                            !findLoadedDocShellByName(aExtDocName));
6485     m_pDoc->DeleteTab(0);
6486 }
6487 
testExtRefFuncT(ScDocument * pDoc,ScDocument & rExtDoc)6488 static void testExtRefFuncT(ScDocument* pDoc, ScDocument& rExtDoc)
6489 {
6490     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6491     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
6492 
6493     rExtDoc.SetString(0, 0, 0, "'1.2");
6494     rExtDoc.SetString(0, 1, 0, "Foo");
6495     rExtDoc.SetValue(0, 2, 0, 12.3);
6496     pDoc->SetString(0, 0, 0, "=T('file:///extdata.fake'#Data.A1)");
6497     pDoc->SetString(0, 1, 0, "=T('file:///extdata.fake'#Data.A2)");
6498     pDoc->SetString(0, 2, 0, "=T('file:///extdata.fake'#Data.A3)");
6499     pDoc->CalcAll();
6500 
6501     OUString aRes = pDoc->GetString(0, 0, 0);
6502     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("1.2"), aRes);
6503     aRes = pDoc->GetString(0, 1, 0);
6504     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("Foo"), aRes);
6505     aRes = pDoc->GetString(0, 2, 0);
6506     CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
6507 }
6508 
testExtRefFuncOFFSET(ScDocument * pDoc,ScDocument & rExtDoc)6509 static void testExtRefFuncOFFSET(ScDocument* pDoc, ScDocument& rExtDoc)
6510 {
6511     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6512     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
6513 
6514     sc::AutoCalcSwitch aACSwitch(*pDoc, true);
6515 
6516     // External document has sheet named 'Data', and the internal doc has sheet named 'Test'.
6517     rExtDoc.SetValue(ScAddress(0,1,0), 1.2); // Set 1.2 to A2.
6518     pDoc->SetString(ScAddress(0,0,0), "=OFFSET('file:///extdata.fake'#Data.$A$1;1;0;1;1)");
6519     CPPUNIT_ASSERT_EQUAL(1.2, pDoc->GetValue(ScAddress(0,0,0)));
6520 }
6521 
testExtRefFuncVLOOKUP(ScDocument * pDoc,ScDocument & rExtDoc)6522 static void testExtRefFuncVLOOKUP(ScDocument* pDoc, ScDocument& rExtDoc)
6523 {
6524     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6525     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
6526 
6527     // Populate the external document.
6528     rExtDoc.SetString(ScAddress(0,0,0), "A1");
6529     rExtDoc.SetString(ScAddress(0,1,0), "A2");
6530     rExtDoc.SetString(ScAddress(0,2,0), "A3");
6531     rExtDoc.SetString(ScAddress(0,3,0), "A4");
6532     rExtDoc.SetString(ScAddress(0,4,0), "A5");
6533 
6534     rExtDoc.SetString(ScAddress(1,0,0), "B1");
6535     rExtDoc.SetString(ScAddress(1,1,0), "B2");
6536     rExtDoc.SetString(ScAddress(1,2,0), "B3");
6537     rExtDoc.SetString(ScAddress(1,3,0), "B4");
6538     rExtDoc.SetString(ScAddress(1,4,0), "B5");
6539 
6540     // Put formula in the source document.
6541 
6542     pDoc->SetString(ScAddress(0,0,0), "A2");
6543 
6544     // Sort order TRUE
6545     pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;1)");
6546     CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
6547 
6548     // Sort order FALSE. It should return the same result.
6549     pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;0)");
6550     CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
6551 }
6552 
testExtRefConcat(ScDocument * pDoc,ScDocument & rExtDoc)6553 static void testExtRefConcat(ScDocument* pDoc, ScDocument& rExtDoc)
6554 {
6555     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6556     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
6557 
6558     sc::AutoCalcSwitch aACSwitch(*pDoc, true);
6559 
6560     // String and number
6561     rExtDoc.SetString(ScAddress(0,0,0), "Answer: ");
6562     rExtDoc.SetValue(ScAddress(0,1,0), 42);
6563 
6564     // Concat operation should combine string and number converted to string
6565     pDoc->SetString(ScAddress(0,0,0), "='file:///extdata.fake'#Data.A1 & 'file:///extdata.fake'#Data.A2");
6566     CPPUNIT_ASSERT_EQUAL(OUString("Answer: 42"), pDoc->GetString(ScAddress(0,0,0)));
6567 }
6568 
testExternalRefFunctions()6569 void Test::testExternalRefFunctions()
6570 {
6571     ScDocShellRef xExtDocSh = new ScDocShell;
6572     xExtDocSh->SetIsInUcalc();
6573     OUString aExtDocName("file:///extdata.fake");
6574     SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6575     xExtDocSh->DoInitNew(pMed);
6576     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6577                            findLoadedDocShellByName(aExtDocName) != nullptr);
6578 
6579     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
6580     CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
6581     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
6582     const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
6583     CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
6584                            pFileName && *pFileName == aExtDocName);
6585 
6586     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6587 
6588     // Populate the external source document.
6589     ScDocument& rExtDoc = xExtDocSh->GetDocument();
6590     rExtDoc.InsertTab(0, "Data");
6591     double val = 1;
6592     rExtDoc.SetValue(0, 0, 0, val);
6593     // leave cell B1 empty.
6594     val = 2;
6595     rExtDoc.SetValue(0, 1, 0, val);
6596     rExtDoc.SetValue(1, 1, 0, val);
6597     val = 3;
6598     rExtDoc.SetValue(0, 2, 0, val);
6599     rExtDoc.SetValue(1, 2, 0, val);
6600     val = 4;
6601     rExtDoc.SetValue(0, 3, 0, val);
6602     rExtDoc.SetValue(1, 3, 0, val);
6603 
6604     m_pDoc->InsertTab(0, "Test");
6605 
6606     static const struct {
6607         const char* pFormula; double fResult;
6608     } aChecks[] = {
6609         { "=SUM('file:///extdata.fake'#Data.A1:A4)",     10 },
6610         { "=SUM('file:///extdata.fake'#Data.B1:B4)",     9 },
6611         { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
6612         { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
6613         { "=COUNT('file:///extdata.fake'#Data.A1:A4)",   4 },
6614         { "=COUNT('file:///extdata.fake'#Data.B1:B4)",   3 },
6615         // Should not crash, MUST be 0,m_pDoc->MaxRow() and/or 0,m_pDoc->MaxCol() range (here both)
6616         // to yield a result instead of 1x1 error matrix.
6617         { "=SUM('file:///extdata.fake'#Data.1:1048576)", 19 }
6618     };
6619 
6620     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6621     {
6622         m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
6623         m_pDoc->GetValue(0, 0, 0, val);
6624         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected result involving external ranges.", aChecks[i].fResult, val, 1e-15);
6625     }
6626 
6627     // A huge external range should not crash, the matrix generated from the
6628     // external range reference should be 1x1 and have one error value.
6629     // XXX NOTE: in case we supported sparse matrix that can hold this large
6630     // areas these tests may be adapted.
6631     m_pDoc->SetString(0, 0, 0, "=SUM('file:///extdata.fake'#Data.B1:AMJ1048575)");
6632     ScFormulaCell* pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
6633     FormulaError nErr = pFC->GetErrCode();
6634     CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize), static_cast<int>(nErr));
6635 
6636     ScMarkData aMark(MAXROW, MAXCOL);
6637     aMark.SelectOneTable(0);
6638     m_pDoc->InsertMatrixFormula(0,0,0,0, aMark, "'file:///extdata.fake'#Data.B1:AMJ1048575");
6639     pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
6640     nErr = pFC->GetErrCode();
6641     CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize), static_cast<int>(nErr));
6642     SCSIZE nMatCols, nMatRows;
6643     const ScMatrix* pMat = pFC->GetMatrix();
6644     CPPUNIT_ASSERT_MESSAGE("matrix expected", pMat != nullptr);
6645     pMat->GetDimensions( nMatCols, nMatRows);
6646     CPPUNIT_ASSERT_MESSAGE("1x1 matrix expected", nMatCols == 1 && nMatRows == 1);
6647 
6648     pRefMgr->clearCache(nFileId);
6649     testExtRefFuncT(m_pDoc, rExtDoc);
6650     testExtRefFuncOFFSET(m_pDoc, rExtDoc);
6651     testExtRefFuncVLOOKUP(m_pDoc, rExtDoc);
6652     testExtRefConcat(m_pDoc, rExtDoc);
6653 
6654     // Unload the external document shell.
6655     xExtDocSh->DoClose();
6656     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6657                            !findLoadedDocShellByName(aExtDocName));
6658 
6659     m_pDoc->DeleteTab(0);
6660 }
6661 
testExternalRefUnresolved()6662 void Test::testExternalRefUnresolved()
6663 {
6664     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6665     m_pDoc->InsertTab(0, "Test");
6666 
6667     // Test error propagation of unresolved (not existing document) external
6668     // references. Well, let's hope no build machine has such file with sheet...
6669 
6670     const char* aData[][1] = {
6671         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1" },
6672         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23" },
6673         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\"" },
6674         { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6675         { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6676         { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6677         { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6678         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6679         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6680         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23)" },
6681         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\")" },
6682         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=0" },
6683         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=\"\"" },
6684         { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1\")" },
6685         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2" },
6686         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23" },
6687         { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\"" },
6688         { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6689         { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6690         { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6691         { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6692         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6693         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6694         { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23)" },
6695         { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\")" },
6696         // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=0" },
6697         // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=\"\"" },
6698         { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2\")" },
6699     };
6700 
6701     ScAddress aPos(0,0,0);
6702     ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6703     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
6704 
6705     std::vector<std::vector<const char*>> aOutputCheck = {
6706         { "#REF!" },    // plain single ref
6707         { "#REF!" },    // +23
6708         { "#REF!" },    // &"W"
6709         { "FALSE" },    // ISREF
6710         { "TRUE"  },    // ISERROR
6711         { "TRUE"  },    // ISERR
6712         { "FALSE" },    // ISBLANK
6713         { "FALSE" },    // ISNUMBER
6714         { "FALSE" },    // ISTEXT
6715         { "FALSE" },    // ISNUMBER
6716         { "FALSE" },    // ISTEXT
6717         { "#REF!" },    // =0
6718         { "#REF!" },    // =""
6719         { "#REF!" },    // INDIRECT
6720         { "#REF!" },    // A1:A2 range
6721         { "#REF!" },    // +23
6722         { "#REF!" },    // &"W"
6723         { "FALSE" },    // ISREF
6724         { "TRUE"  },    // ISERROR
6725         { "TRUE"  },    // ISERR
6726         { "FALSE" },    // ISBLANK
6727         { "FALSE" },    // ISNUMBER
6728         { "FALSE" },    // ISTEXT
6729         { "FALSE" },    // ISNUMBER
6730         { "FALSE" },    // ISTEXT
6731         // TODO: gives Err:504 FIXME { "#REF!" },    // =0
6732         // TODO: gives Err:504 FIXME { "#REF!" },    // =""
6733         { "#REF!" },    // INDIRECT
6734     };
6735 
6736     bool bSuccess = checkOutput(m_pDoc, aRange, aOutputCheck, "Check unresolved external reference.");
6737     CPPUNIT_ASSERT_MESSAGE("Unresolved reference check failed", bSuccess);
6738 
6739     m_pDoc->DeleteTab(0);
6740 }
6741 
testMatrixOp()6742 void Test::testMatrixOp()
6743 {
6744     m_pDoc->InsertTab(0, "Test");
6745 
6746     for (SCROW nRow = 0; nRow < 4; ++nRow)
6747     {
6748         m_pDoc->SetValue(0, nRow, 0, nRow);
6749     }
6750     m_pDoc->SetValue(1, 0, 0, 2.0);
6751     m_pDoc->SetValue(3, 0, 0, 1.0);
6752     m_pDoc->SetValue(3, 1, 0, 2.0);
6753     m_pDoc->SetString(2, 0, 0, "=SUMPRODUCT((A1:A4)*B1+D1)");
6754     m_pDoc->SetString(2, 1, 0, "=SUMPRODUCT((A1:A4)*B1-D2)");
6755 
6756     double nVal = m_pDoc->GetValue(2, 0, 0);
6757     CPPUNIT_ASSERT_EQUAL(16.0, nVal);
6758 
6759     nVal = m_pDoc->GetValue(2, 1, 0);
6760     CPPUNIT_ASSERT_EQUAL(4.0, nVal);
6761 
6762     m_pDoc->SetString(4, 0, 0, "=SUMPRODUCT({1;2;4}+8)");
6763     m_pDoc->SetString(4, 1, 0, "=SUMPRODUCT(8+{1;2;4})");
6764     m_pDoc->SetString(4, 2, 0, "=SUMPRODUCT({1;2;4}-8)");
6765     m_pDoc->SetString(4, 3, 0, "=SUMPRODUCT(8-{1;2;4})");
6766     m_pDoc->SetString(4, 4, 0, "=SUMPRODUCT({1;2;4}+{8;16;32})");
6767     m_pDoc->SetString(4, 5, 0, "=SUMPRODUCT({8;16;32}+{1;2;4})");
6768     m_pDoc->SetString(4, 6, 0, "=SUMPRODUCT({1;2;4}-{8;16;32})");
6769     m_pDoc->SetString(4, 7, 0, "=SUMPRODUCT({8;16;32}-{1;2;4})");
6770     double fResult[8] = { 31.0, 31.0, -17.0, 17.0, 63.0, 63.0, -49.0, 49.0 };
6771     for (size_t i = 0; i < SAL_N_ELEMENTS(fResult); ++i)
6772     {
6773         CPPUNIT_ASSERT_EQUAL( fResult[i], m_pDoc->GetValue(4, i, 0));
6774     }
6775 
6776     m_pDoc->DeleteTab(0);
6777 }
6778 
testFuncRangeOp()6779 void Test::testFuncRangeOp()
6780 {
6781     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6782 
6783     m_pDoc->InsertTab(0, "Sheet1");
6784     m_pDoc->InsertTab(1, "Sheet2");
6785     m_pDoc->InsertTab(2, "Sheet3");
6786 
6787     // Sheet1.B1:B3
6788     m_pDoc->SetValue(1,0,0, 1.0);
6789     m_pDoc->SetValue(1,1,0, 2.0);
6790     m_pDoc->SetValue(1,2,0, 4.0);
6791     // Sheet2.B1:B3
6792     m_pDoc->SetValue(1,0,1, 8.0);
6793     m_pDoc->SetValue(1,1,1, 16.0);
6794     m_pDoc->SetValue(1,2,1, 32.0);
6795     // Sheet3.B1:B3
6796     m_pDoc->SetValue(1,0,2, 64.0);
6797     m_pDoc->SetValue(1,1,2, 128.0);
6798     m_pDoc->SetValue(1,2,2, 256.0);
6799 
6800     // Range operator should extend concatenated literal references during
6801     // parse time already, so with this we can test ScComplexRefData::Extend()
6802 
6803     // Current sheet is Sheet1, so B1:B2 implies relative Sheet1.B1:B2
6804 
6805     ScAddress aPos(0,0,0);
6806     m_pDoc->SetString( aPos, "=SUM(B1:B2:B3)");
6807     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
6808     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
6809 
6810     aPos.IncRow();
6811     m_pDoc->SetString( aPos, "=SUM(B1:B3:B2)");
6812     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
6813     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
6814 
6815     aPos.IncRow();
6816     m_pDoc->SetString( aPos, "=SUM(B2:B3:B1)");
6817     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
6818     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
6819 
6820     aPos.IncRow();
6821     m_pDoc->SetString( aPos, "=SUM(Sheet2.B1:B2:B3)");
6822     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet2.B1:B3)", "Wrong formula.");
6823     CPPUNIT_ASSERT_EQUAL( 56.0, m_pDoc->GetValue(aPos));
6824 
6825     aPos.IncRow();
6826     m_pDoc->SetString( aPos, "=SUM(B2:B2:Sheet1.B2)");
6827     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B2:B2)", "Wrong formula.");
6828     CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(aPos));
6829 
6830     aPos.IncRow();
6831     m_pDoc->SetString( aPos, "=SUM(B2:B3:Sheet2.B1)");
6832     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet2.B3)", "Wrong formula.");
6833     CPPUNIT_ASSERT_EQUAL( 63.0, m_pDoc->GetValue(aPos));
6834 
6835     aPos.IncRow();
6836     m_pDoc->SetString( aPos, "=SUM(Sheet1.B1:Sheet2.B2:Sheet3.B3)");
6837     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
6838     CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc->GetValue(aPos));
6839 
6840     // B1:Sheet2.B2 would be ambiguous, Sheet1.B1:Sheet2.B2 or Sheet2.B1:B2
6841     // The actual representation of the error case may change, so this test may
6842     // have to be adapted.
6843     aPos.IncRow();
6844     m_pDoc->SetString( aPos, "=SUM(B1:Sheet2.B2:Sheet3.B3)");
6845     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(#REF!.B2:#REF!.B3)", "Wrong formula.");
6846     CPPUNIT_ASSERT_EQUAL( OUString("#REF!"), m_pDoc->GetString(aPos));
6847 
6848     aPos.IncRow();
6849     m_pDoc->SetString( aPos, "=SUM(Sheet1.B1:Sheet3.B2:Sheet2.B3)");
6850     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
6851     CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc->GetValue(aPos));
6852 
6853     aPos.IncRow();
6854     m_pDoc->SetString( aPos, "=SUM(B$2:B$2:B2)");
6855     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B$2:B2)", "Wrong formula.");
6856     CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(aPos));
6857 
6858     m_pDoc->DeleteTab(2);
6859     m_pDoc->DeleteTab(1);
6860     m_pDoc->DeleteTab(0);
6861 }
6862 
testFuncFORMULA()6863 void Test::testFuncFORMULA()
6864 {
6865     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6866 
6867     m_pDoc->InsertTab(0, "Sheet1");
6868 
6869     // Data in B1:D3
6870     const char* aData[][3] = {
6871         { "=A1", "=FORMULA(B1)", "=FORMULA(B1:B3)" },
6872         {     nullptr, "=FORMULA(B2)", "=FORMULA(B1:B3)" },
6873         { "=A3", "=FORMULA(B3)", "=FORMULA(B1:B3)" },
6874     };
6875 
6876     ScAddress aPos(1,0,0);
6877     ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6878     CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
6879 
6880     // Checks of C1:D3, where Cy==Dy, and D4:D6
6881     const char* aChecks[] = {
6882         "=A1",
6883         "#N/A",
6884         "=A3",
6885     };
6886     for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
6887     {
6888         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(2,i,0));
6889         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i,0));
6890     }
6891 
6892     // Matrix in D4:D6, no intersection with B1:B3
6893     ScMarkData aMark(MAXROW, MAXCOL);
6894     aMark.SelectOneTable(0);
6895     m_pDoc->InsertMatrixFormula(3, 3, 3, 5, aMark, "=FORMULA(B1:B3)");
6896     for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
6897     {
6898         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i+3,0));
6899     }
6900 
6901     m_pDoc->DeleteTab(0);
6902 }
6903 
testFuncTableRef()6904 void Test::testFuncTableRef()
6905 {
6906     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6907 
6908     m_pDoc->InsertTab(0, "Sheet1");
6909     ScMarkData aMark(MAXROW, MAXCOL);
6910     aMark.SelectOneTable(0);
6911     ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
6912 
6913     {
6914         ScDBCollection* pDBs = m_pDoc->GetDBCollection();
6915         CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
6916 
6917         // Insert "table" database range definition for A1:B4, with default
6918         // HasHeader=true and HasTotals=false.
6919         std::unique_ptr<ScDBData> pData(new ScDBData( "table", 0,0,0, 1,3));
6920         bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
6921         CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"table\" database range.", bInserted);
6922     }
6923 
6924     {
6925         // Populate "table" database range with headers and data in A1:B4
6926         const char* aData[][2] = {
6927             { "Header1", "Header2" },
6928             { "1", "2" },
6929             { "4", "8" },
6930             { "16", "32" }
6931         };
6932         ScAddress aPos(0,0,0);
6933         ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6934         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
6935     }
6936 
6937     // Named expressions that use Table structured references.
6938     /* TODO: should the item/header separator really be equal to the parameter
6939      * separator, thus be locale dependent and ';' semicolon here, or should it
6940      * be a fixed ',' comma instead? */
6941     static const struct {
6942         const char* pName;
6943         const char* pExpr;
6944         const char* pCounta; // expected result when used in row 2 (first data row) as argument to COUNTA()
6945         const char* pSum3;   // expected result when used in row 3 (second data row) as argument to SUM().
6946         const char* pSum4;   // expected result when used in row 4 (third data row) as argument to SUM().
6947         const char* pSumX;   // expected result when used in row 5 (non-intersecting) as argument to SUM().
6948     } aNames[] = {
6949         { "all",                            "table[[#All]]",                            "8", "63", "63", "63" },
6950         { "data_implicit",                  "table[]",                                  "6", "63", "63", "63" },
6951         { "data",                           "table[[#Data]]",                           "6", "63", "63", "63" },
6952         { "headers",                        "table[[#Headers]]",                        "2",  "0",  "0",  "0" },
6953         { "header1",                        "table[[Header1]]",                         "3", "21", "21", "21" },
6954         { "header2",                        "table[[Header2]]",                         "3", "42", "42", "42" },
6955         { "data_header1",                   "table[[#Data];[Header1]]",                 "3", "21", "21", "21" },
6956         { "data_header2",                   "table[[#Data];[Header2]]",                 "3", "42", "42", "42" },
6957         { "this_row",                       "table[[#This Row]]",                       "2", "12", "48", "#VALUE!" },
6958         { "this_row_header1",               "table[[#This Row];[Header1]]",             "1",  "4", "16", "#VALUE!" },
6959         { "this_row_header2",               "table[[#This Row];[Header2]]",             "1",  "8", "32", "#VALUE!" },
6960         { "this_row_range_header_1_to_2",   "table[[#This Row];[Header1]:[Header2]]",   "2", "12", "48", "#VALUE!" }
6961     };
6962 
6963     {
6964         // Insert named expressions.
6965         ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
6966         CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
6967 
6968         for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
6969         {
6970             // Choose base position that does not intersect with the database
6971             // range definition to test later use of [#This Row] results in
6972             // proper rows.
6973             ScRangeData* pName = new ScRangeData(
6974                     m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
6975                     ScAddress(2,4,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
6976             bool bInserted = pGlobalNames->insert(pName);
6977             CPPUNIT_ASSERT_MESSAGE(
6978                     OString(OStringLiteral("Failed to insert named expression ") + aNames[i].pName +".").getStr(), bInserted);
6979         }
6980     }
6981 
6982     // Use the named expressions in COUNTA() formulas, on row 2 that intersects.
6983     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
6984     {
6985         OUString aFormula( "=COUNTA(" + OUString::createFromAscii( aNames[i].pName) + ")");
6986         ScAddress aPos(3+i,1,0);
6987         m_pDoc->SetString( aPos, aFormula);
6988         // For easier "debugability" have position and formula in assertion.
6989         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
6990         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pCounta)),
6991                 OUString(aPrefix + m_pDoc->GetString( aPos)));
6992     }
6993 
6994     // Use the named expressions in SUM() formulas, on row 3 that intersects.
6995     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
6996     {
6997         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
6998         ScAddress aPos(3+i,2,0);
6999         m_pDoc->SetString( aPos, aFormula);
7000         // For easier "debugability" have position and formula in assertion.
7001         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7002         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum3)),
7003                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7004     }
7005 
7006     // Use the named expressions in SUM() formulas, on row 4 that intersects.
7007     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7008     {
7009         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7010         ScAddress aPos(3+i,3,0);
7011         m_pDoc->SetString( aPos, aFormula);
7012         // For easier "debugability" have position and formula in assertion.
7013         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7014         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum4)),
7015                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7016     }
7017 
7018     // Use the named expressions in SUM() formulas, on row 5 that does not intersect.
7019     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7020     {
7021         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7022         ScAddress aPos(3+i,4,0);
7023         m_pDoc->SetString( aPos, aFormula);
7024         // For easier "debugability" have position and formula in assertion.
7025         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7026         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSumX)),
7027                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7028     }
7029 
7030     // Insert a column at column B to extend database range from column A,B to
7031     // A,B,C. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7032     rDocFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
7033 
7034     // Re-verify the named expression in SUM() formula, on row 4 that
7035     // intersects, now starting at column E, still works.
7036     m_pDoc->CalcAll();
7037     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7038     {
7039         OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7040         ScAddress aPos(4+i,3,0);
7041         // For easier "debugability" have position and formula in assertion.
7042         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7043         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum4)),
7044                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7045     }
7046 
7047     const char* pColumn2Formula = "=SUM(table[[#Data];[Column2]])";
7048     {
7049         // Populate "table" database range with empty header and data in newly
7050         // inserted column, B1:B4 plus a table formula in B6. The empty header
7051         // should result in the internal table column name "Column2" that is
7052         // used in the formula.
7053         const char* aData[][1] = {
7054             { "" },
7055             { "64" },
7056             { "128" },
7057             { "256" },
7058             { "" },
7059             { pColumn2Formula }
7060         };
7061         ScAddress aPos(1,0,0);
7062         ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
7063         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7064     }
7065 
7066     // Verify the formula result in B6 (64+128+256=448).
7067     {
7068         OUString aFormula( OUString::createFromAscii( pColumn2Formula));
7069         ScAddress aPos(1,5,0);
7070         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7071         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + "448"), OUString(aPrefix + m_pDoc->GetString(aPos)));
7072     }
7073 
7074     // Set header in column B. Use ScDocFunc to have table column names refreshed.
7075     rDocFunc.SetStringCell(ScAddress(1,0,0), "NewHeader",true);
7076     // Verify that formula adapted using the updated table column names.
7077     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader]])", "Wrong formula");
7078 
7079     // Set header in column A to identical string. Internal table column name
7080     // for B should get a "2" appended.
7081     rDocFunc.SetStringCell(ScAddress(0,0,0), "NewHeader",true);
7082     // Verify that formula adapted using the updated table column names.
7083     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7084 
7085     // Set header in column B to empty string, effectively clearing the cell.
7086     rDocFunc.SetStringCell(ScAddress(1,0,0), "",true);
7087     // Verify that formula is still using the previous table column name.
7088     ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7089 
7090     // === header-less ===
7091 
7092     {
7093         ScDBCollection* pDBs = m_pDoc->GetDBCollection();
7094         CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
7095 
7096         // Insert "headerless" database range definition for E10:F12, without headers.
7097         std::unique_ptr<ScDBData> pData(new ScDBData( "hltable", 0, 4,9, 5,11, true, false));
7098         bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
7099         CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"hltable\" database range.", bInserted);
7100     }
7101 
7102     {
7103         // Populate "hltable" database range with data in E10:F12
7104         const char* aData[][2] = {
7105             { "1", "2" },
7106             { "4", "8" },
7107             { "16", "32" }
7108         };
7109         ScAddress aPos(4,9,0);
7110         ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
7111         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7112     }
7113 
7114     // Named expressions that use header-less Table structured references.
7115     static const struct {
7116         const char* pName;
7117         const char* pExpr;
7118         const char* pCounta; // expected result when used in row 10 (first data row) as argument to COUNTA()
7119         const char* pSum3;   // expected result when used in row 11 (second data row) as argument to SUM().
7120         const char* pSum4;   // expected result when used in row 12 (third data row) as argument to SUM().
7121         const char* pSumX;   // expected result when used in row 13 (non-intersecting) as argument to SUM().
7122     } aHlNames[] = {
7123         { "hl_all",                          "hltable[[#All]]",                          "6", "63", "63", "63" },
7124         { "hl_data_implicit",                "hltable[]",                                "6", "63", "63", "63" },
7125         { "hl_data",                         "hltable[[#Data]]",                         "6", "63", "63", "63" },
7126         { "hl_headers",                      "hltable[[#Headers]]",                      "1", "#REF!", "#REF!", "#REF!" },
7127         { "hl_column1",                      "hltable[[Column1]]",                       "3", "21", "21", "21" },
7128         { "hl_column2",                      "hltable[[Column2]]",                       "3", "42", "42", "42" },
7129         { "hl_data_column1",                 "hltable[[#Data];[Column1]]",               "3", "21", "21", "21" },
7130         { "hl_data_column2",                 "hltable[[#Data];[Column2]]",               "3", "42", "42", "42" },
7131         { "hl_this_row",                     "hltable[[#This Row]]",                     "2", "12", "48", "#VALUE!" },
7132         { "hl_this_row_column1",             "hltable[[#This Row];[Column1]]",           "1",  "4", "16", "#VALUE!" },
7133         { "hl_this_row_column2",             "hltable[[#This Row];[Column2]]",           "1",  "8", "32", "#VALUE!" },
7134         { "hl_this_row_range_column_1_to_2", "hltable[[#This Row];[Column1]:[Column2]]", "2", "12", "48", "#VALUE!" }
7135     };
7136 
7137     {
7138         // Insert named expressions.
7139         ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
7140         CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
7141 
7142         for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7143         {
7144             // Choose base position that does not intersect with the database
7145             // range definition to test later use of [#This Row] results in
7146             // proper rows.
7147             ScRangeData* pName = new ScRangeData(
7148                     m_pDoc, OUString::createFromAscii(aHlNames[i].pName), OUString::createFromAscii(aHlNames[i].pExpr),
7149                     ScAddress(6,12,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
7150             bool bInserted = pGlobalNames->insert(pName);
7151             CPPUNIT_ASSERT_MESSAGE(
7152                     OString(OStringLiteral("Failed to insert named expression ") + aHlNames[i].pName +".").getStr(), bInserted);
7153         }
7154     }
7155 
7156     // Use the named expressions in COUNTA() formulas, on row 10 that intersects.
7157     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7158     {
7159         OUString aFormula( "=COUNTA(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7160         ScAddress aPos(7+i,9,0);
7161         m_pDoc->SetString( aPos, aFormula);
7162         // For easier "debugability" have position and formula in assertion.
7163         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7164         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pCounta)),
7165                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7166     }
7167 
7168     // Use the named expressions in SUM() formulas, on row 11 that intersects.
7169     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7170     {
7171         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7172         ScAddress aPos(7+i,10,0);
7173         m_pDoc->SetString( aPos, aFormula);
7174         // For easier "debugability" have position and formula in assertion.
7175         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7176         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum3)),
7177                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7178     }
7179 
7180     // Use the named expressions in SUM() formulas, on row 12 that intersects.
7181     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7182     {
7183         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7184         ScAddress aPos(7+i,11,0);
7185         m_pDoc->SetString( aPos, aFormula);
7186         // For easier "debugability" have position and formula in assertion.
7187         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7188         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum4)),
7189                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7190     }
7191 
7192     // Use the named expressions in SUM() formulas, on row 13 that does not intersect.
7193     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7194     {
7195         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7196         ScAddress aPos(7+i,12,0);
7197         m_pDoc->SetString( aPos, aFormula);
7198         // For easier "debugability" have position and formula in assertion.
7199         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7200         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSumX)),
7201                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7202     }
7203 
7204     // Insert a column at column F to extend database range from column E,F to
7205     // E,F,G. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7206     rDocFunc.InsertCells(ScRange(5,0,0,5,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
7207 
7208     // Re-verify the named expression in SUM() formula, on row 12 that
7209     // intersects, now starting at column I, still works.
7210     m_pDoc->CalcAll();
7211     for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7212     {
7213         OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7214         ScAddress aPos(8+i,11,0);
7215         // For easier "debugability" have position and formula in assertion.
7216         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7217         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum4)),
7218                 OUString(aPrefix + m_pDoc->GetString( aPos)));
7219     }
7220 
7221     const char* pColumn3Formula = "=SUM(hltable[[#Data];[Column3]])";
7222     {
7223         // Populate "hltable" database range with data in newly inserted
7224         // column, F10:F12 plus a table formula in F14. The new header should
7225         // result in the internal table column name "Column3" that is used in
7226         // the formula.
7227         const char* aData[][1] = {
7228             { "64" },
7229             { "128" },
7230             { "256" },
7231             { "" },
7232             { pColumn3Formula }
7233         };
7234         ScAddress aPos(5,9,0);
7235         ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
7236         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7237     }
7238 
7239     // Verify the formula result in F14 (64+128+256=448).
7240     {
7241         OUString aFormula( OUString::createFromAscii( pColumn3Formula));
7242         ScAddress aPos(5,13,0);
7243         OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7244         CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + "448"), OUString(aPrefix + m_pDoc->GetString(aPos)));
7245     }
7246 
7247     m_pDoc->DeleteTab(0);
7248 }
7249 
testFuncFTEST()7250 void Test::testFuncFTEST()
7251 {
7252     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7253 
7254     m_pDoc->InsertTab(0, "FTest");
7255 
7256     ScAddress aPos(6,0,0);
7257     m_pDoc->SetString(aPos, "=FTEST(A1:C3;D1:F3)");
7258     m_pDoc->SetValue(0, 0, 0, 9.0); // A1
7259     OUString aVal = m_pDoc->GetString(aPos);
7260     CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7261             OUString("#VALUE!"), aVal);
7262     m_pDoc->SetValue(0, 1, 0, 8.0); // A2
7263     aVal = m_pDoc->GetString(aPos);
7264     CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7265             OUString("#VALUE!"), aVal);
7266     m_pDoc->SetValue(3, 0, 0, 5.0); // D1
7267     aVal = m_pDoc->GetString(aPos);
7268     CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7269             OUString("#VALUE!"), aVal);
7270     m_pDoc->SetValue(3, 1, 0, 6.0); // D2
7271     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 1.0000, m_pDoc->GetValue(aPos), 10e-4);
7272     m_pDoc->SetValue(1, 0, 0, 6.0); // B1
7273     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.6222, m_pDoc->GetValue(aPos), 10e-4);
7274     m_pDoc->SetValue(1, 1, 0, 8.0); // B2
7275     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.7732, m_pDoc->GetValue(aPos), 10e-4);
7276     m_pDoc->SetValue(4, 0, 0, 7.0); // E1
7277     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.8194, m_pDoc->GetValue(aPos), 10e-4);
7278     m_pDoc->SetValue(4, 1, 0, 4.0); // E2
7279     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9674, m_pDoc->GetValue(aPos), 10e-4);
7280     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7281     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.3402, m_pDoc->GetValue(aPos), 10e-4);
7282     m_pDoc->SetValue(5, 0, 0, 28.0); // F1
7283     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc->GetValue(aPos), 10e-4);
7284     m_pDoc->SetValue(2, 1, 0, 9.0); // C2
7285     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0063, m_pDoc->GetValue(aPos), 10e-4);
7286     m_pDoc->SetValue(5, 1, 0, 4.0); // F2
7287     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0081, m_pDoc->GetValue(aPos), 10e-4);
7288     m_pDoc->SetValue(0, 2, 0, 2.0); // A3
7289     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0122, m_pDoc->GetValue(aPos), 10e-4);
7290     m_pDoc->SetValue(3, 2, 0, 8.0); // D3
7291     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0178, m_pDoc->GetValue(aPos), 10e-4);
7292     m_pDoc->SetValue(1, 2, 0, 4.0); // B3
7293     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0093, m_pDoc->GetValue(aPos), 10e-4);
7294     m_pDoc->SetValue(4, 2, 0, 7.0); // E3
7295     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0132, m_pDoc->GetValue(aPos), 10e-4);
7296     m_pDoc->SetValue(5, 2, 0, 5.0); // F3
7297     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0168, m_pDoc->GetValue(aPos), 10e-4);
7298     m_pDoc->SetValue(2, 2, 0, 13.0); // C3
7299     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0422, m_pDoc->GetValue(aPos), 10e-4);
7300 
7301     m_pDoc->SetString(0, 2, 0, "a"); // A3
7302     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0334, m_pDoc->GetValue(aPos), 10e-4);
7303     m_pDoc->SetString(2, 0, 0, "b"); // C1
7304     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0261, m_pDoc->GetValue(aPos), 10e-4);
7305     m_pDoc->SetString(5, 1, 0, "c"); // F2
7306     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0219, m_pDoc->GetValue(aPos), 10e-4);
7307     m_pDoc->SetString(4, 2, 0, "d"); // E3
7308     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc->GetValue(aPos), 10e-4);
7309     m_pDoc->SetString(3, 2, 0, "e"); // D3
7310     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0110, m_pDoc->GetValue(aPos), 10e-4);
7311 
7312     m_pDoc->DeleteTab(0);
7313     m_pDoc->InsertTab(0, "FTest2");
7314 
7315     /* Summary of the following test
7316        A1:A5   =  SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7317        A6:A10  = -SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7318        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 };
7319        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 };
7320        C1      =  POWER(1.5, D1)   ; This is going to be the sample variance of the vector A1:A10
7321        C2      =  POWER(1.5, D2)   ; This is going to be the sample variance of the vector B1:B20
7322        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 }
7323 
7324        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.
7325 
7326        The minimum variance ratio obtained in this way is 0.017342 and the maximum variance ratio is 57.665039
7327     */
7328 
7329     const size_t nNumParams = 11;
7330     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 };
7331 
7332     // Results of var_test() from Octave
7333     const double fResults[nNumParams][nNumParams] = {
7334         { 0.9451191535603041,0.5429768686792684,0.213130093422756,0.06607644828558357,0.0169804365506927,0.003790723514148109,
7335           0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06,7.495090956766148e-07 },
7336         { 0.4360331979746912,0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927,
7337           0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06 },
7338         { 0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.2131300934227565,0.06607644828558357,
7339           0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05 },
7340         { 0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603058,0.5429768686792689,0.2131300934227565,
7341           0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777 },
7342         { 0.002886791075972228,0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603041,0.5429768686792689,
7343           0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703 },
7344         { 0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746912,0.9451191535603036,
7345           0.5429768686792689,0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109 },
7346         { 1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,
7347           0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927 },
7348         { 5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,
7349           0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.213130093422756,0.06607644828558357 },
7350         { 1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,
7351           0.02453502500565086,0.1309752286653509,0.4360331979746914,0.9451191535603041,0.5429768686792684,0.2131300934227565 },
7352         { 5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,
7353           0.002886791075972228,0.02453502500565108,0.1309752286653516,0.4360331979746914,0.9451191535603058,0.5429768686792684 },
7354         { 1.354649725726631e-11,5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,
7355           0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,0.9451191535603054 }
7356     };
7357 
7358     m_pDoc->SetValue(3, 0, 0, fParameter[0]); // D1
7359     m_pDoc->SetValue(3, 1, 0, fParameter[0]); // D2
7360     aPos.Set(2,0,0); // C1
7361     m_pDoc->SetString(aPos, "=POWER(1.5;D1)" ); // C1
7362     aPos.Set(2, 1, 0);     // C2
7363     m_pDoc->SetString(aPos, "=POWER(1.5;D2)" ); // C2
7364     for ( SCROW nRow = 0; nRow < 5; ++nRow )    // Set A1:A5  = SQRT(C1*9/10), and A6:A10 = -SQRT(C1*9/10)
7365     {
7366         aPos.Set(0, nRow, 0);
7367         m_pDoc->SetString(aPos, "=SQRT(C1*9/10)");
7368         aPos.Set(0, nRow + 5, 0);
7369         m_pDoc->SetString(aPos, "=-SQRT(C1*9/10)");
7370     }
7371 
7372     for ( SCROW nRow = 0; nRow < 10; ++nRow )    // Set B1:B10  = SQRT(C2*19/20), and B11:B20 = -SQRT(C2*19/20)
7373     {
7374         aPos.Set(1, nRow, 0);
7375         m_pDoc->SetString(aPos, "=SQRT(C2*19/20)");
7376         aPos.Set(1, nRow + 10, 0);
7377         m_pDoc->SetString(aPos, "=-SQRT(C2*19/20)");
7378     }
7379 
7380     aPos.Set(4, 0, 0); // E1
7381     m_pDoc->SetString(aPos, "=FTEST(A1:A10;B1:B20)");
7382     aPos.Set(4, 1, 0); // E2
7383     m_pDoc->SetString(aPos, "=FTEST(B1:B20;A1:A10)");
7384 
7385     ScAddress aPosRev(4, 1, 0); // E2
7386     aPos.Set(4, 0, 0);  // E1
7387 
7388     for ( size_t nFirstIdx = 0; nFirstIdx < nNumParams; ++nFirstIdx )
7389     {
7390         m_pDoc->SetValue(3, 0, 0, fParameter[nFirstIdx]); // Set D1
7391         for ( size_t nSecondIdx = 0; nSecondIdx < nNumParams; ++nSecondIdx )
7392         {
7393             m_pDoc->SetValue(3, 1, 0, fParameter[nSecondIdx]); // Set D2
7394             double fExpected = fResults[nFirstIdx][nSecondIdx];
7395             // 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
7396             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected, m_pDoc->GetValue(aPos),    std::min(10e-5, fExpected*0.0001) );
7397             CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected, m_pDoc->GetValue(aPosRev), std::min(10e-5, fExpected*0.0001) );
7398         }
7399     }
7400     m_pDoc->DeleteTab(0);
7401 }
7402 
testFuncFTESTBug()7403 void Test::testFuncFTESTBug()
7404 {
7405     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7406 
7407     m_pDoc->InsertTab(0, "FTest");
7408 
7409     ScAddress aPos(9,0,0);
7410     m_pDoc->SetString(aPos, "=FTEST(H1:H3;I1:I3)");
7411 
7412     m_pDoc->SetValue(7, 0, 0, 9.0); // H1
7413     m_pDoc->SetValue(7, 1, 0, 8.0); // H2
7414     m_pDoc->SetValue(7, 2, 0, 6.0); // H3
7415     m_pDoc->SetValue(8, 0, 0, 5.0); // I1
7416     m_pDoc->SetValue(8, 1, 0, 7.0); // I2
7417     // tdf#93329
7418     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9046, m_pDoc->GetValue(aPos), 10e-4);
7419 
7420     m_pDoc->DeleteTab(0);
7421 }
7422 
testFuncCHITEST()7423 void Test::testFuncCHITEST()
7424 {
7425     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7426 
7427     m_pDoc->InsertTab(0, "ChiTest");
7428 
7429     ScAddress aPos(6,0,0);
7430     // 2x2 matrices test
7431     m_pDoc->SetString(aPos, "=CHITEST(A1:B2;D1:E2)");
7432     OUString aVal = m_pDoc->GetString(aPos);
7433     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with empty cells",
7434             OUString("Err:502"), aVal);
7435 
7436     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
7437     m_pDoc->SetValue(0, 1, 0, 2.0); // A2
7438     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
7439     m_pDoc->SetValue(1, 1, 0, 1.0); // B2
7440     aVal = m_pDoc->GetString(aPos);
7441     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrix with empty cells",
7442             OUString("Err:502"), aVal);
7443 
7444     m_pDoc->SetValue(3, 0, 0, 2.0); // D1
7445     m_pDoc->SetValue(3, 1, 0, 3.0); // D2
7446     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc->GetValue(aPos), 10e-4);
7447 
7448     m_pDoc->SetValue(4, 1, 0, 1.0); // E2
7449     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc->GetValue(aPos), 10e-4);
7450     m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7451     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.2801, m_pDoc->GetValue(aPos), 10e-4);
7452     m_pDoc->SetValue(4, 0, 0, 0.0); // E1
7453     aVal = m_pDoc->GetString(aPos);
7454     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0 for expected values of 0", OUString("#DIV/0!"), aVal);
7455     m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7456     m_pDoc->SetValue(1, 1, 0, 0.0); // B2
7457     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1410, m_pDoc->GetValue(aPos), 10e-4);
7458 
7459     // 3x3 matrices test
7460     m_pDoc->SetString(aPos, "=CHITEST(A1:C3;D1:F3)");
7461     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.7051, m_pDoc->GetValue(aPos), 10e-4);
7462 
7463     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7464     m_pDoc->SetValue(2, 1, 0, 2.0); // C2
7465     m_pDoc->SetValue(2, 2, 0, 3.0); // C3
7466     m_pDoc->SetValue(0, 2, 0, 4.0); // A3
7467     m_pDoc->SetValue(1, 2, 0, 2.0); // B3
7468     m_pDoc->SetValue(5, 0, 0, 1.0); // F1
7469     m_pDoc->SetValue(5, 1, 0, 2.0); // F2
7470     m_pDoc->SetValue(5, 2, 0, 3.0); // F3
7471     m_pDoc->SetValue(3, 2, 0, 3.0); // D3
7472     m_pDoc->SetValue(4, 2, 0, 1.0); // E3
7473     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc->GetValue(aPos), 10e-4);
7474 
7475     // test with strings
7476     m_pDoc->SetString(4, 2, 0, "a"); // E3
7477     aVal = m_pDoc->GetString(aPos);
7478     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
7479             OUString("Err:502"), aVal);
7480     m_pDoc->SetString(1, 2, 0, "a"); // B3
7481     aVal = m_pDoc->GetString(aPos);
7482     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
7483             OUString("Err:502"), aVal);
7484     m_pDoc->SetValue(4, 2, 0, 1.0); // E3
7485     aVal = m_pDoc->GetString(aPos);
7486     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
7487             OUString("Err:502"), aVal);
7488     m_pDoc->SetValue(1, 2, 0, 2.0); // B3
7489     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc->GetValue(aPos), 10e-4);
7490 
7491     m_pDoc->SetValue(4, 1, 0, 5.0); // E2
7492     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0215, m_pDoc->GetValue(aPos), 10e-4);
7493     m_pDoc->SetValue(1, 2, 0, 1.0); // B3
7494     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0328, m_pDoc->GetValue(aPos), 10e-4);
7495     m_pDoc->SetValue(5, 0, 0, 3.0); // F1
7496     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1648, m_pDoc->GetValue(aPos), 10e-4);
7497     m_pDoc->SetValue(0, 1, 0, 3.0); // A2
7498     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1870, m_pDoc->GetValue(aPos), 10e-4);
7499     m_pDoc->SetValue(3, 1, 0, 5.0); // D2
7500     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1377, m_pDoc->GetValue(aPos), 10e-4);
7501     m_pDoc->SetValue(3, 2, 0, 4.0); // D3
7502     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1566, m_pDoc->GetValue(aPos), 10e-4);
7503 
7504     m_pDoc->SetValue(0, 0, 0, 0.0); // A1
7505     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0868, m_pDoc->GetValue(aPos), 10e-4);
7506 
7507     // no convergence error
7508     m_pDoc->SetValue(4, 0, 0, 1.0E308); // E1
7509     aVal = m_pDoc->GetString(aPos);
7510     CPPUNIT_ASSERT_EQUAL(OUString("Err:523"), aVal);
7511     m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7512 
7513     // zero in all cells
7514     m_pDoc->SetValue(0, 1, 0, 0.0); // A2
7515     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0150, m_pDoc->GetValue(aPos), 10e-4);
7516     m_pDoc->SetValue(0, 2, 0, 0.0); // A3
7517     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0026, m_pDoc->GetValue(aPos), 10e-4);
7518     m_pDoc->SetValue(1, 0, 0, 0.0); // B1
7519     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.00079, m_pDoc->GetValue(aPos), 10e-5);
7520     m_pDoc->SetValue(1, 2, 0, 0.0); // B3
7521     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0005, m_pDoc->GetValue(aPos), 10e-4);
7522     m_pDoc->SetValue(2, 0, 0, 0.0); // C1
7523     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0001, m_pDoc->GetValue(aPos), 10e-4);
7524     m_pDoc->SetValue(2, 1, 0, 0.0); // C2
7525     m_pDoc->SetValue(2, 2, 0, 0.0); // C3
7526     m_pDoc->SetValue(3, 0, 0, 0.0); // D1
7527     aVal = m_pDoc->GetString(aPos);
7528     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
7529             OUString("#DIV/0!"), aVal);
7530     m_pDoc->SetValue(3, 1, 0, 0.0); // D2
7531     m_pDoc->SetValue(3, 2, 0, 0.0); // D3
7532     m_pDoc->SetValue(4, 0, 0, 0.0); // E1
7533     m_pDoc->SetValue(4, 1, 0, 0.0); // E2
7534     m_pDoc->SetValue(4, 2, 0, 0.0); // E3
7535     m_pDoc->SetValue(5, 0, 0, 0.0); // F1
7536     m_pDoc->SetValue(5, 1, 0, 0.0); // F2
7537     m_pDoc->SetValue(5, 2, 0, 0.0); // F3
7538     aVal = m_pDoc->GetString(aPos);
7539     CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
7540             OUString("#DIV/0!"), aVal);
7541 
7542     m_pDoc->DeleteTab(0);
7543 }
7544 
testFuncTTEST()7545 void Test::testFuncTTEST()
7546 {
7547     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7548 
7549     m_pDoc->InsertTab(0, "TTest");
7550 
7551     ScAddress aPos(6,0,0);
7552     // type 1, mode/tails 1
7553     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;1;1)");
7554     OUString aVal = m_pDoc->GetString(aPos);
7555     CPPUNIT_ASSERT_EQUAL_MESSAGE("TTEST should return #VALUE! for empty matrices",
7556             OUString("#VALUE!"), aVal);
7557 
7558     m_pDoc->SetValue(0, 0, 0, 8.0); // A1
7559     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
7560     m_pDoc->SetValue(3, 0, 0, 3.0); // D1
7561     m_pDoc->SetValue(4, 0, 0, 1.0); // E1
7562     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc->GetValue(aPos), 10e-5);
7563     m_pDoc->SetValue(2, 0, 0, 1.0); // C1
7564     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc->GetValue(aPos), 10e-5);
7565     m_pDoc->SetValue(5, 0, 0, 6.0); // F1
7566     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc->GetValue(aPos), 10e-5);
7567     m_pDoc->SetValue(0, 1, 0, -4.0); // A2
7568     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc->GetValue(aPos), 10e-5);
7569     m_pDoc->SetValue(3, 1, 0, 1.0); // D2
7570     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc->GetValue(aPos), 10e-5);
7571     m_pDoc->SetValue(1, 1, 0, 5.0); // B2
7572     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc->GetValue(aPos), 10e-5);
7573     m_pDoc->SetValue(4, 1, 0, -2.0); // E2
7574     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc->GetValue(aPos), 10e-5);
7575     m_pDoc->SetValue(2, 1, 0, -1.0); // C2
7576     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc->GetValue(aPos), 10e-5);
7577     m_pDoc->SetValue(5, 1, 0, -3.0); // F2
7578     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc->GetValue(aPos), 10e-5);
7579     m_pDoc->SetValue(0, 2, 0, 10.0); // A3
7580     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc->GetValue(aPos), 10e-5);
7581     m_pDoc->SetValue(3, 2, 0, 10.0); // D3
7582     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc->GetValue(aPos), 10e-5);
7583     m_pDoc->SetValue(1, 2, 0, 3.0); // B3
7584     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc->GetValue(aPos), 10e-5);
7585     m_pDoc->SetValue(4, 2, 0, 9.0); // E3
7586     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc->GetValue(aPos), 10e-5);
7587     m_pDoc->SetValue(2, 2, 0, -5.0); // C3
7588     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc->GetValue(aPos), 10e-5);
7589     m_pDoc->SetValue(5, 2, 0, 6.0); // F3
7590     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25529, m_pDoc->GetValue(aPos), 10e-5);
7591 
7592     m_pDoc->SetString(1, 1, 0, "a"); // B2
7593     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc->GetValue(aPos), 10e-5);
7594     m_pDoc->SetString(4, 1, 0, "b"); // E2
7595     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc->GetValue(aPos), 10e-5);
7596     m_pDoc->SetString(2, 2, 0, "c"); // C3
7597     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25030, m_pDoc->GetValue(aPos), 10e-5);
7598     m_pDoc->SetString(5, 1, 0, "d"); // F2
7599     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.19637, m_pDoc->GetValue(aPos), 10e-5);
7600 
7601     // type 1, mode/tails 2
7602     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;2;1)");
7603     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc->GetValue(aPos), 10e-5);
7604     m_pDoc->SetValue(1, 1, 0, 4.0); // B2
7605     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc->GetValue(aPos), 10e-5);
7606     m_pDoc->SetValue(4, 1, 0, 3.0); // E2
7607     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43970, m_pDoc->GetValue(aPos), 10e-5);
7608     m_pDoc->SetValue(2, 2, 0, -2.0); // C3
7609     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22217, m_pDoc->GetValue(aPos), 10e-5);
7610     m_pDoc->SetValue(5, 1, 0, -10.0); // F2
7611     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.64668, m_pDoc->GetValue(aPos), 10e-5);
7612     m_pDoc->SetValue(0, 1, 0, 3.0); // A2
7613     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.95266, m_pDoc->GetValue(aPos), 10e-5);
7614     m_pDoc->SetValue(3, 2, 0, -1.0); // D3
7615     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62636, m_pDoc->GetValue(aPos), 10e-5);
7616 
7617     // type 2, mode/tails 2
7618     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;2;2)");
7619     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62549, m_pDoc->GetValue(aPos), 10e-5);
7620     m_pDoc->SetValue(5, 1, 0, -1.0); // F2
7621     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.94952, m_pDoc->GetValue(aPos), 10e-5);
7622     m_pDoc->SetValue(2, 2, 0, 5.0); // C3
7623     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.58876, m_pDoc->GetValue(aPos), 10e-5);
7624     m_pDoc->SetValue(2, 1, 0, 2.0); // C2
7625     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43205, m_pDoc->GetValue(aPos), 10e-5);
7626     m_pDoc->SetValue(3, 2, 0, -4.0); // D3
7627     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36165, m_pDoc->GetValue(aPos), 10e-5);
7628     m_pDoc->SetValue(0, 1, 0, 1.0); // A2
7629     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.44207, m_pDoc->GetValue(aPos), 10e-5);
7630 
7631     // type 3, mode/tails 1
7632     m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;1;3)");
7633     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22132, m_pDoc->GetValue(aPos), 10e-5);
7634     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
7635     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36977, m_pDoc->GetValue(aPos), 10e-5);
7636     m_pDoc->SetValue(0, 2, 0, -30.0); // A3
7637     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16871, m_pDoc->GetValue(aPos), 10e-5);
7638     m_pDoc->SetValue(3, 1, 0, 5.0); // D2
7639     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.14396, m_pDoc->GetValue(aPos), 10e-5);
7640     m_pDoc->SetValue(5, 1, 0, 2.0); // F2
7641     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12590, m_pDoc->GetValue(aPos), 10e-5);
7642     m_pDoc->SetValue(4, 2, 0, 2.0); // E3
7643     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16424, m_pDoc->GetValue(aPos), 10e-5);
7644     m_pDoc->SetValue(5, 0, 0, -1.0); // F1
7645     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.21472, m_pDoc->GetValue(aPos), 10e-5);
7646 
7647     m_pDoc->DeleteTab(0);
7648 }
7649 
testFuncSUMX2PY2()7650 void Test::testFuncSUMX2PY2()
7651 {
7652     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7653 
7654     m_pDoc->InsertTab(0, "SumX2PY2 Test");
7655 
7656     OUString aVal;
7657     ScAddress aPos(6,0,0);
7658     m_pDoc->SetString(aPos, "=SUMX2PY2(A1:C3;D1:F3)");
7659     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc->GetValue(aPos));
7660 
7661     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
7662     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc->GetValue(aPos));
7663     m_pDoc->SetValue(3, 0, 0, 2.0); // D1
7664     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc->GetValue(aPos));
7665     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
7666     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc->GetValue(aPos));
7667     m_pDoc->SetValue(4, 0, 0, 0.0); // E1
7668     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 9.0, m_pDoc->GetValue(aPos));
7669     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7670     m_pDoc->SetValue(5, 0, 0, 3.0); // F1
7671     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 27.0, m_pDoc->GetValue(aPos));
7672     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
7673     m_pDoc->SetValue(3, 1, 0, -10.0); // D2
7674     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc->GetValue(aPos));
7675     m_pDoc->SetValue(1, 1, 0, -5.0); // B2
7676     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc->GetValue(aPos));
7677     m_pDoc->SetValue(4, 1, 0, -5.0); // E2
7678     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc->GetValue(aPos));
7679     m_pDoc->SetValue(2, 1, 0, 0.0); // C2
7680     m_pDoc->SetValue(5, 1, 0, 0.0); // F2
7681     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc->GetValue(aPos));
7682     m_pDoc->SetValue(0, 2, 0, -8.0); // A3
7683     m_pDoc->SetValue(3, 2, 0, 8.0); // D3
7684     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc->GetValue(aPos));
7685     m_pDoc->SetValue(1, 2, 0, 0.0); // B3
7686     m_pDoc->SetValue(4, 2, 0, 0.0); // E3
7687     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc->GetValue(aPos));
7688     m_pDoc->SetValue(2, 2, 0, 1.0); // C3
7689     m_pDoc->SetValue(5, 2, 0, 1.0); // F3
7690     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 407.0, m_pDoc->GetValue(aPos));
7691 
7692     // add some strings
7693     m_pDoc->SetString(4, 1, 0, "a"); // E2
7694     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc->GetValue(aPos));
7695     m_pDoc->SetString(1, 1, 0, "a"); // B2
7696     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc->GetValue(aPos));
7697     m_pDoc->SetString(0, 0, 0, "a"); // A1
7698     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc->GetValue(aPos));
7699     m_pDoc->SetString(3, 0, 0, "a"); // D1
7700     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc->GetValue(aPos));
7701 
7702     m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3};{2;3;4})");
7703     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 43.0, m_pDoc->GetValue(aPos));
7704     m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3};{2;3})");
7705     aVal = m_pDoc->GetString(aPos);
7706     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 should return #VALUE! for matrices with different sizes",
7707             OUString("#VALUE!"), aVal);
7708     m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3})");
7709     aVal = m_pDoc->GetString(aPos);
7710     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 needs two parameters",
7711             OUString("Err:511"), aVal);
7712 
7713     m_pDoc->DeleteTab(0);
7714 }
7715 
testFuncSUMX2MY2()7716 void Test::testFuncSUMX2MY2()
7717 {
7718     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7719 
7720     m_pDoc->InsertTab(0, "SumX2MY2 Test");
7721 
7722     OUString aVal;
7723     ScAddress aPos(6,0,0);
7724     m_pDoc->SetString(aPos, "=SUMX2MY2(A1:C3;D1:F3)");
7725     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc->GetValue(aPos));
7726 
7727     m_pDoc->SetValue(0, 0, 0, 10.0); // A1
7728     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc->GetValue(aPos));
7729     m_pDoc->SetValue(3, 0, 0, -9.0); // D1
7730     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc->GetValue(aPos));
7731     m_pDoc->SetValue(1, 0, 0, 2.0); // B1
7732     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc->GetValue(aPos));
7733     m_pDoc->SetValue(4, 0, 0, 1.0); // E1
7734     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7735     m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7736     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7737     m_pDoc->SetValue(5, 0, 0, 3.0); // F1
7738     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7739     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
7740     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7741     m_pDoc->SetValue(3, 1, 0, -10.0); // D2
7742     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7743     m_pDoc->SetValue(1, 1, 0, -5.0); // B2
7744     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7745     m_pDoc->SetValue(4, 1, 0, -5.0); // E2
7746     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7747     m_pDoc->SetValue(2, 1, 0, -3.0); // C2
7748     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7749     m_pDoc->SetValue(5, 1, 0, 3.0); // F2
7750     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7751     m_pDoc->SetValue(0, 2, 0, -8.0); // A3
7752     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
7753     m_pDoc->SetValue(3, 2, 0, 3.0); // D3
7754     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc->GetValue(aPos));
7755     m_pDoc->SetValue(1, 2, 0, 2.0); // B3
7756     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc->GetValue(aPos));
7757     m_pDoc->SetValue(4, 2, 0, -6.0); // E3
7758     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
7759     m_pDoc->SetValue(2, 2, 0, -4.0); // C3
7760     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
7761     m_pDoc->SetValue(5, 2, 0, 6.0); // F3
7762     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 25.0, m_pDoc->GetValue(aPos));
7763 
7764     // add some strings
7765     m_pDoc->SetString(5, 2, 0, "a"); // F3
7766     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
7767     m_pDoc->SetString(0, 2, 0, "a"); // A3
7768     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -10.0, m_pDoc->GetValue(aPos));
7769     m_pDoc->SetString(1, 0, 0, "a"); // B1
7770     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -13.0, m_pDoc->GetValue(aPos));
7771     m_pDoc->SetString(3, 0, 0, "a"); // D1
7772     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -32.0, m_pDoc->GetValue(aPos));
7773 
7774     m_pDoc->SetString(aPos, "=SUMX2MY2({1;3;5};{0;4;4})");
7775     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc->GetValue(aPos));
7776     m_pDoc->SetString(aPos, "=SUMX2MY2({1;-3;-5};{0;-4;4})");
7777     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc->GetValue(aPos));
7778     m_pDoc->SetString(aPos, "=SUMX2MY2({9;5;1};{3;-3;3})");
7779     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 80.0, m_pDoc->GetValue(aPos));
7780     m_pDoc->SetString(aPos, "=SUMX2MY2({1;2;3};{2;3})");
7781     aVal = m_pDoc->GetString(aPos);
7782     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 should return #VALUE! for matrices with different sizes",
7783             OUString("#VALUE!"), aVal);
7784     m_pDoc->SetString(aPos, "=SUMX2MY2({1;2;3})");
7785     aVal = m_pDoc->GetString(aPos);
7786     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 needs two parameters",
7787             OUString("Err:511"), aVal);
7788 
7789     m_pDoc->DeleteTab(0);
7790 }
7791 
testFuncGCD()7792 void Test::testFuncGCD()
7793 {
7794     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7795 
7796     m_pDoc->InsertTab(0, "GCDTest");
7797 
7798     OUString aVal;
7799     ScAddress aPos(4,0,0);
7800 
7801     m_pDoc->SetString(aPos, "=GCD(A1)");
7802     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc->GetValue(aPos));
7803     m_pDoc->SetValue(0, 0, 0, 10.0); // A1
7804     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
7805     m_pDoc->SetValue(0, 0, 0, -2.0); // A1
7806     aVal = m_pDoc->GetString(aPos);
7807     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for values less than 0",
7808             OUString("Err:502"), aVal);
7809     m_pDoc->SetString(0, 0, 0, "a"); // A1
7810     aVal = m_pDoc->GetString(aPos);
7811     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for a single string",
7812             OUString("#VALUE!"), aVal);
7813 
7814     m_pDoc->SetString(aPos, "=GCD(A1:B2)");
7815     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc->GetValue(aPos));
7816     m_pDoc->SetValue(0, 1, 0, -12.0); // B1
7817     aVal = m_pDoc->GetString(aPos);
7818     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for a matrix with values less than 0",
7819             OUString("Err:502"), aVal);
7820     m_pDoc->SetValue(0, 0, 0, 15.0); // A1
7821     m_pDoc->SetValue(0, 1, 0, 0.0); // B1
7822     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 15.0, m_pDoc->GetValue(aPos));
7823     m_pDoc->SetValue(1, 0, 0, 5.0); // B1
7824     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7825     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
7826     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7827     m_pDoc->SetValue(1, 0, 0, 30.0); // B1
7828     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7829     m_pDoc->SetValue(0, 0, 0, 20.0); // A1
7830     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
7831     m_pDoc->SetValue(1, 1, 0, 120.0); // B2
7832     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
7833     m_pDoc->SetValue(0, 1, 0, 80.0); // A2
7834     m_pDoc->SetValue(1, 0, 0, 40.0); // B1
7835     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 20.0, m_pDoc->GetValue(aPos));
7836     m_pDoc->SetValue(1, 0, 0, 45.0); // B1
7837     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7838 
7839     // with floor
7840     m_pDoc->SetValue(1, 0, 0, 45.381); // B1
7841     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7842     m_pDoc->SetValue(1, 1, 0, 120.895); // B2
7843     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7844     m_pDoc->SetValue(0, 0, 0, 20.97); // A1
7845     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7846     m_pDoc->SetValue(0, 1, 0, 10.15); // A2
7847     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
7848 
7849     // inline array
7850     m_pDoc->SetString(aPos, "=GCD({3;6;9})");
7851     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc->GetValue(aPos));
7852     m_pDoc->SetString(aPos, "=GCD({150;0})");
7853     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc->GetValue(aPos));
7854     m_pDoc->SetString(aPos, "=GCD({-3;6;9})");
7855     aVal = m_pDoc->GetString(aPos);
7856     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
7857             OUString("Err:502"), aVal);
7858     m_pDoc->SetString(aPos, "=GCD({\"a\";6;9})");
7859     aVal = m_pDoc->GetString(aPos);
7860     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
7861             OUString("Err:502"), aVal);
7862 
7863     //many inline array
7864     m_pDoc->SetString(aPos, "=GCD({6;6;6};{3;6;9})");
7865     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc->GetValue(aPos));
7866     m_pDoc->SetString(aPos, "=GCD({300;300;300};{150;0})");
7867     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc->GetValue(aPos));
7868     m_pDoc->SetString(aPos,"=GCD({3;6;9};{3;-6;9})");
7869     aVal = m_pDoc->GetString(aPos);
7870     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
7871             OUString("Err:502"), aVal);
7872     m_pDoc->SetString(aPos, "=GCD({3;6;9};{\"a\";6;9})");
7873     aVal = m_pDoc->GetString(aPos);
7874     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
7875             OUString("Err:502"), aVal);
7876 
7877     // inline list of values
7878     m_pDoc->SetString(aPos, "=GCD(12;24;36;48;60)");
7879     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc->GetValue(aPos));
7880     m_pDoc->SetString(aPos, "=GCD(0;12;24;36;48;60)");
7881     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc->GetValue(aPos));
7882     m_pDoc->SetString(aPos, "=GCD(\"a\";1)");
7883     aVal = m_pDoc->GetString(aPos);
7884     CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for an array with strings",
7885             OUString("#VALUE!"), aVal);
7886 
7887     m_pDoc->DeleteTab(0);
7888 }
7889 
testFuncLCM()7890 void Test::testFuncLCM()
7891 {
7892     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7893 
7894     m_pDoc->InsertTab(0, "LCMTest");
7895 
7896     OUString aVal;
7897     ScAddress aPos(4,0,0);
7898 
7899     m_pDoc->SetString(aPos, "=LCM(A1)");
7900     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
7901     m_pDoc->SetValue(0, 0, 0, 10.0); // A1
7902     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 10.0, m_pDoc->GetValue(aPos));
7903     m_pDoc->SetValue(0, 0, 0, -2.0); // A1
7904     aVal = m_pDoc->GetString(aPos);
7905     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for values less than 0",
7906             OUString("Err:502"), aVal);
7907     m_pDoc->SetString(0, 0, 0, "a"); // A1
7908     aVal = m_pDoc->GetString(aPos);
7909     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for a single string",
7910             OUString("#VALUE!"), aVal);
7911 
7912     m_pDoc->SetString(aPos, "=LCM(A1:B2)");
7913     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 1.0, m_pDoc->GetValue(aPos));
7914     m_pDoc->SetValue(0, 1, 0, -12.0); // B1
7915     aVal = m_pDoc->GetString(aPos);
7916     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for a matrix with values less than 0",
7917             OUString("Err:502"), aVal);
7918     m_pDoc->SetValue(0, 0, 0, 15.0); // A1
7919     m_pDoc->SetValue(0, 1, 0, 0.0); // A2
7920     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
7921     m_pDoc->SetValue(1, 0, 0, 5.0); // B1
7922     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
7923     m_pDoc->SetValue(0, 1, 0, 10.0); // A2
7924     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc->GetValue(aPos));
7925     m_pDoc->SetValue(1, 0, 0, 30.0); // B1
7926     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc->GetValue(aPos));
7927     m_pDoc->SetValue(0, 0, 0, 20.0); // A1
7928     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 60.0, m_pDoc->GetValue(aPos));
7929     m_pDoc->SetValue(1, 1, 0, 125.0); // B2
7930     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1500.0, m_pDoc->GetValue(aPos));
7931     m_pDoc->SetValue(1, 0, 0, 99.0); // B1
7932     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 49500.0, m_pDoc->GetValue(aPos));
7933     m_pDoc->SetValue(0, 1, 0, 37.0); // A2
7934     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc->GetValue(aPos));
7935 
7936     // with floor
7937     m_pDoc->SetValue(1, 0, 0, 99.89); // B1
7938     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc->GetValue(aPos));
7939     m_pDoc->SetValue(1, 1, 0, 11.32); // B2
7940     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 73260.0, m_pDoc->GetValue(aPos));
7941     m_pDoc->SetValue(0, 0, 0, 22.58); // A1
7942     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 7326.0, m_pDoc->GetValue(aPos));
7943     m_pDoc->SetValue(0, 1, 0, 3.99); // A2
7944     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 198.0, m_pDoc->GetValue(aPos));
7945 
7946     // inline array
7947     m_pDoc->SetString(aPos, "=LCM({3;6;9})");
7948     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc->GetValue(aPos));
7949     m_pDoc->SetString(aPos, "=LCM({150;0})");
7950     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
7951     m_pDoc->SetString(aPos, "=LCM({-3;6;9})");
7952     aVal = m_pDoc->GetString(aPos);
7953     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
7954             OUString("Err:502"), aVal);
7955     m_pDoc->SetString(aPos, "=LCM({\"a\";6;9})");
7956     aVal = m_pDoc->GetString(aPos);
7957     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
7958             OUString("Err:502"), aVal);
7959 
7960         //many inline array
7961     m_pDoc->SetString(aPos, "=LCM({6;6;6};{3;6;9})");
7962     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc->GetValue(aPos));
7963     m_pDoc->SetString(aPos, "=LCM({300;300;300};{150;0})");
7964     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
7965     m_pDoc->SetString(aPos,"=LCM({3;6;9};{3;-6;9})");
7966     aVal = m_pDoc->GetString(aPos);
7967     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
7968             OUString("Err:502"), aVal);
7969     m_pDoc->SetString(aPos, "=LCM({3;6;9};{\"a\";6;9})");
7970     aVal = m_pDoc->GetString(aPos);
7971     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
7972             OUString("Err:502"), aVal);
7973 
7974     m_pDoc->SetString(aPos, "=LCM(12;24;36;48;60)");
7975     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 720.0, m_pDoc->GetValue(aPos));
7976     m_pDoc->SetString(aPos, "=LCM(0;12;24;36;48;60)");
7977     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
7978     m_pDoc->SetString(aPos, "=LCM(\"a\";1)");
7979     aVal = m_pDoc->GetString(aPos);
7980     CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for an array with strings",
7981             OUString("#VALUE!"), aVal);
7982 
7983     m_pDoc->DeleteTab(0);
7984 }
7985 
testFuncSUMSQ()7986 void Test::testFuncSUMSQ()
7987 {
7988     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7989 
7990     m_pDoc->InsertTab(0, "SUMSQTest");
7991 
7992     ScAddress aPos(4,0,0);
7993 
7994     m_pDoc->SetString(aPos, "=SUMSQ(A1)");
7995     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 0.0, m_pDoc->GetValue(aPos));
7996     m_pDoc->SetValue(0, 0, 0, 1.0); // A1
7997     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
7998     m_pDoc->SetValue(0, 0, 0, -1.0); // A1
7999     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8000     m_pDoc->SetValue(0, 1, 0, -2.0); // A2
8001     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8002 
8003     m_pDoc->SetString(aPos, "=SUMSQ(A1:A3)");
8004     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc->GetValue(aPos));
8005     m_pDoc->SetValue(1, 0, 0, 3.0); // B1
8006     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc->GetValue(aPos));
8007     m_pDoc->SetString(aPos, "=SUMSQ(A1:C3)");
8008     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc->GetValue(aPos));
8009     m_pDoc->SetValue(1, 1, 0, -4.0); // B2
8010     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 30.0, m_pDoc->GetValue(aPos));
8011     m_pDoc->SetString(1, 2, 0, "a"); // B3
8012     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc->GetValue(aPos));
8013     m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8014     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc->GetValue(aPos));
8015     m_pDoc->SetValue(0, 2, 0, 6.0); // A3
8016     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 66.0, m_pDoc->GetValue(aPos));
8017     m_pDoc->SetValue(2, 0, 0, -5.0); // C1
8018     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 91.0, m_pDoc->GetValue(aPos));
8019     m_pDoc->SetValue(2, 1, 0, 3.0); // C2
8020     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 100.0, m_pDoc->GetValue(aPos));
8021     m_pDoc->SetValue(2, 2, 0, 2.0); // C3
8022     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 104.0, m_pDoc->GetValue(aPos));
8023 
8024     // inline array
8025     m_pDoc->SetString(aPos, "=SUMSQ({1;2;3})");
8026     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc->GetValue(aPos));
8027     m_pDoc->SetString(aPos, "=SUMSQ({3;6;9})");
8028     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 126.0, m_pDoc->GetValue(aPos));
8029     m_pDoc->SetString(aPos, "=SUMSQ({15;0})");
8030     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 225.0, m_pDoc->GetValue(aPos));
8031     m_pDoc->SetString(aPos, "=SUMSQ({-3;3;1})");
8032     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 19.0, m_pDoc->GetValue(aPos));
8033     m_pDoc->SetString(aPos, "=SUMSQ({\"a\";-4;-5})");
8034     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 41.0, m_pDoc->GetValue(aPos));
8035 
8036     m_pDoc->SetString(aPos, "=SUMSQ({2;3};{4;5})");
8037     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 54.0, m_pDoc->GetValue(aPos));
8038     m_pDoc->SetString(aPos, "=SUMSQ({-3;3;1};{-1})");
8039     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 20.0, m_pDoc->GetValue(aPos));
8040     m_pDoc->SetString(aPos, "=SUMSQ({-4};{1;4;2};{-5;7};{9})");
8041     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 192.0, m_pDoc->GetValue(aPos));
8042     m_pDoc->SetString(aPos, "=SUMSQ({-2;2};{1};{-1};{0;0;0;4})");
8043     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc->GetValue(aPos));
8044 
8045     m_pDoc->SetString(aPos, "=SUMSQ(4;1;-3)");
8046     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc->GetValue(aPos));
8047     m_pDoc->SetString(aPos, "=SUMSQ(0;5;13;-7;-4)");
8048     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 259.0, m_pDoc->GetValue(aPos));
8049     m_pDoc->SetString(aPos, "=SUMSQ(0;12;24;36;48;60)");
8050     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc->GetValue(aPos));
8051     m_pDoc->SetString(aPos, "=SUMSQ(0;-12;-24;36;-48;60)");
8052     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc->GetValue(aPos));
8053     m_pDoc->SetString(aPos, "=SUMSQ(\"a\";1;\"d\";-4;2)");
8054     OUString aVal = m_pDoc->GetString(aPos);
8055     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMSQ should return #VALUE! for an array with strings",
8056             OUString("#VALUE!"), aVal);
8057 
8058     m_pDoc->DeleteTab(0);
8059 }
8060 
testFuncMDETERM()8061 void Test::testFuncMDETERM()
8062 {
8063     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8064 
8065     m_pDoc->InsertTab(0, "MDETERM_test");
8066     ScAddress aPos(8,0,0);
8067     OUString const aColCodes("ABCDEFGH");
8068     OUString const aFormulaTemplate("=MDETERM(A1:B2)");
8069     OUStringBuffer aFormulaBuffer(aFormulaTemplate);
8070     for( SCSIZE nSize = 3; nSize <= 8; nSize++ )
8071     {
8072         double fVal = 1.0;
8073         // Generate a singular integer matrix
8074         for( SCROW nRow = 0; nRow < static_cast<SCROW>(nSize); nRow++ )
8075         {
8076             for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(nSize); nCol++ )
8077             {
8078                 m_pDoc->SetValue(nCol, nRow, 0, fVal);
8079                 fVal += 1.0;
8080             }
8081         }
8082         aFormulaBuffer[12] = aColCodes[nSize-1];
8083         aFormulaBuffer[13] = static_cast<sal_Unicode>( '0' + nSize );
8084         m_pDoc->SetString(aPos, aFormulaBuffer.toString());
8085 
8086 #if SAL_TYPES_SIZEOFPOINTER == 4
8087         // On crappy 32-bit targets, presumably without extended precision on
8088         // interim results or optimization not catching it, this test fails
8089         // when comparing to 0.0, so have a narrow error margin. See also
8090         // commit message of 8140309d636d4a870875f2dd75ed3dfff2c0fbaf
8091         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8092                 0.0, m_pDoc->GetValue(aPos), 1e-12);
8093 #else
8094         // Even on one (and only one) x86_64 target the result was
8095         // 6.34413156928661e-17 instead of 0.0 (tdf#99730) so lower the bar to
8096         // 10e-14.
8097         // Then again on aarch64, ppc64* and s390x it also fails.
8098         // Sigh... why do we even test this? The original complaint in tdf#32834
8099         // was about -9.51712667007776E-016
8100         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8101                 0.0, m_pDoc->GetValue(aPos), 1e-14);
8102 #endif
8103     }
8104 
8105     int const aVals[] = {23, 31, 13, 12, 34, 64, 34, 31, 98, 32, 33, 63, 45, 54, 65, 76};
8106     int nIdx = 0;
8107     for( SCROW nRow = 0; nRow < 4; nRow++ )
8108         for( SCCOL nCol = 0; nCol < 4; nCol++ )
8109             m_pDoc->SetValue(nCol, nRow, 0, static_cast<double>(aVals[nIdx++]));
8110     m_pDoc->SetString(aPos, "=MDETERM(A1:D4)");
8111     // Following test is conservative in the sense that on Linux x86_64 the error is less that 1.0E-9
8112     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for non-singular integer matrix",
8113                                          -180655.0, m_pDoc->GetValue(aPos), 1.0E-6);
8114     m_pDoc->DeleteTab(0);
8115 }
8116 
testFormulaErrorPropagation()8117 void Test::testFormulaErrorPropagation()
8118 {
8119     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8120 
8121     m_pDoc->InsertTab(0, "Sheet1");
8122 
8123     ScMarkData aMark(MAXROW, MAXCOL);
8124     aMark.SelectOneTable(0);
8125     ScAddress aPos, aPos2;
8126     const OUString aTRUE("TRUE");
8127     const OUString aFALSE("FALSE");
8128 
8129     aPos.Set(0,0,0);// A1
8130     m_pDoc->SetValue( aPos, 1.0);
8131     aPos.IncCol();  // B1
8132     m_pDoc->SetValue( aPos, 2.0);
8133     aPos.IncCol();
8134 
8135     aPos.IncRow();  // C2
8136     m_pDoc->SetString( aPos, "=ISERROR(A1:B1+3)");
8137     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8138 
8139     aPos.IncRow();  // C3
8140     m_pDoc->SetString( aPos, "=ISERROR(A1:B1+{3})");
8141     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8142     aPos.IncRow();  // C4
8143     aPos2 = aPos;
8144     aPos2.IncCol(); // D4
8145     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(A1:B1+{3})");
8146     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8147     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8148 
8149     aPos.IncRow();  // C5
8150     m_pDoc->SetString( aPos, "=ISERROR({1;\"x\"}+{3;4})");
8151     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8152     aPos.IncRow();  // C6
8153     aPos2 = aPos;
8154     aPos2.IncCol(); // D6
8155     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR({1;\"x\"}+{3;4})");
8156     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8157     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos2));
8158 
8159     aPos.IncRow();  // C7
8160     m_pDoc->SetString( aPos, "=ISERROR({\"x\";2}+{3;4})");
8161     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8162     aPos.IncRow();  // C8
8163     aPos2 = aPos;
8164     aPos2.IncCol(); // D8
8165     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR({\"x\";2}+{3;4})");
8166     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8167     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8168 
8169     aPos.IncRow();  // C9
8170     m_pDoc->SetString( aPos, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8171     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8172     aPos.IncRow();  // C10
8173     aPos2 = aPos;
8174     aPos2.IncCol(); // D10
8175     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8176     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8177     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos2));
8178 
8179     aPos.IncRow();  // C11
8180     m_pDoc->SetString( aPos, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8181     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8182     aPos.IncRow();  // C12
8183     aPos2 = aPos;
8184     aPos2.IncCol(); // D12
8185     m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8186     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8187     CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8188 
8189     m_pDoc->DeleteTab(0);
8190 }
8191 
8192 namespace {
8193 
8194 class ColumnTest
8195 {
8196     ScDocument * m_pDoc;
8197 
8198     const SCROW m_nTotalRows;
8199     const SCROW m_nStart1;
8200     const SCROW m_nEnd1;
8201     const SCROW m_nStart2;
8202     const SCROW m_nEnd2;
8203 
8204 public:
ColumnTest(ScDocument * pDoc,SCROW nTotalRows,SCROW nStart1,SCROW nEnd1,SCROW nStart2,SCROW nEnd2)8205     ColumnTest( ScDocument * pDoc, SCROW nTotalRows,
8206                 SCROW nStart1, SCROW nEnd1, SCROW nStart2, SCROW nEnd2 )
8207         : m_pDoc(pDoc), m_nTotalRows(nTotalRows)
8208         , m_nStart1(nStart1), m_nEnd1(nEnd1)
8209         , m_nStart2(nStart2), m_nEnd2(nEnd2)
8210     {}
8211 
operator ()(SCCOL nColumn,const OUString & rFormula,std::function<double (SCROW)> const & lExpected) const8212     void operator() ( SCCOL nColumn, const OUString& rFormula,
8213                       std::function<double(SCROW )> const & lExpected ) const
8214     {
8215         ScDocument aClipDoc(SCDOCMODE_CLIP);
8216         ScMarkData aMark(MAXROW, MAXCOL);
8217 
8218         ScAddress aPos(nColumn, m_nStart1, 0);
8219         m_pDoc->SetString(aPos, rFormula);
8220         ASSERT_DOUBLES_EQUAL( lExpected(m_nStart1), m_pDoc->GetValue(aPos) );
8221 
8222         // Copy formula cell to clipboard.
8223         ScClipParam aClipParam(aPos, false);
8224         aMark.SetMarkArea(aPos);
8225         m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
8226 
8227         // Paste it to first range.
8228         InsertDeleteFlags nFlags = InsertDeleteFlags::CONTENTS;
8229         ScRange aDestRange(nColumn, m_nStart1, 0, nColumn, m_nEnd1, 0);
8230         aMark.SetMarkArea(aDestRange);
8231         m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
8232 
8233         // Paste it second range.
8234         aDestRange = ScRange(nColumn, m_nStart2, 0, nColumn, m_nEnd2, 0);
8235         aMark.SetMarkArea(aDestRange);
8236         m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
8237 
8238         // Check the formula results for passed column.
8239         for( SCROW i = 0; i < m_nTotalRows; ++i )
8240         {
8241             if( !((m_nStart1 <= i && i <= m_nEnd1) || (m_nStart2 <= i && i <= m_nEnd2)) )
8242                 continue;
8243             double fExpected = lExpected(i);
8244             ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(nColumn,i,0)));
8245         }
8246     }
8247 };
8248 
8249 }
8250 
testTdf97369()8251 void Test::testTdf97369()
8252 {
8253     const SCROW TOTAL_ROWS = 330;
8254     const SCROW ROW_RANGE = 10;
8255     const SCROW START1 = 9;
8256     const SCROW END1 = 159;
8257     const SCROW START2 = 169;
8258     const SCROW END2 = 319;
8259 
8260     const double SHIFT1 = 200;
8261     const double SHIFT2 = 400;
8262 
8263     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8264                             m_pDoc->InsertTab (0, "tdf97369"));
8265 
8266     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8267 
8268     // set up columns A, B, C
8269     for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8270     {
8271         m_pDoc->SetValue(ScAddress(0, i, 0), i);          // A
8272         m_pDoc->SetValue(ScAddress(1, i, 0), i + SHIFT1); // B
8273         m_pDoc->SetValue(ScAddress(2, i, 0), i + SHIFT2); // C
8274     }
8275 
8276     const ColumnTest columnTest( m_pDoc, TOTAL_ROWS, START1, END1, START2, END2 );
8277 
8278     auto lExpectedinD = [=] (SCROW n) {
8279         return 3.0 * (n-START1) + SHIFT1 + SHIFT2;
8280     };
8281     columnTest(3, "=SUM(A1:C1)", lExpectedinD);
8282 
8283     auto lExpectedinE = [=] (SCROW ) {
8284         return SHIFT1 + SHIFT2;
8285     };
8286     columnTest(4, "=SUM(A$1:C$1)", lExpectedinE);
8287 
8288     auto lExpectedinF = [=] (SCROW n) {
8289         return ((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2.0;
8290     };
8291     columnTest(5, "=SUM(A1:A10)", lExpectedinF);
8292 
8293     auto lExpectedinG = [=] (SCROW n) {
8294         return ((n + 1) * n) / 2.0;
8295     };
8296     columnTest(6, "=SUM(A$1:A10)", lExpectedinG);
8297 
8298     auto lExpectedinH = [=] (SCROW n) {
8299         return 3.0 * (((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8300     };
8301     columnTest(7, "=SUM(A1:C10)", lExpectedinH);
8302 
8303     auto lExpectedinI = [=] (SCROW ) {
8304         return 3.0 * (((2*START1 + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8305     };
8306     columnTest(8, "=SUM(A$1:C$10)", lExpectedinI);
8307 
8308     m_pDoc->DeleteTab(0);
8309 }
8310 
testTdf97587()8311 void Test::testTdf97587()
8312 {
8313     const SCROW TOTAL_ROWS = 150;
8314     const SCROW ROW_RANGE = 10;
8315 
8316     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8317                             m_pDoc->InsertTab (0, "tdf97587"));
8318 
8319     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8320 
8321     std::set<SCROW> emptyCells = {0, 100};
8322     for( SCROW i = 0; i < ROW_RANGE; ++i )
8323     {
8324         emptyCells.insert(i + TOTAL_ROWS / 3);
8325         emptyCells.insert(i + TOTAL_ROWS);
8326     }
8327 
8328     // set up columns A
8329     for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8330     {
8331         if( emptyCells.find(i) != emptyCells.end() )
8332             continue;
8333         m_pDoc->SetValue(ScAddress(0, i, 0), 1.0);
8334     }
8335 
8336     ScDocument aClipDoc(SCDOCMODE_CLIP);
8337     ScMarkData aMark(MAXROW, MAXCOL);
8338 
8339     ScAddress aPos(1, 0, 0);
8340     m_pDoc->SetString(aPos, "=SUM(A1:A10)");
8341 
8342     // Copy formula cell to clipboard.
8343     ScClipParam aClipParam(aPos, false);
8344     aMark.SetMarkArea(aPos);
8345     m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
8346 
8347     // Paste it to first range.
8348     ScRange aDestRange(1, 1, 0, 1, TOTAL_ROWS + ROW_RANGE, 0);
8349     aMark.SetMarkArea(aDestRange);
8350     m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
8351 
8352     // Check the formula results in column B.
8353     for( SCROW i = 0; i < TOTAL_ROWS + 1; ++i )
8354     {
8355         int k = std::count_if( emptyCells.begin(), emptyCells.end(),
8356                 [=](SCROW n) { return (i <= n && n < i + ROW_RANGE); } );
8357         double fExpected = ROW_RANGE - k;
8358         ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(1,i,0)));
8359     }
8360     m_pDoc->DeleteTab(0);
8361 }
8362 
testMatConcat()8363 void Test::testMatConcat()
8364 {
8365     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8366 
8367     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8368     {
8369         for (SCROW nRow = 0; nRow < 10; ++nRow)
8370         {
8371             m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8372         }
8373     }
8374 
8375     ScMarkData aMark(MAXROW, MAXCOL);
8376     aMark.SelectOneTable(0);
8377     m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J10");
8378 
8379     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8380     {
8381         for (SCROW nRow = 12; nRow < 22; ++nRow)
8382         {
8383             OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8384             CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + OUString::number(nCol * (nRow - 12))), aStr);
8385         }
8386     }
8387 
8388     {   // Data in A12:B16
8389         const char* aData[][2] = {
8390             { "q", "w" },
8391             { "a",  "" },
8392             {  "", "x" },
8393             {  "",  "" },
8394             { "e", "r" },
8395         };
8396 
8397         ScAddress aPos(0,11,0);
8398         ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
8399         CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
8400     }
8401     // Matrix formula in C17:C21
8402     m_pDoc->InsertMatrixFormula(2, 16, 2, 20, aMark, "=A12:A16&B12:B16");
8403     // Check proper concatenation including empty cells.
8404     OUString aStr;
8405     ScAddress aPos(2,16,0);
8406     aStr = m_pDoc->GetString(aPos);
8407     CPPUNIT_ASSERT_EQUAL(OUString("qw"),aStr);
8408     aPos.IncRow();
8409     aStr = m_pDoc->GetString(aPos);
8410     CPPUNIT_ASSERT_EQUAL(OUString("a"),aStr);
8411     aPos.IncRow();
8412     aStr = m_pDoc->GetString(aPos);
8413     CPPUNIT_ASSERT_EQUAL(OUString("x"),aStr);
8414     aPos.IncRow();
8415     aStr = m_pDoc->GetString(aPos);
8416     CPPUNIT_ASSERT_EQUAL(OUString(),aStr);
8417     aPos.IncRow();
8418     aStr = m_pDoc->GetString(aPos);
8419     CPPUNIT_ASSERT_EQUAL(OUString("er"),aStr);
8420 
8421     m_pDoc->DeleteTab(0);
8422 }
8423 
testMatConcatReplication()8424 void Test::testMatConcatReplication()
8425 {
8426     // if one of the matrices is a one column or row matrix
8427     // the matrix is replicated across the larger matrix
8428     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8429 
8430     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8431     {
8432         for (SCROW nRow = 0; nRow < 10; ++nRow)
8433         {
8434             m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8435         }
8436     }
8437 
8438     ScMarkData aMark(MAXROW, MAXCOL);
8439     aMark.SelectOneTable(0);
8440     m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J1");
8441 
8442     for (SCCOL nCol = 0; nCol < 10; ++nCol)
8443     {
8444         for (SCROW nRow = 12; nRow < 22; ++nRow)
8445         {
8446             OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8447             CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + "0"), aStr);
8448         }
8449     }
8450 
8451     m_pDoc->DeleteTab(0);
8452 }
8453 
testRefR1C1WholeCol()8454 void Test::testRefR1C1WholeCol()
8455 {
8456     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8457 
8458     ScAddress aPos(1, 1, 1);
8459     ScCompiler aComp(m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
8460     std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString("=C[10]"));
8461     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
8462     OUString aFormula = pTokens->CreateString(m_pDoc, aCxt, aPos);
8463 
8464     CPPUNIT_ASSERT_EQUAL(OUString("L:L"), aFormula);
8465 
8466     m_pDoc->DeleteTab(0);
8467 }
8468 
testRefR1C1WholeRow()8469 void Test::testRefR1C1WholeRow()
8470 {
8471     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8472 
8473     ScAddress aPos(1, 1, 1);
8474     ScCompiler aComp(m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
8475     std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString("=R[3]"));
8476     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
8477     OUString aFormula = pTokens->CreateString(m_pDoc, aCxt, aPos);
8478 
8479     CPPUNIT_ASSERT_EQUAL(OUString("5:5"), aFormula);
8480 
8481     m_pDoc->DeleteTab(0);
8482 }
8483 
testSingleCellCopyColumnLabel()8484 void Test::testSingleCellCopyColumnLabel()
8485 {
8486     ScDocOptions aOptions = m_pDoc->GetDocOptions();
8487     aOptions.SetLookUpColRowNames(true);
8488     m_pDoc->SetDocOptions(aOptions);
8489     m_pDoc->InsertTab(0, "Test");
8490 
8491     m_pDoc->SetString(0, 0, 0, "a");
8492     m_pDoc->SetValue(0, 1, 0, 1.0);
8493     m_pDoc->SetValue(0, 2, 0, 2.0);
8494     m_pDoc->SetValue(0, 3, 0, 3.0);
8495     m_pDoc->SetString(1, 1, 0, "='a'");
8496 
8497     double nVal = m_pDoc->GetValue(1, 1, 0);
8498     ASSERT_DOUBLES_EQUAL(1.0, nVal);
8499 
8500     ScDocument aClipDoc(SCDOCMODE_CLIP);
8501     copyToClip(m_pDoc, ScRange(1, 1, 0), &aClipDoc);
8502     pasteOneCellFromClip(m_pDoc, ScRange(1, 2, 0), &aClipDoc);
8503     nVal = m_pDoc->GetValue(1, 2, 0);
8504     ASSERT_DOUBLES_EQUAL(2.0, nVal);
8505 
8506     m_pDoc->DeleteTab(0);
8507 }
8508 
8509 // Significant whitespace operator intersection in Excel syntax, tdf#96426
testIntersectionOpExcel()8510 void Test::testIntersectionOpExcel()
8511 {
8512     CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8513 
8514     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
8515     // Horizontal cell range covering C2.
8516     pGlobalNames->insert( new ScRangeData( m_pDoc, "horz", "$B$2:$D$2"));
8517     // Vertical cell range covering C2.
8518     pGlobalNames->insert( new ScRangeData( m_pDoc, "vert", "$C$1:$C$3"));
8519     // Data in C2.
8520     m_pDoc->SetValue(2,1,0, 1.0);
8521 
8522     m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH_XL_A1);
8523 
8524     // Choose formula positions that don't intersect with those data ranges.
8525     ScAddress aPos(0,3,0);
8526     m_pDoc->SetString(aPos,"=B2:D2 C1:C3");
8527     CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 intersecting references failed", 1.0, m_pDoc->GetValue(aPos));
8528     aPos.IncRow();
8529     m_pDoc->SetString(aPos,"=horz vert");
8530     CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 intersecting named expressions failed", 1.0, m_pDoc->GetValue(aPos));
8531     aPos.IncRow();
8532     m_pDoc->SetString(aPos,"=(horz vert)*2");
8533     CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 calculating with intersecting named expressions failed", 2.0, m_pDoc->GetValue(aPos));
8534     aPos.IncRow();
8535     m_pDoc->SetString(aPos,"=2*(horz vert)");
8536     CPPUNIT_ASSERT_EQUAL_MESSAGE("A7 calculating with intersecting named expressions failed", 2.0, m_pDoc->GetValue(aPos));
8537 
8538     m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH);
8539 
8540     m_pDoc->DeleteTab(0);
8541 }
8542 
8543 //Test Subtotal and Aggregate during hide rows #tdf93171
testFuncRowsHidden()8544 void Test::testFuncRowsHidden()
8545 {
8546     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8547     m_pDoc->InsertTab(0, "Test");
8548     m_pDoc->SetValue(0, 0, 0, 1); //A1
8549     m_pDoc->SetValue(0, 1, 0, 2); //A2
8550     m_pDoc->SetValue(0, 2, 0, 4); //A3
8551     m_pDoc->SetValue(0, 3, 0, 8); //A4
8552     m_pDoc->SetValue(0, 4, 0, 16); //A5
8553     m_pDoc->SetValue(0, 5, 0, 32); //A6
8554 
8555     ScAddress aPos(0,6,0);
8556     m_pDoc->SetString(aPos, "=SUBTOTAL(109; A1:A6)");
8557     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc->GetValue(aPos));
8558     //Hide row 1
8559     m_pDoc->SetRowHidden(0, 0, 0, true);
8560     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 62.0, m_pDoc->GetValue(aPos));
8561     m_pDoc->SetRowHidden(0, 0, 0, false);
8562     //Hide row 2 and 3
8563     m_pDoc->SetRowHidden(1, 2, 0, true);
8564     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 57.0, m_pDoc->GetValue(aPos));
8565     m_pDoc->SetRowHidden(1, 2, 0, false);
8566     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc->GetValue(aPos));
8567 
8568     m_pDoc->SetString(aPos, "=AGGREGATE(9; 5; A1:A6)"); //9=SUM 5=Ignore only hidden rows
8569     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc->GetValue(aPos));
8570     //Hide row 1
8571     m_pDoc->SetRowHidden(0, 0, 0, true);
8572     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 62.0, m_pDoc->GetValue(aPos));
8573     m_pDoc->SetRowHidden(0, 0, 0, false);
8574     //Hide rows 3 to 5
8575     m_pDoc->SetRowHidden(2, 4, 0, true);
8576     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 35.0, m_pDoc->GetValue(aPos));
8577     m_pDoc->SetRowHidden(2, 4, 0, false);
8578     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc->GetValue(aPos));
8579 
8580     m_pDoc->SetString(aPos, "=SUM(A1:A6)");
8581     m_pDoc->SetRowHidden(2, 4, 0, true);
8582     CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUM failed", 63.0, m_pDoc->GetValue(aPos));
8583 
8584     m_pDoc->DeleteTab(0);
8585 }
8586 
8587 // Test COUNTIFS, SUMIFS, AVERAGEIFS in array context.
testFuncSUMIFS()8588 void Test::testFuncSUMIFS()
8589 {
8590     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8591     m_pDoc->InsertTab(0, "Test");
8592 
8593     // Data in A1:B7, query in A9:A11
8594     std::vector<std::vector<const char*>> aData = {
8595         { "a",  "1" },
8596         { "b",  "2" },
8597         { "c",  "4" },
8598         { "d",  "8" },
8599         { "a", "16" },
8600         { "b", "32" },
8601         { "c", "64" },
8602         { "" },             // {} doesn't work with some compilers
8603         { "a" },
8604         { "b" },
8605         { "c" },
8606     };
8607 
8608     insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
8609 
8610     ScMarkData aMark(MAXROW, MAXCOL);
8611     aMark.SelectOneTable(0);
8612     // Matrix formula in C8:C10 with SUMIFS
8613     m_pDoc->InsertMatrixFormula(2, 7, 2, 9, aMark, "=SUMIFS(B1:B7;A1:A7;A9:A11)");
8614     // Matrix formula in D8:D10 with COUNTIFS
8615     m_pDoc->InsertMatrixFormula(3, 7, 3, 9, aMark, "=COUNTIFS(A1:A7;A9:A11)");
8616     // Matrix formula in E8:E10 with AVERAGEIFS
8617     m_pDoc->InsertMatrixFormula(4, 7, 4, 9, aMark, "=AVERAGEIFS(B1:B7;A1:A7;A9:A11)");
8618 
8619     {
8620         // Result B1+B5, B2+B6, B3+B7 and counts and averages.
8621         std::vector<std::vector<const char*>> aCheck = {
8622             { "17", "2",  "8.5" },
8623             { "34", "2", "17" },
8624             { "68", "2", "34" }
8625         };
8626         bool bGood = checkOutput(m_pDoc, ScRange(2,7,0, 4,9,0), aCheck,
8627                 "SUMIFS, COUNTIFS and AVERAGEIFS in array context");
8628         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS in array context failed", bGood);
8629     }
8630 
8631     // Matrix formula in G8:G10 with SUMIFS and reference list arrays.
8632     m_pDoc->InsertMatrixFormula(6, 7, 6, 9, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
8633     // Matrix formula in H8:H10 with COUNTIFS and reference list arrays.
8634     m_pDoc->InsertMatrixFormula(7, 7, 7, 9, aMark, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\")");
8635     // Matrix formula in I8:I10 with AVERAGEIFS and reference list arrays.
8636     m_pDoc->InsertMatrixFormula(8, 7, 8, 9, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
8637 
8638     {
8639         // Result sums, counts and averages.
8640         std::vector<std::vector<const char*>> aCheck = {
8641             {  "0", "0", "#DIV/0!" },
8642             {  "8", "1",  "8" },
8643             { "24", "2", "12" }
8644         };
8645         bool bGood = checkOutput(m_pDoc, ScRange(6,7,0, 8,9,0), aCheck,
8646                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays");
8647         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays failed", bGood);
8648     }
8649 
8650     // Matrix formula in K8:K10 with SUMIFS and reference list array condition
8651     // and "normal" data range.
8652     m_pDoc->InsertMatrixFormula(10, 7, 10, 9, aMark, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
8653     // Matrix formula in L8:L10 with AVERAGEIFS and reference list array
8654     // condition and "normal" data range.
8655     m_pDoc->InsertMatrixFormula(11, 7, 11, 9, aMark, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
8656 
8657     {
8658         // Result sums and averages.
8659         std::vector<std::vector<const char*>> aCheck = {
8660             { "0", "#DIV/0!" },
8661             { "2", "2" },
8662             { "3", "1.5" }
8663         };
8664         bool bGood = checkOutput(m_pDoc, ScRange(10,7,0, 11,9,0), aCheck,
8665                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal range");
8666         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal range failed", bGood);
8667     }
8668 
8669     // Matrix formula in G18:G20 with SUMIFS and reference list arrays and a
8670     // "normal" criteria range.
8671     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\")");
8672     // Matrix formula in H18:H20 with COUNTIFS and reference list arrays and a
8673     // "normal" criteria range.
8674     m_pDoc->InsertMatrixFormula(7, 17, 7, 19, aMark, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8675     // Matrix formula in I18:I20 with AVERAGEIFS and reference list arrays and
8676     // a "normal" criteria range.
8677     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\")");
8678 
8679     {
8680         // Result sums, counts and averages.
8681         std::vector<std::vector<const char*>> aCheck = {
8682             {  "0", "0", "#DIV/0!" },
8683             {  "8", "1",  "8" },
8684             { "16", "1", "16" }
8685         };
8686         bool bGood = checkOutput(m_pDoc, ScRange(6,17,0, 8,19,0), aCheck,
8687                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range");
8688         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed", bGood);
8689     }
8690 
8691     // Matrix formula in K18:K20 with SUMIFS and reference list array condition
8692     // and "normal" data range and a "normal" criteria range.
8693     m_pDoc->InsertMatrixFormula(10, 17, 10, 19, aMark, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8694     // Matrix formula in L18:L20 with AVERAGEIFS and reference list array
8695     // condition and "normal" data range and a "normal" criteria range.
8696     m_pDoc->InsertMatrixFormula(11, 17, 11, 19, aMark, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8697 
8698     {
8699         // Result sums and averages.
8700         std::vector<std::vector<const char*>> aCheck = {
8701             { "0", "#DIV/0!" },
8702             { "2", "2" },
8703             { "2", "2" }
8704         };
8705         bool bGood = checkOutput(m_pDoc, ScRange(10,17,0, 11,19,0), aCheck,
8706                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range");
8707         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed", bGood);
8708     }
8709 
8710     // Same, but swapped normal and array criteria.
8711 
8712     // Matrix formula in G28:G30 with SUMIFS and reference list arrays and a
8713     // "normal" criteria range, swapped.
8714     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\")");
8715     // Matrix formula in H28:H30 with COUNTIFS and reference list arrays and a
8716     // "normal" criteria range, swapped.
8717     m_pDoc->InsertMatrixFormula(7, 27, 7, 29, aMark, "=COUNTIFS(B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8718     // Matrix formula in I28:I30 with AVERAGEIFS and reference list arrays and
8719     // a "normal" criteria range, swapped.
8720     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\")");
8721 
8722     {
8723         // Result sums, counts and averages.
8724         std::vector<std::vector<const char*>> aCheck = {
8725             {  "0", "0", "#DIV/0!" },
8726             {  "8", "1",  "8" },
8727             { "16", "1", "16" }
8728         };
8729         bool bGood = checkOutput(m_pDoc, ScRange(6,27,0, 8,29,0), aCheck,
8730                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range, swapped");
8731         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed, swapped", bGood);
8732     }
8733 
8734     // Matrix formula in K28:K30 with SUMIFS and reference list array condition
8735     // and "normal" data range and a "normal" criteria range, swapped.
8736     m_pDoc->InsertMatrixFormula(10, 27, 10, 29, aMark, "=SUMIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8737     // Matrix formula in L28:L30 with AVERAGEIFS and reference list array
8738     // condition and "normal" data range and a "normal" criteria range,
8739     // swapped.
8740     m_pDoc->InsertMatrixFormula(11, 27, 11, 29, aMark, "=AVERAGEIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8741 
8742     {
8743         // Result sums and averages.
8744         std::vector<std::vector<const char*>> aCheck = {
8745             { "0", "#DIV/0!" },
8746             { "2", "2" },
8747             { "2", "2" }
8748         };
8749         bool bGood = checkOutput(m_pDoc, ScRange(10,27,0, 11,29,0), aCheck,
8750                 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range, swapped");
8751         CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed, swapped", bGood);
8752     }
8753 
8754     m_pDoc->DeleteTab(0);
8755 }
8756 
8757 // Test SUBTOTAL with reference lists in array context.
testFuncRefListArraySUBTOTAL()8758 void Test::testFuncRefListArraySUBTOTAL()
8759 {
8760     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8761     m_pDoc->InsertTab(0, "Test");
8762 
8763     m_pDoc->SetValue(0,0,0,  1.0);  // A1
8764     m_pDoc->SetValue(0,1,0,  2.0);  // A2
8765     m_pDoc->SetValue(0,2,0,  4.0);  // A3
8766     m_pDoc->SetValue(0,3,0,  8.0);  // A4
8767     m_pDoc->SetValue(0,4,0, 16.0);  // A5
8768     m_pDoc->SetValue(0,5,0, 32.0);  // A6
8769 
8770     // Matrix in B7:B9, individual SUM of A2:A3, A3:A4 and A4:A5
8771     ScMarkData aMark(MAXROW, MAXCOL);
8772     aMark.SelectOneTable(0);
8773     m_pDoc->InsertMatrixFormula(1, 6, 1, 8, aMark, "=SUBTOTAL(9;OFFSET(A1;ROW(1:3);0;2))");
8774     ScAddress aPos(1,6,0);
8775     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A2:A3 failed",  6.0, m_pDoc->GetValue(aPos));
8776     aPos.IncRow();
8777     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A3:A4 failed", 12.0, m_pDoc->GetValue(aPos));
8778     aPos.IncRow();
8779     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A4:A5 failed", 24.0, m_pDoc->GetValue(aPos));
8780 
8781     // Matrix in C7:C9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
8782     m_pDoc->InsertMatrixFormula(2, 6, 2, 8, aMark, "=SUBTOTAL(1;OFFSET(A1;ROW(1:3);0;2))");
8783     aPos.Set(2,6,0);
8784     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A2:A3 failed",  3.0, m_pDoc->GetValue(aPos));
8785     aPos.IncRow();
8786     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A3:A4 failed",  6.0, m_pDoc->GetValue(aPos));
8787     aPos.IncRow();
8788     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A4:A5 failed", 12.0, m_pDoc->GetValue(aPos));
8789 
8790     // Matrix in D7:D9, individual MIN of A2:A3, A3:A4 and A4:A5
8791     m_pDoc->InsertMatrixFormula(3, 6, 3, 8, aMark, "=SUBTOTAL(5;OFFSET(A1;ROW(1:3);0;2))");
8792     aPos.Set(3,6,0);
8793     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A2:A3 failed",  2.0, m_pDoc->GetValue(aPos));
8794     aPos.IncRow();
8795     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A3:A4 failed",  4.0, m_pDoc->GetValue(aPos));
8796     aPos.IncRow();
8797     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A4:A5 failed",  8.0, m_pDoc->GetValue(aPos));
8798 
8799     // Matrix in E7:E9, individual MAX of A2:A3, A3:A4 and A4:A5
8800     m_pDoc->InsertMatrixFormula(4, 6, 4, 8, aMark, "=SUBTOTAL(4;OFFSET(A1;ROW(1:3);0;2))");
8801     aPos.Set(4,6,0);
8802     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A2:A3 failed",  4.0, m_pDoc->GetValue(aPos));
8803     aPos.IncRow();
8804     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A3:A4 failed",  8.0, m_pDoc->GetValue(aPos));
8805     aPos.IncRow();
8806     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A4:A5 failed", 16.0, m_pDoc->GetValue(aPos));
8807 
8808     // Matrix in F7:F9, individual STDEV of A2:A3, A3:A4 and A4:A5
8809     m_pDoc->InsertMatrixFormula(5, 6, 5, 8, aMark, "=SUBTOTAL(7;OFFSET(A1;ROW(1:3);0;2))");
8810     aPos.Set(5,6,0);
8811     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A2:A3 failed", 1.414214, m_pDoc->GetValue(aPos), 1e-6);
8812     aPos.IncRow();
8813     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A3:A4 failed", 2.828427, m_pDoc->GetValue(aPos), 1e-6);
8814     aPos.IncRow();
8815     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A4:A5 failed", 5.656854, m_pDoc->GetValue(aPos), 1e-6);
8816 
8817     // Matrix in G7:G9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
8818     // Plus two "ordinary" ranges, one before and one after.
8819     m_pDoc->InsertMatrixFormula(6, 6, 6, 8, aMark, "=SUBTOTAL(1;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
8820     aPos.Set(6,6,0);
8821     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A2:A3,A5:A6 failed",  9.5, m_pDoc->GetValue(aPos));
8822     aPos.IncRow();
8823     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A3:A4,A5:A6 failed", 10.5, m_pDoc->GetValue(aPos));
8824     aPos.IncRow();
8825     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A4:A5,A5:A6 failed", 12.5, m_pDoc->GetValue(aPos));
8826 
8827     // Matrix in H7:H9, individual MAX of A2:A3, A3:A4 and A4:A5
8828     // Plus two "ordinary" ranges, one before and one after.
8829     m_pDoc->InsertMatrixFormula(7, 6, 7, 8, aMark, "=SUBTOTAL(4;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
8830     aPos.Set(7,6,0);
8831     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A2:A3,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
8832     aPos.IncRow();
8833     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A3:A4,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
8834     aPos.IncRow();
8835     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A4:A5,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
8836 
8837     // Matrix in I7:I9, individual STDEV of A2:A3, A3:A4 and A4:A5
8838     // Plus two "ordinary" ranges, one before and one after.
8839     m_pDoc->InsertMatrixFormula(8, 6, 8, 8, aMark, "=SUBTOTAL(7;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
8840     aPos.Set(8,6,0);
8841     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A2:A3,A5:A6 failed", 12.35718, m_pDoc->GetValue(aPos), 1e-5);
8842     aPos.IncRow();
8843     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A3:A4,A5:A6 failed", 11.86170, m_pDoc->GetValue(aPos), 1e-5);
8844     aPos.IncRow();
8845     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A4:A5,A5:A6 failed", 11.55422, m_pDoc->GetValue(aPos), 1e-5);
8846 
8847     // Empty two cells such that they affect two ranges.
8848     m_pDoc->SetString(0,1,0, "");   // A2
8849     m_pDoc->SetString(0,2,0, "");   // A3
8850     // Matrix in J7:J9, individual COUNTBLANK of A2:A3, A3:A4 and A4:A5
8851     m_pDoc->InsertMatrixFormula(9, 6, 9, 8, aMark, "=COUNTBLANK(OFFSET(A1;ROW(1:3);0;2))");
8852     aPos.Set(9,6,0);
8853     CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A2:A3,A5:A6 failed", 2.0, m_pDoc->GetValue(aPos));
8854     aPos.IncRow();
8855     CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A3:A4,A5:A6 failed", 1.0, m_pDoc->GetValue(aPos));
8856     aPos.IncRow();
8857     CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A4:A5,A5:A6 failed", 0.0, m_pDoc->GetValue(aPos));
8858 
8859     // Restore these two cell values so we'd catch failures below.
8860     m_pDoc->SetValue(0,1,0,  2.0);  // A2
8861     m_pDoc->SetValue(0,2,0,  4.0);  // A3
8862     // Hide rows 2 to 4.
8863     m_pDoc->SetRowHidden(1,3,0, true);
8864     // Matrix in K7, array of references as OFFSET result.
8865     m_pDoc->InsertMatrixFormula(10, 6, 10, 6, aMark, "=SUM(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
8866     aPos.Set(10,6,0);
8867     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM SUBTOTAL failed", 49.0, m_pDoc->GetValue(aPos));
8868     aPos.IncRow();
8869     // ForceArray in K8, array of references as OFFSET result.
8870     m_pDoc->SetString( aPos, "=SUMPRODUCT(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
8871     CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMPRODUCT SUBTOTAL failed", 49.0, m_pDoc->GetValue(aPos));
8872 
8873     m_pDoc->DeleteTab(0);
8874 }
8875 
8876 // tdf#115493 jump commands return the matrix result instead of the reference
8877 // list array.
testFuncJumpMatrixArrayIF()8878 void Test::testFuncJumpMatrixArrayIF()
8879 {
8880     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8881     m_pDoc->InsertTab(0, "Test");
8882 
8883     m_pDoc->SetString(0,0,0, "a");  // A1
8884     std::vector<std::vector<const char*>> aData = {
8885         { "a", "1" },
8886         { "b", "2" },
8887         { "a", "4" }
8888     };                              // A7:B9
8889     insertRangeData(m_pDoc, ScAddress(0,6,0), aData);
8890 
8891     ScMarkData aMark(MAXROW, MAXCOL);
8892     aMark.SelectOneTable(0);
8893 
8894     // Matrix in C10, summing B7,B9
8895     m_pDoc->InsertMatrixFormula( 2,9, 2,9, aMark, "=SUM(IF(EXACT(A7:A9;A$1);B7:B9;0))");
8896     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C10 failed", 5.0, m_pDoc->GetValue(ScAddress(2,9,0)));
8897 
8898     // Matrix in C11, summing B7,B9
8899     m_pDoc->InsertMatrixFormula( 2,10, 2,10, aMark,
8900             "=SUM(IF(EXACT(OFFSET(A7;0;0):OFFSET(A7;2;0);A$1);OFFSET(A7;0;1):OFFSET(A7;2;1);0))");
8901     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C11 failed", 5.0, m_pDoc->GetValue(ScAddress(2,10,0)));
8902 
8903     m_pDoc->DeleteTab(0);
8904 }
8905 
8906 // tdf#123477 OFFSET() returns the matrix result instead of the reference list
8907 // array if result is not used as ReferenceOrRefArray.
testFuncJumpMatrixArrayOFFSET()8908 void Test::testFuncJumpMatrixArrayOFFSET()
8909 {
8910     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8911     m_pDoc->InsertTab(0, "Test");
8912 
8913     std::vector<std::vector<const char*>> aData = {
8914         { "abc" },
8915         { "bcd" },
8916         { "cde" }
8917     };
8918     insertRangeData(m_pDoc, ScAddress(0,0,0), aData);   // A1:A3
8919 
8920     ScMarkData aMark(MAXROW, MAXCOL);
8921     aMark.SelectOneTable(0);
8922 
8923     // Matrix in C5:C7, COLUMN()-3 here offsets by 0 but the entire expression
8924     // is in array/matrix context.
8925     m_pDoc->InsertMatrixFormula( 2,4, 2,6, aMark, "=FIND(\"c\";OFFSET(A1:A3;0;COLUMN()-3))");
8926     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C5 failed", 3.0, m_pDoc->GetValue(ScAddress(2,4,0)));
8927     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C6 failed", 2.0, m_pDoc->GetValue(ScAddress(2,5,0)));
8928     CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C7 failed", 1.0, m_pDoc->GetValue(ScAddress(2,6,0)));
8929 
8930     m_pDoc->DeleteTab(0);
8931 }
8932 
8933 // Test iterations with circular chain of references.
testIterations()8934 void Test::testIterations()
8935 {
8936     ScDocOptions aDocOpts = m_pDoc->GetDocOptions();
8937     aDocOpts.SetIter( true );
8938     m_pDoc->SetDocOptions( aDocOpts );
8939 
8940     m_pDoc->InsertTab(0, "Test");
8941 
8942     m_pDoc->SetValue( 0, 0, 0, 0.01 );         // A1
8943     m_pDoc->SetString( 0, 1, 0, "=A1" );       // A2
8944     m_pDoc->SetString( 0, 2, 0, "=COS(A2)" );  // A3
8945     m_pDoc->CalcAll();
8946 
8947     // Establish reference cycle for the computation of the fixed point of COS() function
8948     m_pDoc->SetString( 0, 0, 0, "=A3" );       // A1
8949     m_pDoc->CalcAll();
8950 
8951     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error", FormulaError::NONE, m_pDoc->GetErrCode( ScAddress( 0, 2, 0) ) );
8952     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate fixed point of cos() failed", 0.7387, m_pDoc->GetValue(0, 2, 0), 1e-4 );
8953 
8954     // Modify the formula
8955     m_pDoc->SetString( 0, 2, 0, "=COS(A2)+0.001" );  // A3
8956     m_pDoc->CalcAll();
8957 
8958     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error after perturbation", FormulaError::NONE, m_pDoc->GetErrCode( ScAddress( 0, 2, 0) ) );
8959     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate perturbed fixed point of cos() failed", 0.7399, m_pDoc->GetValue(0, 2, 0), 1e-4 );
8960 
8961     m_pDoc->DeleteTab(0);
8962 
8963     aDocOpts.SetIter( false );
8964     m_pDoc->SetDocOptions( aDocOpts );
8965 }
8966 
8967 // tdf#111428 CellStoreEvent and its counter used for quick "has a column
8968 // formula cells" must point to the correct column.
testInsertColCellStoreEventSwap()8969 void Test::testInsertColCellStoreEventSwap()
8970 {
8971     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8972     m_pDoc->InsertTab(0, "Test");
8973 
8974     m_pDoc->SetValue(  0,0,0, 1.0 );    // A1
8975     m_pDoc->SetString( 1,0,0, "=A1" );  // B1
8976     // Insert column left of B
8977     m_pDoc->InsertCol( ScRange(1,0,0, 1,m_pDoc->MaxRow(),0));
8978     ScAddress aPos(2,0,0);              // C1, new formula position
8979     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Should be formula cell having value", 1.0, m_pDoc->GetValue(aPos));
8980     // After having swapped in an empty column, editing or adding a formula
8981     // cell has to use the correct store context. To test this,
8982     // ScDocument::SetString() can't be used as it doesn't expose the behavior
8983     // in question, use ScDocFunc::SetFormulaCell() instead which actually is
8984     // also called when editing a cell and creating a formula cell.
8985     ScFormulaCell* pCell = new ScFormulaCell( m_pDoc, aPos, "=A1+1");
8986     ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
8987     rDocFunc.SetFormulaCell( aPos, pCell, false);   // C1, change formula
8988     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Initial calculation failed", 2.0, m_pDoc->GetValue(aPos));
8989     m_pDoc->SetValue( 0,0,0, 2.0 );     // A1, change value
8990     CPPUNIT_ASSERT_EQUAL_MESSAGE( "Recalculation failed", 3.0, m_pDoc->GetValue(aPos));
8991 
8992     m_pDoc->DeleteTab(0);
8993 }
8994 
testFormulaAfterDeleteRows()8995 void Test::testFormulaAfterDeleteRows()
8996 {
8997     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8998     m_pDoc->InsertTab(0, "Test");
8999 
9000     // Fill A1:A70000 with 1.0
9001     std::vector<double> aVals(70000, 1.0);
9002     m_pDoc->SetValues(ScAddress(0, 0, 0), aVals);
9003     // Set A70001 with formula "=SUM(A1:A70000)"
9004     m_pDoc->SetString(0, 70000, 0, "=SUM(A1:A70000)");
9005 
9006     // Delete rows 2:69998
9007     m_pDoc->DeleteRow(ScRange(0, 1, 0, m_pDoc->MaxCol(), 69997, 0));
9008 
9009     const ScAddress aPos(0, 3, 0);  // A4
9010     ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A1:A3)", "Wrong formula in A4.");
9011 
9012     ASSERT_DOUBLES_EQUAL_MESSAGE("Wrong value at A4", 3.0, m_pDoc->GetValue(aPos));
9013 }
9014 
9015 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
9016