Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / qa / unit / ucalc_formula.cxx
blobc7de928a3de994cf5eacb4e74b6c1b29ccf92447
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
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>
37 #include <formula/vectortoken.hxx>
38 #include <svl/broadcast.hxx>
39 #include <svl/intitem.hxx>
40 #include <sfx2/docfile.hxx>
42 #include <memory>
43 #include <functional>
44 #include <set>
45 #include <algorithm>
47 using namespace formula;
49 namespace {
51 ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
53 ScRange aRange;
55 vector<SCROW> aRows;
56 pCacheTab->getAllRows(aRows);
57 bool bFirst = true;
58 for (const SCROW nRow : aRows)
60 vector<SCCOL> aCols;
61 pCacheTab->getAllCols(nRow, aCols);
62 for (const SCCOL nCol : aCols)
64 if (bFirst)
66 aRange.aStart = ScAddress(nCol, nRow, 0);
67 aRange.aEnd = aRange.aStart;
68 bFirst = false;
70 else
72 if (nCol < aRange.aStart.Col())
73 aRange.aStart.SetCol(nCol);
74 else if (aRange.aEnd.Col() < nCol)
75 aRange.aEnd.SetCol(nCol);
77 if (nRow < aRange.aStart.Row())
78 aRange.aStart.SetRow(nRow);
79 else if (aRange.aEnd.Row() < nRow)
80 aRange.aEnd.SetRow(nRow);
84 return aRange;
89 void Test::testFormulaCreateStringFromTokens()
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);
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" },
111 { false, "sheetx", "Test.J1" }
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);
119 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
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);
125 if (aNames[i].bGlobal)
127 bool bInserted = pGlobalNames->insert(pName);
128 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
130 else
132 bool bInserted = pSheetNames->insert(pName);
133 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
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 }
151 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
152 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
154 for (size_t i = 0; i < SAL_N_ELEMENTS(aDBs); ++i)
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);
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)",
179 (void) aTests;
181 std::unique_ptr<ScTokenArray> pArray;
183 sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
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);
192 ScAddress aPos(0,0,0);
194 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
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());
204 OUString aCheck = pArray->CreateString(aCxt, aPos);
205 CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
206 #endif
209 m_pDoc->DeleteTab(3);
210 m_pDoc->DeleteTab(2);
211 m_pDoc->DeleteTab(1);
212 m_pDoc->DeleteTab(0);
215 namespace {
217 bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
219 if (rArray.mpStringArray)
221 if (rArray.mpStringArray[nPos])
222 return false;
225 if (rArray.mpNumericArray)
226 return rtl::math::isNan(rArray.mpNumericArray[nPos]);
227 else
228 return true;
231 bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
233 if (rArray.mpStringArray && rArray.mpStringArray[nPos])
234 // This is a string cell.
235 return false;
237 return rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal;
240 bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
242 if (!rArray.mpStringArray)
243 return false;
245 bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
246 if (!bEquals)
248 cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
249 cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
251 return bEquals;
256 void Test::testFormulaParseReference()
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.
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);
275 // Make sure the formula input and output match.
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"
284 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
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");
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);
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);
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);
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);
323 ScRange aRange;
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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)));
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)));
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)));
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)));
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)));
456 // Check for reference input conversion to and display string of entire column/row.
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" }
471 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
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");
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);
486 void Test::testFetchVectorRefArray()
488 m_pDoc->InsertTab(0, "Test");
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);
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]);
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));
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));
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();
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));
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();
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"));
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();
576 // Leave row 7 empty.
577 m_pDoc->SetString(ScAddress(4,8,0), "Andy");
578 m_pDoc->SetValue(ScAddress(4,9,0), 123);
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));
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));
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));
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();
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));
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));
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));
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);
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");
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"));
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"));
683 // Column H starts with formula cells.
684 for (SCROW i = 0; i < 10; ++i)
685 m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
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));
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));
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();
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]));
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]));
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]));
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]));
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);
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]);
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);
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]);
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);
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]);
775 m_pDoc->DeleteTab(0);
778 void Test::testGroupConverter3D()
780 m_pDoc->InsertTab(0, "Test");
781 m_pDoc->InsertTab(1, "Test2");
783 m_pDoc->SetValue(1, 0, 0, 1.0);
784 m_pDoc->SetValue(1, 0, 1, 2.0);
786 for (SCROW nRow = 0; nRow < 200; ++nRow)
788 OUString aFormula = "=SUM(Test.B" + OUString::number(nRow+1) + ":Test2.B" + OUString::number(nRow+1) + ")";
789 m_pDoc->SetString(0, nRow, 0, aFormula);
792 double nVal = m_pDoc->GetValue(0, 0, 0);
793 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
795 m_pDoc->DeleteTab(1);
796 m_pDoc->DeleteTab(0);
799 void Test::testFormulaHashAndTag()
801 m_pDoc->InsertTab(0, "Test");
803 ScAddress aPos1(0,0,0), aPos2(1,0,0);
805 // Test formula hashing.
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 },
832 for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
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);
839 std::ostringstream os;
840 os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
841 if (aHashTests[i].bEqual)
843 os << " Error: these hashes should be equal." << endl;
844 CPPUNIT_ASSERT_EQUAL_MESSAGE(os.str(), nHashVal1, nHashVal2);
846 else
848 os << " Error: these hashes should differ." << endl;
849 CPPUNIT_ASSERT_MESSAGE(os.str(), nHashVal1 != nHashVal2);
852 aPos1.IncRow();
853 aPos2.IncRow();
856 // Go back to row 1.
857 aPos1.SetRow(0);
858 aPos2.SetRow(0);
860 // Test formula vectorization state.
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 },
874 for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
876 m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
877 ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
878 ScFormulaVectorState eReferenceState = aVectorTests[i].eState;
880 if (eState != eReferenceState)
882 std::ostringstream os;
883 os << "Unexpected vectorization state: expr: '" << aVectorTests[i].pFormula << "'";
884 CPPUNIT_ASSERT_MESSAGE(os.str(), false);
886 aPos1.IncRow();
889 m_pDoc->DeleteTab(0);
892 void Test::testFormulaTokenEquality()
894 struct FormulaTokenEqualityTest
896 const char* mpFormula1;
897 const char* mpFormula2;
898 bool const mbEqual;
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 },
917 formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
918 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
920 ScFormulaCell aCell1(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
921 ScFormulaCell aCell2(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
923 ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
924 if (aTests[i].mbEqual)
926 if (eComp == ScFormulaCell::NotEqual)
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());
934 else
936 if (eComp != ScFormulaCell::NotEqual)
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());
947 void Test::testFormulaRefData()
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());
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());
965 // Test extension of range reference.
967 ScComplexRefData aDoubleRef;
968 aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
970 aRef.InitAddress(ScAddress(6,5,0));
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);
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));
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);
986 void Test::testFormulaCompiler()
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 },
998 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
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);
1003 OUString aFormula = toString(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
1004 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
1008 void Test::testFormulaCompilerJumpReordering()
1010 struct TokenCheck
1012 OpCode const meOp;
1013 StackVar const meType;
1016 // Set separators first.
1017 ScFormulaOptions aOptions;
1018 aOptions.SetFormulaSepArg(";");
1019 aOptions.SetFormulaSepArrayCol(";");
1020 aOptions.SetFormulaSepArrayRow("|");
1021 getDocShell().SetFormulaOptions(aOptions);
1024 OUString const aInput("=IF(B1;12;\"text\")");
1026 // Compile formula string first.
1027 std::unique_ptr<ScTokenArray> pCode(compileFormula(m_pDoc, aInput));
1028 CPPUNIT_ASSERT(pCode);
1030 // Then generate RPN tokens.
1031 ScCompiler aCompRPN(m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1032 aCompRPN.CompileTokenArray();
1034 // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
1035 static const TokenCheck aCheckRPN[] =
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 },
1045 sal_uInt16 nLen = pCode->GetCodeLen();
1046 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN)), nLen);
1048 FormulaToken** ppTokens = pCode->GetCode();
1049 for (sal_uInt16 i = 0; i < nLen; ++i)
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()));
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();
1063 static const TokenCheck aCheckRPN2[] =
1065 { ocPush, svSingleRef },
1066 { ocPush, svDouble },
1067 { ocPush, svString },
1068 { ocIf, svUnknown }, // type is context dependent, don't test it
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)
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()));
1084 void Test::testFormulaCompilerImplicitIntersection2Param()
1086 struct TestCaseFormula
1088 OUString const aFormula;
1089 ScAddress const aCellAddress;
1090 ScRange const aSumRange;
1091 bool const bStartColRel; // SumRange-StartCol
1092 bool const bEndColRel; // SumRange-EndCol
1095 m_pDoc->InsertTab(0, "Formula");
1096 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1099 TestCaseFormula aTestCases[] =
1101 // Formula, FormulaCellAddress, SumRange with Implicit Intersection
1103 // Sumrange is single cell, address is abs
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
1112 // Sumrange is single cell, address is relative
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
1121 // Baserange(abs,abs), Sumrange(abs,abs)
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
1130 // Baserange(abs,rel), Sumrange(abs,abs)
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
1139 // Baserange(rel,abs), Sumrange(abs,abs)
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
1148 // Baserange(rel,rel), Sumrange(abs,abs)
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
1157 // Baserange(abs,abs), Sumrange(abs,rel)
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
1166 // Baserange(abs,abs), Sumrange(rel,abs)
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
1175 // Baserange(abs,abs), Sumrange(rel,rel)
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
1185 for (const auto& rCase : aTestCases)
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);
1192 sal_uInt16 nLen = pCode->GetCodeLen();
1193 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(4), nLen);
1195 FormulaToken** ppTokens = pCode->GetCode();
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());
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);
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());
1210 void Test::testFormulaCompilerImplicitIntersection1ParamNoChange()
1212 struct TestCaseFormulaNoChange
1214 OUString const aFormula;
1215 ScAddress const aCellAddress;
1216 bool const bMatrixFormula;
1217 bool const bForcedArray;
1220 m_pDoc->InsertTab(0, "Formula");
1221 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1224 ScAddress aStartAddr(4, 5, 0);
1225 TestCaseFormulaNoChange aCasesNoChange[] =
1228 OUString("=COS(A$2:A$100)"), // No change because of abs col ref.
1229 aStartAddr,
1230 false,
1231 false
1234 OUString("=COS($A7:$A100)"), // No intersection
1235 aStartAddr,
1236 false,
1237 false
1240 OUString("=COS($A5:$C7)"), // No intersection 2-D range
1241 aStartAddr,
1242 false,
1243 false
1246 OUString("=SUMPRODUCT(COS(A6:A10))"), // COS() in forced array mode
1247 aStartAddr,
1248 false,
1249 true
1252 OUString("=COS(A6:A10)"), // Matrix formula
1253 aStartAddr,
1254 true,
1255 false
1259 for (const auto& rCase : aCasesNoChange)
1261 if (rCase.bMatrixFormula)
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);
1270 else
1271 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1273 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1274 const ScTokenArray* pCode = pCell->GetCode();
1275 CPPUNIT_ASSERT(pCode);
1277 sal_uInt16 nRPNLen = pCode->GetCodeLen();
1278 sal_uInt16 nRawLen = pCode->GetLen();
1279 sal_uInt16 nRawArgPos;
1280 if (rCase.bForcedArray)
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);
1286 else
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);
1293 FormulaToken** ppRawTokens = pCode->GetArray();
1294 FormulaToken** ppRPNTokens = pCode->GetCode();
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());
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);
1307 void Test::testFormulaCompilerImplicitIntersection1ParamWithChange()
1309 struct TestCaseFormula
1311 OUString const aFormula;
1312 ScAddress const aCellAddress;
1313 ScAddress const aArgAddr;
1316 m_pDoc->InsertTab(0, "Formula");
1317 m_pDoc->InsertTab(1, "Formula1");
1318 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1321 ScAddress aStartAddr(10, 5, 0);
1322 TestCaseFormula aCasesWithChange[] =
1325 OUString("=COS($A6:$A100)"), // Corner case with intersection
1326 aStartAddr,
1327 ScAddress(0, 5, 0)
1330 OUString("=COS($A2:$A6)"), // Corner case with intersection
1331 aStartAddr,
1332 ScAddress(0, 5, 0)
1335 OUString("=COS($A2:$A100)"), // Typical 1D case
1336 aStartAddr,
1337 ScAddress(0, 5, 0)
1340 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1341 ScAddress(0, 0, 1), // Formula in sheet 1
1342 ScAddress(0, 0, 0)
1345 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1346 ScAddress(0, 2, 1), // Formula in sheet 1
1347 ScAddress(0, 2, 0)
1350 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1351 ScAddress(2, 0, 1), // Formula in sheet 1
1352 ScAddress(2, 0, 0)
1355 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1356 ScAddress(2, 2, 1), // Formula in sheet 1
1357 ScAddress(2, 2, 0)
1360 OUString("=COS($Formula.$A1:$C3)"), // Typical 2D case
1361 ScAddress(1, 1, 1), // Formula in sheet 1
1362 ScAddress(1, 1, 0)
1366 for (const auto& rCase : aCasesWithChange)
1368 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1370 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1371 const ScTokenArray* pCode = pCell->GetCode();
1372 CPPUNIT_ASSERT(pCode);
1374 sal_uInt16 nRPNLen = pCode->GetCodeLen();
1375 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1377 FormulaToken** ppRPNTokens = pCode->GetCode();
1379 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef, ppRPNTokens[0]->GetType());
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);
1388 void Test::testFormulaCompilerImplicitIntersection1NoGroup()
1390 m_pDoc->InsertTab(0, "Formula");
1391 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1393 m_pDoc->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
1394 m_pDoc->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
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.");
1402 m_pDoc->DeleteTab(0);
1405 void Test::testFormulaCompilerImplicitIntersectionOperators()
1407 struct TestCase
1409 OUString const formula[3];
1410 double const result[3];
1413 m_pDoc->InsertTab(0, "Test");
1414 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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
1423 TestCase tests[] =
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 }
1429 for (const TestCase& test : tests)
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)));
1438 m_pDoc->DeleteTab(0);
1441 void Test::testFormulaRefUpdate()
1443 m_pDoc->InsertTab(0, "Formula");
1445 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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
1451 ScAddress aPos(2,2,0);
1452 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1454 aPos = ScAddress(2,3,0);
1455 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
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));
1460 aPos = ScAddress(2,1,0);
1461 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C2.");
1463 aPos = ScAddress(2,2,0);
1464 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C3.");
1466 // Insert one row at row 2 to move them back.
1467 m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1469 aPos = ScAddress(2,2,0);
1470 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1472 aPos = ScAddress(2,3,0);
1473 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
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));
1478 aPos = ScAddress(2,4,0);
1479 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A3", "Wrong formula in C5.");
1481 aPos = ScAddress(2,5,0);
1482 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$3", "Wrong formula in C6.");
1484 // Delete 2 rows at row 1 to shift them back.
1485 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1487 aPos = ScAddress(2,2,0);
1488 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1490 aPos = ScAddress(2,3,0);
1491 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
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));
1496 aPos = ScAddress(5,2,0);
1497 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in F3.");
1499 aPos = ScAddress(5,3,0);
1500 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in F4.");
1502 // Delete columns B:D to shift them back.
1503 m_pDoc->DeleteCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1505 aPos = ScAddress(2,2,0);
1506 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1508 aPos = ScAddress(2,3,0);
1509 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1511 // Insert cells over A1:A3 to only shift A1 down to A4.
1512 m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
1514 aPos = ScAddress(2,2,0);
1515 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A4", "Wrong formula in C3.");
1517 aPos = ScAddress(2,3,0);
1518 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$4", "Wrong formula in C4.");
1520 // .. and back.
1521 m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
1523 aPos = ScAddress(2,2,0);
1524 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1526 aPos = ScAddress(2,3,0);
1527 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1529 // Delete row 1 which will delete the value cell (A1).
1530 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
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()));
1541 // Clear all and start over.
1542 clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
1544 // Test range updates
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);
1552 m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
1553 m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
1555 aPos = ScAddress(0,5,0);
1556 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1558 aPos = ScAddress(0,6,0);
1559 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1561 // Insert a row at row 1.
1562 m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1564 aPos = ScAddress(0,6,0);
1565 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B3:C4)", "Wrong formula in A7.");
1567 aPos = ScAddress(0,7,0);
1568 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$3:$C$4)", "Wrong formula in A8.");
1570 // ... and back.
1571 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1573 aPos = ScAddress(0,5,0);
1574 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1576 aPos = ScAddress(0,6,0);
1577 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1579 // Insert columns B:C to shift only the value range.
1580 m_pDoc->InsertCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1582 aPos = ScAddress(0,5,0);
1583 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(D2:E3)", "Wrong formula in A6.");
1585 aPos = ScAddress(0,6,0);
1586 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($D$2:$E$3)", "Wrong formula in A7.");
1588 // ... and back.
1589 m_pDoc->DeleteCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1591 aPos = ScAddress(0,5,0);
1592 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1594 aPos = ScAddress(0,6,0);
1595 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1597 // Insert rows 5:6 to shift the formula cells only.
1598 m_pDoc->InsertRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1600 aPos = ScAddress(0,7,0);
1601 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A8.");
1603 aPos = ScAddress(0,8,0);
1604 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A9.");
1606 // ... and back.
1607 m_pDoc->DeleteRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1609 aPos = ScAddress(0,5,0);
1610 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1612 aPos = ScAddress(0,6,0);
1613 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
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)));
1619 // Insert cells over B1:B2 to partially shift value range.
1620 m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
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)));
1626 // ... and shift them back.
1627 m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
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)));
1633 // Delete rows 2:3 to completely remove the referenced range.
1634 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),2,0));
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()));
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()));
1645 m_pDoc->DeleteTab(0);
1648 void Test::testFormulaRefUpdateRange()
1650 m_pDoc->InsertTab(0, "Formula");
1652 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1654 setExpandRefs(false);
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);
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)");
1670 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(B2:C5)", "Wrong formula in A7.");
1672 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($B$2:$C$5)", "Wrong formula in A8.");
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)));
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));
1680 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(B2:C4)", "Wrong formula in A6.");
1682 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($B$2:$C$4)", "Wrong formula in A7.");
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)));
1687 // Delete row 4 - bottom of range
1688 m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
1690 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(B2:C3)", "Wrong formula in A5.");
1692 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($B$2:$C$3)", "Wrong formula in A6.");
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)));
1697 // Delete row 2 - top of range
1698 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1700 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,3,0), "SUM(B2:C2)", "Wrong formula in A4.");
1702 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM($B$2:$C$2)", "Wrong formula in A5.");
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)));
1707 // Clear the range and start over.
1708 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
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);
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)");
1724 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:F3)", "Wrong formula in A2.");
1726 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$F$3)", "Wrong formula in A3.");
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)));
1731 // Delete column D.
1732 m_pDoc->DeleteCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
1734 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:E3)", "Wrong formula in A2.");
1736 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$E$3)", "Wrong formula in A3.");
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)));
1741 // Delete column E - the right edge of reference range.
1742 m_pDoc->DeleteCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
1744 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:D3)", "Wrong formula in A2.");
1746 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$D$3)", "Wrong formula in A3.");
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)));
1751 // Delete column C - the left edge of reference range.
1752 m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1754 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:C3)", "Wrong formula in A2.");
1756 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$C$3)", "Wrong formula in A3.");
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)));
1761 // Clear the range and start over.
1762 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1764 // Disable expansion of range reference on insertion in adjacent areas.
1765 setExpandRefs(false);
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);
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)");
1777 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
1779 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
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));
1784 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(D2:E3)", "Wrong formula in A5.");
1786 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($D$2:$E$3)", "Wrong formula in A6.");
1788 // Shift it back.
1789 m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1791 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
1793 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
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));
1798 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
1800 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
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));
1805 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
1807 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
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));
1812 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(C3:E4)", "Wrong formula in A6.");
1814 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($C$3:$E$4)", "Wrong formula in A7.");
1816 // Insert at row 4 to expand the reference range.
1817 m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
1819 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(C3:E5)", "Wrong formula in A7.");
1821 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($C$3:$E$5)", "Wrong formula in A8.");
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));
1826 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM(C3:E5)", "Wrong formula in A8.");
1828 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM($C$3:$E$5)", "Wrong formula in A9.");
1830 // Clear the range and start over.
1831 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1833 // Turn edge expansion on.
1834 setExpandRefs(true);
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);
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)");
1846 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:D7)", "Wrong formula in A2.");
1848 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$D$7)", "Wrong formula in A3.");
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));
1853 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:E7)", "Wrong formula in A2.");
1855 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$E$7)", "Wrong formula in A3.");
1857 // Insert at column C to edge-expand the reference range.
1858 m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1860 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F7)", "Wrong formula in A2.");
1862 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$7)", "Wrong formula in A3.");
1864 // Insert at row 8 to edge-expand.
1865 m_pDoc->InsertRow(ScRange(0,7,0,m_pDoc->MaxCol(),7,0));
1867 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F8)", "Wrong formula in A2.");
1869 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$8)", "Wrong formula in A3.");
1871 // Insert at row 6 to edge-expand.
1872 m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
1874 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F9)", "Wrong formula in A2.");
1876 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$9)", "Wrong formula in A3.");
1878 m_pDoc->InsertTab(1, "StickyRange");
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();
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));
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();
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();
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));
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();
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();
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));
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();
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));
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));
2077 m_pDoc->DeleteTab(1);
2079 m_pDoc->DeleteTab(0);
2082 void Test::testFormulaRefUpdateSheets()
2084 m_pDoc->InsertTab(0, "Sheet1");
2085 m_pDoc->InsertTab(1, "Sheet2");
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);
2093 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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);
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)");
2105 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2107 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
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);
2116 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2118 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
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);
2127 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2129 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2131 // Insert a new sheet between the two.
2132 m_pDoc->InsertTab(1, "Temp");
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);
2139 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2141 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2143 // Move the last sheet (Sheet2) to the first position.
2144 m_pDoc->MoveTab(2, 0);
2146 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2148 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2150 // Move back.
2151 m_pDoc->MoveTab(0, 2);
2153 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2155 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2157 // Move the "Temp" sheet to the last position.
2158 m_pDoc->MoveTab(1, 2);
2160 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2162 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2164 // Move back.
2165 m_pDoc->MoveTab(2, 1);
2167 // Delete the temporary sheet.
2168 m_pDoc->DeleteTab(1);
2170 m_pDoc->GetName(1, aName);
2171 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2173 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2175 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2177 // Insert a new sheet before the first one.
2178 m_pDoc->InsertTab(0, "Temp");
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);
2185 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2187 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2189 // Delete the temporary sheet.
2190 m_pDoc->DeleteTab(0);
2192 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2194 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2196 // Append a bunch of sheets.
2197 m_pDoc->InsertTab(2, "Temp1");
2198 m_pDoc->InsertTab(3, "Temp2");
2199 m_pDoc->InsertTab(4, "Temp3");
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);
2205 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2207 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2209 // Delete the temp sheets.
2210 m_pDoc->DeleteTab(4);
2211 m_pDoc->DeleteTab(3);
2212 m_pDoc->DeleteTab(2);
2214 // Delete Sheet1.
2215 m_pDoc->DeleteTab(0);
2216 m_pDoc->GetName(0, aName);
2217 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2219 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(#REF!.B2:C3)", "Wrong formula in Sheet2.B2.");
2221 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2223 m_pDoc->DeleteTab(0);
2226 void Test::testFormulaRefUpdateInsertRows()
2228 setExpandRefs(false);
2230 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2231 m_pDoc->InsertTab(0, "Formula");
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);
2238 // Insert a formula in B5 to sum up B2:B4.
2239 m_pDoc->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
2241 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,4,0)));
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);
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)));
2254 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,6,0), "SUM(B4:B6)", "Wrong formula!");
2256 // Clear and start over.
2257 clearSheet(m_pDoc, 0);
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);
2264 // Set formula in A3 to reference A4:A6.
2265 m_pDoc->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
2267 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
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)));
2276 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "MAX(A7:A9)", "Wrong formula!");
2278 m_pDoc->DeleteTab(0);
2281 void Test::testFormulaRefUpdateSheetsDelete()
2283 m_pDoc->InsertTab(0, "Sheet1");
2284 m_pDoc->InsertTab(1, "Sheet2");
2285 m_pDoc->InsertTab(2, "Sheet3");
2286 m_pDoc->InsertTab(3, "Sheet4");
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);
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");
2295 m_pDoc->InsertTab(1, "Sheet2");
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);
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");
2304 m_pDoc->InsertTab(2, "Sheet3");
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);
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");
2313 m_pDoc->InsertTab(0, "Sheet1");
2314 m_pDoc->InsertTab(1, "Sheet2");
2315 m_pDoc->InsertTab(2, "Sheet3");
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");
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");
2325 m_pDoc->DeleteTab(2);
2327 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.A2:Sheet2.A2)", "Wrong Formula");
2329 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,1), "SUM(Sheet1.A1:Sheet2.A1)", "Wrong Formula");
2331 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,1), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2333 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.A2:$Sheet2.A2)", "Wrong Formula");
2335 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,1), "SUM($Sheet1.A1:$Sheet2.A1)", "Wrong Formula");
2337 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,1), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2339 m_pDoc->DeleteTab(0);
2341 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet2.A2:Sheet2.A2)", "Wrong Formula");
2343 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "SUM(Sheet2.A1:Sheet2.A1)", "Wrong Formula");
2345 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2347 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet2.A2:$Sheet2.A2)", "Wrong Formula");
2349 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,0), "SUM($Sheet2.A1:$Sheet2.A1)", "Wrong Formula");
2351 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,0), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2353 m_pDoc->DeleteTab(0);
2354 m_pDoc->DeleteTab(0);
2357 void Test::testFormulaRefUpdateInsertColumns()
2359 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2360 setExpandRefs(false);
2362 m_pDoc->InsertTab(0, "Formula");
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);
2369 // Set named range for entire absolute column B.
2370 bInserted = m_pDoc->InsertNewRangeName("EntireColumn", aNamePos, "$B:$B");
2371 CPPUNIT_ASSERT(bInserted);
2373 // Set named range for entire absolute row 2.
2374 bInserted = m_pDoc->InsertNewRangeName("EntireRow", aNamePos, "$2:$2");
2375 CPPUNIT_ASSERT(bInserted);
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);
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)));
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)));
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)));
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)));
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);
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.");
2407 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
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);
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.");
2419 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,1,0)));
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);
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.");
2430 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(4,2,0)));
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);
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.");
2442 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(4,3,0)));
2444 m_pDoc->DeleteTab(0);
2447 void Test::testFormulaRefUpdateMove()
2449 m_pDoc->InsertTab(0, "Sheet1");
2451 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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);
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");
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));
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);
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));
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.");
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);
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));
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.");
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);
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));
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.");
2517 m_pDoc->DeleteTab(0);
2520 void Test::testFormulaRefUpdateMoveUndo()
2522 m_pDoc->InsertTab(0, "Test");
2524 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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);
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.");
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.");
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.");
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.");
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.");
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.");
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);
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.");
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.");
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.");
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.");
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.");
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.");
2581 // Undo the move.
2582 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2583 CPPUNIT_ASSERT(pUndoMgr);
2584 pUndoMgr->Undo();
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.");
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.");
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.");
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.");
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.");
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." );
2604 // Make sure the broadcasters are still valid by changing the value of A1.
2605 m_pDoc->SetValue(ScAddress(0,0,0), 20);
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)));
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)));
2615 m_pDoc->DeleteTab(0);
2618 void Test::testFormulaRefUpdateMoveUndo2()
2620 m_pDoc->InsertTab(0, "Test");
2622 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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)" },
2630 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2632 std::vector<std::vector<const char*>> aCheckInitial = {
2633 { "1", "2", "30", "3" },
2634 { "3", "4", "7", "7" },
2635 { "3", nullptr, nullptr, nullptr },
2638 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2639 CPPUNIT_ASSERT(bGood);
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());
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);
2651 std::vector<std::vector<const char*>> aCheckAfter = {
2652 { nullptr, nullptr, "10", "3" },
2653 { "1", "2", "3", "3" },
2654 { "3", nullptr, nullptr, nullptr },
2657 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:B1 moved to A2:B2");
2658 CPPUNIT_ASSERT(bGood);
2660 // Undo the move.
2661 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2662 CPPUNIT_ASSERT(pUndoMgr);
2663 pUndoMgr->Undo();
2665 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2666 CPPUNIT_ASSERT(bGood);
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());
2673 // Redo and check.
2674 pUndoMgr->Redo();
2676 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2677 CPPUNIT_ASSERT(bGood);
2679 m_pDoc->DeleteTab(0);
2682 void Test::testFormulaRefUpdateMoveUndo3NonShared()
2684 m_pDoc->InsertTab(0, "Test");
2686 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2688 std::vector<std::vector<const char*>> aData = {
2689 { "10", nullptr, nullptr },
2690 { "=A1", nullptr, nullptr },
2691 { "=A2+A1", nullptr, nullptr },
2694 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2696 std::vector<std::vector<const char*>> aCheckInitial = {
2697 { "10", nullptr, nullptr },
2698 { "10", nullptr, nullptr },
2699 { "20", nullptr, nullptr },
2702 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2703 CPPUNIT_ASSERT(bGood);
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);
2710 std::vector<std::vector<const char*>> aCheckAfter = {
2711 { "10", nullptr, nullptr},
2712 { nullptr, nullptr, "10" },
2713 { nullptr, nullptr, "20" },
2716 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A3 moved to C2:C3");
2717 CPPUNIT_ASSERT(bGood);
2719 // Undo the move.
2720 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2721 CPPUNIT_ASSERT(pUndoMgr);
2722 pUndoMgr->Undo();
2724 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2725 CPPUNIT_ASSERT(bGood);
2727 // Redo and check.
2728 pUndoMgr->Redo();
2730 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2731 CPPUNIT_ASSERT(bGood);
2733 m_pDoc->DeleteTab(0);
2736 void Test::testFormulaRefUpdateMoveUndo3Shared()
2738 m_pDoc->InsertTab(0, "Test");
2740 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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 },
2749 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2751 std::vector<std::vector<const char*>> aCheckInitial = {
2752 { "10", nullptr, nullptr },
2753 { "10", nullptr, nullptr },
2754 { "20", nullptr, nullptr },
2755 { "30", nullptr, nullptr },
2758 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2759 CPPUNIT_ASSERT(bGood);
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());
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);
2771 std::vector<std::vector<const char*>> aCheckAfter = {
2772 { "10", nullptr, nullptr},
2773 { nullptr, nullptr, "10" },
2774 { nullptr, nullptr, "20" },
2775 { nullptr, nullptr, "30" },
2778 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A4 moved to C2:C4");
2779 CPPUNIT_ASSERT(bGood);
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());
2786 // Undo the move.
2787 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2788 CPPUNIT_ASSERT(pUndoMgr);
2789 pUndoMgr->Undo();
2791 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2792 CPPUNIT_ASSERT(bGood);
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());
2799 // Redo and check.
2800 pUndoMgr->Redo();
2802 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2803 CPPUNIT_ASSERT(bGood);
2805 m_pDoc->DeleteTab(0);
2808 void Test::testFormulaRefUpdateMoveUndoDependents()
2810 m_pDoc->InsertTab(0, "Test");
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" },
2823 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData);
2825 std::vector<std::vector<const char*>> aCheckInitial = {
2826 { "1" },
2827 { "22" },
2828 { "3" },
2829 { "4" },
2830 { "5" },
2831 { "35" },
2832 { "35" },
2835 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2836 CPPUNIT_ASSERT(bGood);
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);
2843 std::vector<std::vector<const char*>> aCheckAfter = {
2844 { "1" },
2845 { nullptr },
2846 { "3" },
2847 { "4" },
2848 { "5" },
2849 { "13" },
2850 { "13" },
2853 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "C2 moved to D2");
2854 CPPUNIT_ASSERT(bGood);
2856 // Undo the move.
2857 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2858 CPPUNIT_ASSERT(pUndoMgr);
2859 pUndoMgr->Undo();
2861 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2862 CPPUNIT_ASSERT(bGood);
2864 // Redo and check.
2865 pUndoMgr->Redo();
2867 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2868 CPPUNIT_ASSERT(bGood);
2870 m_pDoc->DeleteTab(0);
2873 void Test::testFormulaRefUpdateMoveUndo4()
2875 m_pDoc->InsertTab(0, "Test");
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" },
2883 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2885 std::vector<std::vector<const char*>> aCheckInitial = {
2886 { "1", nullptr, "0", "1" },
2887 { "2", nullptr, "0", "2" },
2890 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2891 CPPUNIT_ASSERT(bGood);
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);
2898 std::vector<std::vector<const char*>> aCheckAfter = {
2899 { nullptr, "1", "1", "1" },
2900 { nullptr, "2", "2", "2" },
2903 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:A2 moved to B1:B2");
2904 CPPUNIT_ASSERT(bGood);
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
2911 // Undo the move.
2912 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2913 CPPUNIT_ASSERT(pUndoMgr);
2914 pUndoMgr->Undo();
2916 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2917 CPPUNIT_ASSERT(bGood);
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
2924 // Redo and check.
2925 pUndoMgr->Redo();
2927 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2928 CPPUNIT_ASSERT(bGood);
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
2935 m_pDoc->DeleteTab(0);
2938 void Test::testFormulaRefUpdateMoveToSheet()
2940 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2942 m_pDoc->InsertTab(0, "Sheet1");
2943 m_pDoc->InsertTab(1, "Sheet2");
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");
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");
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)));
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);
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");
2965 // Undo and check again.
2966 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2967 pUndoMgr->Undo();
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");
2972 // Redo and check.
2973 pUndoMgr->Redo();
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");
2978 m_pDoc->DeleteTab(1);
2979 m_pDoc->DeleteTab(0);
2982 void Test::testFormulaRefUpdateDeleteContent()
2984 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2986 m_pDoc->InsertTab(0, "Test");
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");
2993 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
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);
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)));
3004 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3005 CPPUNIT_ASSERT(pUndoMgr);
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
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)));
3017 m_pDoc->DeleteTab(0);
3020 void Test::testFormulaRefUpdateDeleteAndShiftLeft()
3022 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3024 m_pDoc->InsertTab(0, "Test");
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);
3030 // Insert formula in H1.
3031 ScAddress aPos(7,0,0);
3032 m_pDoc->SetString(aPos, "=SUM(C1:G1)");
3034 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
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);
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!");
3047 // Undo and check.
3048 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3049 CPPUNIT_ASSERT(pUndo);
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!");
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);
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!");
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!");
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);
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!");
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!");
3084 // Start over with a new scenario.
3085 clearSheet(m_pDoc, 0);
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);
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));
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);
3100 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3101 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
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!");
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);
3112 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3113 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:F1)", "Wrong formula!");
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!");
3120 m_pDoc->DeleteTab(0);
3123 void Test::testFormulaRefUpdateDeleteAndShiftLeft2()
3125 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3127 m_pDoc->InsertTab(0, "Test");
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)" },
3136 insertRangeData(m_pDoc, ScAddress(), aData);
3138 auto funcCheckOriginal = [&]()
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
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
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
3156 auto funcCheckDeleted = [&]()
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
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
3169 funcCheckOriginal();
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);
3178 funcCheckDeleted();
3180 // Undo and check.
3181 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3182 CPPUNIT_ASSERT(pUndo);
3184 pUndo->Undo();
3185 funcCheckOriginal();
3187 // Redo and check.
3188 pUndo->Redo();
3189 funcCheckDeleted();
3191 m_pDoc->DeleteTab(0);
3194 void Test::testFormulaRefUpdateDeleteAndShiftUp()
3196 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3198 m_pDoc->InsertTab(0, "Test");
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);
3204 // Insert formula in A8.
3205 ScAddress aPos(0,7,0);
3206 m_pDoc->SetString(aPos, "=SUM(A3:A7)");
3208 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
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);
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!");
3221 // Undo and check.
3222 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3223 CPPUNIT_ASSERT(pUndo);
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!");
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);
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!");
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!");
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);
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!");
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!");
3258 // Start over with a new scenario.
3259 clearSheet(m_pDoc, 0);
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);
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));
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);
3274 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3275 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
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!");
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);
3286 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3287 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A6)", "Wrong formula!");
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!");
3294 m_pDoc->DeleteTab(0);
3297 void Test::testFormulaRefUpdateName()
3299 m_pDoc->InsertTab(0, "Formula");
3301 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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);
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);
3316 bool bInserted = pGlobalNames->insert(pName);
3317 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
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");
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));
3331 // Push cells in column C down by one cell.
3332 m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
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));
3340 // Move cells back.
3341 m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
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));
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);
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);
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)));
3364 // Shift B10:B12 to right by 2 columns.
3365 m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
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);
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)));
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)));
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);
3389 pName = pGlobalNames->findByUpperName("MYRANGE");
3390 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3392 m_pDoc->SetValue(ScAddress(3,9,1), 10);
3393 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
3395 // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
3396 m_pDoc->DeleteTab(0);
3398 aName.clear();
3399 m_pDoc->GetName(0, aName);
3400 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3402 pName = pGlobalNames->findByUpperName("MYRANGE");
3403 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3405 m_pDoc->SetValue(ScAddress(3,9,0), 11);
3406 CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3408 // Clear all and start over.
3409 clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
3410 pGlobalNames->clear();
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);
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));
3422 // This should not alter the range.
3423 pName->GetSymbol(aExpr);
3424 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3426 m_pDoc->DeleteTab(0);
3429 void Test::testFormulaRefUpdateNameMove()
3431 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3433 m_pDoc->InsertTab(0, "Test");
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);
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);
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)));
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);
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);
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);
3463 // The value of A10 should remain unchanged.
3464 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3466 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3467 CPPUNIT_ASSERT(pUndoMgr);
3469 // Undo and check.
3470 pUndoMgr->Undo();
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)));
3478 // Redo and check.
3479 pUndoMgr->Redo();
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)));
3487 // Undo again to bring it back to the initial condition, and clear the undo buffer.
3488 pUndoMgr->Undo();
3489 pUndoMgr->Clear();
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());
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);
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)));
3506 // Clear and start over.
3507 clearSheet(m_pDoc, 0);
3508 m_pDoc->GetRangeName()->clear();
3510 // Set value to B2.
3511 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3513 // Define B2 as 'MyCell'.
3514 bInserted = m_pDoc->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
3515 CPPUNIT_ASSERT(bInserted);
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)));
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);
3525 // Value in B3 should remain unchanged.
3526 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3528 m_pDoc->DeleteTab(0);
3531 void Test::testFormulaRefUpdateNameExpandRef()
3533 setExpandRefs(true);
3535 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3537 m_pDoc->InsertTab(0, "Test");
3539 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
3540 CPPUNIT_ASSERT(bInserted);
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);
3547 m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
3548 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
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);
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)));
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);
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)));
3580 // Clear the document and start over.
3581 m_pDoc->GetRangeName()->clear();
3582 clearSheet(m_pDoc, 0);
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);
3589 bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
3590 CPPUNIT_ASSERT(bInserted);
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));
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);
3599 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3600 CPPUNIT_ASSERT(pName);
3602 pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3603 CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
3605 // Clear the document and start over.
3606 m_pDoc->GetRangeName()->clear();
3607 clearSheet(m_pDoc, 0);
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);
3614 // Name A1:A3 'MyData'.
3615 bInserted = m_pDoc->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
3616 CPPUNIT_ASSERT(bInserted);
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)");
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());
3629 // E1 should not be shared.
3630 pFC = m_pDoc->GetFormulaCell(ScAddress(4,0,0));
3631 CPPUNIT_ASSERT(pFC);
3632 CPPUNIT_ASSERT(!pFC->IsShared());
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)));
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);
3642 // Set new value to A3.
3643 m_pDoc->SetValue(ScAddress(0,2,0), 4.0);
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)));
3650 m_pDoc->DeleteTab(0);
3653 void Test::testFormulaRefUpdateNameExpandRef2()
3655 setExpandRefs(true);
3657 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3659 m_pDoc->InsertTab(0, "Test");
3661 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$B$3");
3662 CPPUNIT_ASSERT(bInserted);
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);
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);
3677 m_pDoc->DeleteTab(0);
3680 void Test::testFormulaRefUpdateNameDeleteRow()
3682 m_pDoc->InsertTab(0, "Test");
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);
3688 const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3689 CPPUNIT_ASSERT(pName);
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);
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);
3700 const ScRangeData* pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3701 CPPUNIT_ASSERT(pName2);
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);
3708 ScDocFunc& rFunc = getDocShell().GetDocFunc();
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);
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);
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);
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);
3728 // Undo and check.
3729 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3730 CPPUNIT_ASSERT(pUndoMgr);
3732 pUndoMgr->Undo();
3734 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3735 CPPUNIT_ASSERT(pName);
3736 pCode = pName->GetCode();
3738 aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3739 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
3741 // Undo again and check.
3742 pUndoMgr->Undo();
3744 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3745 CPPUNIT_ASSERT(pName);
3746 pCode = pName->GetCode();
3748 aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3749 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3751 // Delete row 2-3.
3752 rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3754 aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3755 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
3757 // Undo and check.
3758 pUndoMgr->Undo();
3760 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3761 CPPUNIT_ASSERT(pName);
3762 pCode = pName->GetCode();
3764 aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3765 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3767 pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3768 CPPUNIT_ASSERT(pName2);
3769 pCode2 = pName2->GetCode();
3771 aExpr2 = pCode2->CreateString(m_pDoc, aCxt2, ScAddress(0,0,0));
3772 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3774 m_pDoc->InsertTab(1, "test2");
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);
3780 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3781 CPPUNIT_ASSERT(pName);
3782 pCode = pName->GetCode();
3784 aExpr = pCode->CreateString(m_pDoc, aCxt, ScAddress(0,0,0));
3785 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3787 pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3788 CPPUNIT_ASSERT(pName2);
3789 pCode2 = pName2->GetCode();
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);
3800 m_pDoc->DeleteTab(1);
3801 m_pDoc->DeleteTab(0);
3804 void Test::testFormulaRefUpdateNameCopySheet()
3806 m_pDoc->InsertTab(0, "Test");
3807 m_pDoc->InsertTab(1, "Test2");
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);
3816 // insert formula into Test2 that is =RED+BLUE
3817 m_pDoc->SetString(ScAddress(2,2,1), "=RED+BLUE");
3819 double nVal = m_pDoc->GetValue(2, 2, 1);
3820 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3821 m_pDoc->CopyTab(1, 0);
3823 nVal = m_pDoc->GetValue(2, 2, 2);
3824 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3826 nVal = m_pDoc->GetValue(2, 2, 0);
3827 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3829 m_pDoc->SetValue(1, 1, 1, 3);
3831 nVal = m_pDoc->GetValue(2, 2, 2);
3832 CPPUNIT_ASSERT_EQUAL(5.0, nVal);
3834 nVal = m_pDoc->GetValue(2, 2, 0);
3835 CPPUNIT_ASSERT_EQUAL(5.0, nVal);
3837 m_pDoc->DeleteTab(2);
3838 m_pDoc->DeleteTab(1);
3839 m_pDoc->DeleteTab(0);
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);
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);
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);
3874 m_pDoc->DeleteTab(2);
3875 m_pDoc->DeleteTab(1);
3876 m_pDoc->DeleteTab(0);
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);
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);
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);
3907 m_pDoc->DeleteTab(2);
3908 m_pDoc->DeleteTab(1);
3909 m_pDoc->DeleteTab(0);
3910 m_pDoc->SetRangeName(nullptr);
3912 // Test nested names during copying sheet.
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);
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");
3949 testFormulaRefUpdateNameCopySheetCheckTab( 0, false);
3951 // Copy sheet after.
3952 m_pDoc->CopyTab(0, 1);
3953 testFormulaRefUpdateNameCopySheetCheckTab( 0, false);
3954 testFormulaRefUpdateNameCopySheetCheckTab( 1, true);
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);
3962 m_pDoc->DeleteTab(2);
3963 m_pDoc->DeleteTab(1);
3964 m_pDoc->DeleteTab(0);
3967 void Test::testFormulaRefUpdateNameCopySheetCheckTab( SCTAB nTab, bool bCheckNames )
3969 if (bCheckNames)
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);
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));
4011 void Test::testFormulaRefUpdateNameDelete()
4013 m_pDoc->InsertTab(0, "Test");
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);
4019 const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4020 CPPUNIT_ASSERT(pName);
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);
4028 m_pDoc->DeleteTab(0);
4031 void Test::testFormulaRefUpdateValidity()
4033 struct {
4035 bool checkList( std::vector<ScTypedStrData>& rList )
4037 double aExpected[] = { 1.0, 2.0, 3.0 }; // must be sorted.
4038 size_t nCheckSize = SAL_N_ELEMENTS(aExpected);
4040 if (rList.size() != nCheckSize)
4042 cerr << "List size is not what is expected." << endl;
4043 return false;
4046 std::sort(rList.begin(), rList.end(), ScTypedStrData::LessCaseSensitive());
4048 for (size_t i = 0; i < nCheckSize; ++i)
4050 if (aExpected[i] != rList[i].GetValue())
4052 cerr << "Incorrect value at position " << i
4053 << ": expected=" << aExpected[i] << ", actual=" << rList[i].GetValue() << endl;
4054 return false;
4058 return true;
4061 } aCheck;
4063 setExpandRefs(false);
4064 setCalcAsShown(m_pDoc, true);
4066 m_pDoc->InsertTab(0, "Formula");
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);
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());
4078 sal_uLong nIndex = m_pDoc->AddValidationEntry(aData);
4079 SfxUInt32Item aItem(ATTR_VALIDDATA, nIndex);
4081 ScPatternAttr aNewAttrs(
4082 std::make_unique<SfxItemSet>(*m_pDoc->GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{}));
4083 aNewAttrs.GetItemSet().Put(aItem);
4085 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
4087 const ScValidationData* pData = m_pDoc->GetValidationEntry(nIndex);
4088 CPPUNIT_ASSERT(pData);
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);
4096 ScDocFunc& rFunc = getDocShell().GetDocFunc();
4097 ScMarkData aMark(MAXROW, MAXCOL);
4098 aMark.SelectOneTable(0);
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)));
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);
4113 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4114 CPPUNIT_ASSERT(pUndoMgr);
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)));
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);
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)));
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);
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)));
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);
4151 m_pDoc->DeleteTab(0);
4154 void Test::testTokenArrayRefUpdateMove()
4156 m_pDoc->InsertTab(0, "Sheet1");
4157 m_pDoc->InsertTab(1, "Sheet2");
4159 ScAddress aPos(0,0,0); // A1
4161 sc::TokenStringContext aCxt(m_pDoc, m_pDoc->GetGrammar());
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;
4169 std::vector<OUString> aTests = {
4170 "B1*C1",
4171 "SUM(B1:C1)",
4172 "$Sheet1.B1",
4173 "SUM(Sheet1.B1:Sheet2.B1)"
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.
4179 for (const OUString& aTest : aTests)
4181 ScCompiler aComp(m_pDoc, aPos, m_pDoc->GetGrammar());
4182 std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aTest));
4184 OUString aStr = pArray->CreateString(m_pDoc, aCxt, aPos);
4186 CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4188 // This formula cell isn't moving its position. The displayed formula
4189 // string should not change.
4190 pArray->AdjustReferenceOnMove(aRefCxt, aPos, aPos);
4192 aStr = pArray->CreateString(m_pDoc, aCxt, aPos);
4193 CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4196 m_pDoc->DeleteTab(1);
4197 m_pDoc->DeleteTab(0);
4200 void Test::testMultipleOperations()
4202 m_pDoc->InsertTab(0, "MultiOp");
4204 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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)));
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);
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));
4228 // Clear A3:B5.
4229 clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
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)));
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);
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));
4248 m_pDoc->DeleteTab(0);
4251 void Test::testFuncCOLUMN()
4253 m_pDoc->InsertTab(0, "Formula");
4254 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4256 m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
4257 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4259 m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4260 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
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)));
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)));
4269 // Move the column in the other direction.
4270 m_pDoc->DeleteCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4272 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
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)));
4277 m_pDoc->DeleteTab(0);
4280 void Test::testFuncCOUNT()
4282 m_pDoc->InsertTab(0, "Formula");
4283 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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
4289 ScAddress aPos(1,0,0);
4290 m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
4291 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
4293 aPos.IncRow();
4294 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
4295 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
4297 aPos.IncRow();
4298 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
4299 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
4301 aPos.IncRow();
4302 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
4303 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
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)));
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)));
4317 m_pDoc->DeleteTab(0);
4320 void Test::testFuncCOUNTBLANK()
4322 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4323 m_pDoc->InsertTab(0, "Formula");
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)" }
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);
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)));
4343 // Test single cell reference cases.
4345 clearSheet(m_pDoc, 0);
4347 const char* aData2[][2] = {
4348 { "1", "=COUNTBLANK(A1)" },
4349 { "A", "=COUNTBLANK(A2)" },
4350 { nullptr, "=COUNTBLANK(A3)" },
4351 { "=\"\"", "=COUNTBLANK(A4)" },
4352 { "=A4" , "=COUNTBLANK(A5)" },
4355 aRange = insertRangeData(m_pDoc, aPos, aData2, SAL_N_ELEMENTS(aData2));
4356 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
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)));
4364 m_pDoc->DeleteTab(0);
4367 void Test::testFuncROW()
4369 m_pDoc->InsertTab(0, "Formula");
4370 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4372 m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
4373 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4375 m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4376 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
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)));
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)));
4385 // Delete 2 rows to move it back.
4386 m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4388 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
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)));
4393 // Clear sheet and start over.
4394 clearSheet(m_pDoc, 0);
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)));
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());
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!");
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)));
4422 m_pDoc->DeleteTab(0);
4425 void Test::testFuncSUM()
4427 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4428 m_pDoc->InsertTab (0, "foo"));
4430 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
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)));
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);
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);
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)));
4459 // Clear and start over.
4460 clearRange(m_pDoc, ScRange(0,0,0,3,m_pDoc->MaxRow(),0));
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)));
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));
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))));
4485 m_pDoc->DeleteTab(0);
4488 void Test::testFuncPRODUCT()
4490 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4492 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab(0, "foo"));
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));
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);
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));
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));
4538 m_pDoc->DeleteTab(0);
4541 void Test::testFuncSUMPRODUCT()
4543 m_pDoc->InsertTab(0, "Test");
4545 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
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));
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);
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)));
4576 m_pDoc->DeleteTab(0);
4579 void Test::testFuncSUMXMY2()
4581 m_pDoc->InsertTab(0, "Test SumXMY2");
4583 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
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));
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);
4606 m_pDoc->DeleteTab(0);
4609 void Test::testFuncMIN()
4611 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4612 m_pDoc->InsertTab(0, "Formula");
4614 // A1:A2
4615 m_pDoc->SetString(ScAddress(0,0,0), "a");
4616 m_pDoc->SetString(ScAddress(0,1,0), "b");
4618 // B1:B2
4619 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
4620 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
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))");
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());
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);
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))));
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)));
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)));
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);
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)));
4656 m_pDoc->DeleteTab(0);
4659 void Test::testFuncN()
4661 OUString const aTabName("foo");
4662 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4663 m_pDoc->InsertTab (0, aTabName));
4665 double result;
4667 // Clear the area first.
4668 clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
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");
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)");
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\")");
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)");
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
4712 for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
4714 m_pDoc->GetValue(1, i, 0, result);
4715 bool bGood = result == checks1[i];
4716 if (!bGood)
4718 cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
4719 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4722 double checks2[] = {
4723 0, 1, -1, 12.3 // range references
4725 for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
4727 m_pDoc->GetValue(1, i+2, 0, result);
4728 bool bGood = result == checks2[i];
4729 if (!bGood)
4731 cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
4732 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4736 m_pDoc->DeleteTab(0);
4739 void Test::testFuncCOUNTIF()
4741 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4743 // COUNTIF (test case adopted from OOo i#36381)
4745 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4746 m_pDoc->InsertTab (0, "foo"));
4748 // Empty A1:A39 first.
4749 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
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"
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]));
4768 printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
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 }
4787 nRows = SAL_N_ELEMENTS(aChecks);
4788 for (SCROW i = 0; i < nRows; ++i)
4790 SCROW nRow = 20 + i;
4791 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
4794 for (SCROW i = 0; i < nRows; ++i)
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)
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);
4808 // Don't count empty strings when searching for a number.
4810 // Clear A1:A2.
4811 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
4813 m_pDoc->SetString(0, 0, 0, "=\"\"");
4814 m_pDoc->SetString(0, 1, 0, "=COUNTIF(A1;1)");
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);
4819 // Another test case adopted from fdo#77039.
4820 clearSheet(m_pDoc, 0);
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), "=\"\"");
4826 // Insert formula into A5 to count all cells with empty strings.
4827 m_pDoc->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
4829 // We should correctly count with empty string key.
4830 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,4,0)));
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)));
4856 m_pDoc->DeleteTab(0);
4859 void Test::testFuncIF()
4861 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4863 m_pDoc->InsertTab(0, "Formula");
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)));
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)));
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.
4884 const char* aData[][3] = {
4885 { "1", "1", "16" },
4886 { "0", "1", "32" },
4887 { "1", "0", "64" },
4888 { "0", "0", "128" }
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);
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)));
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)));
4905 m_pDoc->DeleteTab(0);
4908 void Test::testFuncCHOOSE()
4910 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4912 m_pDoc->InsertTab(0, "Formula");
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);
4927 m_pDoc->DeleteTab(0);
4930 void Test::testFuncIFERROR()
4932 // IFERROR/IFNA (fdo#56124)
4934 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4935 m_pDoc->InsertTab (0, "foo"));
4937 // Empty A1:A39 first.
4938 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
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"
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]));
4960 printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
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
4981 nRows = SAL_N_ELEMENTS(aChecks);
4982 for (SCROW i = 0; i < nRows-2; ++i)
4984 SCROW nRow = 20 + i;
4985 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
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)");
4994 m_pDoc->CalcAll();
4996 for (SCROW i = 0; i < nRows; ++i)
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);
5004 const SCCOL nCols = 3;
5005 const char* aData2[][nCols] = {
5006 { "1", "2", "3" },
5007 { "4", "=1/0", "6" },
5008 { "7", "8", "9" }
5010 const char* aCheck2[][nCols] = {
5011 { "1", "2", "3" },
5012 { "4", "Error","6" },
5013 { "7", "8", "9" }
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);
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\")");
5028 m_pDoc->CalcAll();
5030 for (SCCOL nCol = nStartCol; nCol < nStartCol + nCols; ++nCol)
5032 for (SCROW nRow = nStartRow; nRow < nStartRow + nElems2; ++nRow)
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);
5040 m_pDoc->DeleteTab(0);
5043 void Test::testFuncSHEET()
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));
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);
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());
5058 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5059 m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
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);
5066 SCTAB nTabCount = m_pDoc->GetTableCount();
5067 m_pDoc->DeleteTab(--nTabCount);
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);
5073 m_pDoc->DeleteTab(--nTabCount);
5076 void Test::testFuncNOW()
5078 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5079 m_pDoc->InsertTab (0, "foo"));
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);
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);
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);
5102 m_pDoc->DeleteTab(0);
5105 void Test::testFuncNUMBERVALUE()
5107 // NUMBERVALUE fdo#57180
5109 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5110 m_pDoc->InsertTab (0, "foo"));
5112 // Empty A1:A39 first.
5113 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
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%"
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]));
5129 printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
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" }
5145 nRows = SAL_N_ELEMENTS(aChecks);
5146 for (SCROW i = 0; i < nRows; ++i)
5148 SCROW nRow = 20 + i;
5149 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5151 m_pDoc->CalcAll();
5153 for (SCROW i = 0; i < nRows; ++i)
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);
5161 m_pDoc->DeleteTab(0);
5164 void Test::testFuncLEN()
5166 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5168 m_pDoc->InsertTab(0, "Formula");
5170 // Leave A1:A3 empty, and insert an array of LEN in B1:B3 that references
5171 // these empty cells.
5173 ScMarkData aMark(MAXROW, MAXCOL);
5174 aMark.SelectOneTable(0);
5175 m_pDoc->InsertMatrixFormula(1, 0, 1, 2, aMark, "=LEN(A1:A3)");
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());
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);
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)));
5194 m_pDoc->DeleteTab(0);
5197 void Test::testFuncLOOKUP()
5199 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5201 m_pDoc->InsertTab(0, "Test");
5203 // Raw data
5204 const char* aData[][2] = {
5205 { "=CONCATENATE(\"A\")", "1" },
5206 { "=CONCATENATE(\"B\")", "2" },
5207 { "=CONCATENATE(\"C\")", "3" },
5208 { nullptr, nullptr } // terminator
5211 // Insert raw data into A1:B3.
5212 for (SCROW i = 0; aData[i][0]; ++i)
5214 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5215 m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
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
5225 // Insert check formulas into A5:B7.
5226 for (SCROW i = 0; aData2[i][0]; ++i)
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]));
5232 printRange(m_pDoc, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
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))));
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)));
5243 m_pDoc->DeleteTab(0);
5246 void Test::testFuncLOOKUParrayWithError()
5248 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5249 m_pDoc->InsertTab(0, "Test");
5251 std::vector<std::vector<const char*>> aData = {
5252 { "x", "y", "z" },
5253 { "a", "b", "c" }
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
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));
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));
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));
5272 m_pDoc->DeleteTab(0);
5275 void Test::testFuncVLOOKUP()
5277 // VLOOKUP
5279 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5280 m_pDoc->InsertTab (0, "foo"));
5282 // Clear A1:F40.
5283 clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
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
5304 // Insert raw data into A1:B14.
5305 for (SCROW i = 0; aData[i][0]; ++i)
5307 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5308 m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5311 printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
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" }
5337 // Insert formula data into D1:E18.
5338 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5340 m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
5341 m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
5343 m_pDoc->CalcAll();
5344 printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
5346 // Verify results.
5347 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5349 if (i == 0)
5350 // Skip the header row.
5351 continue;
5353 OUString aRes = m_pDoc->GetString(4, i, 0);
5354 bool bGood = aRes.equalsAscii(aChecks[i].pRes);
5355 if (!bGood)
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);
5363 // Clear the sheet and start over.
5364 clearSheet(m_pDoc, 0);
5366 // Lookup on sorted data interspersed with empty cells.
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");
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);
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();
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)));
5398 // Start over again.
5399 clearSheet(m_pDoc, 0);
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");
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)));
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" }
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);
5430 aPos2.SetCol(3); // column D formula results
5431 for (size_t i=0; i < SAL_N_ELEMENTS(aData2); ++i)
5433 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aData2[i][4]), m_pDoc->GetString(aPos2));
5434 aPos2.IncRow();
5437 m_pDoc->DeleteTab(0);
5440 struct StrStrCheck {
5441 const char* pVal;
5442 const char* pRes;
5445 template<size_t DataSize, size_t FormulaSize, int Type>
5446 static void runTestMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
5448 size_t nDataSize = DataSize;
5449 for (size_t i = 0; i < nDataSize; ++i)
5450 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5452 for (size_t i = 0; i < FormulaSize; ++i)
5454 pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
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);
5468 pDoc->CalcAll();
5469 Test::printRange(pDoc, ScRange(0, 0, 0, 2, FormulaSize-1, 0), "MATCH");
5471 // verify the results.
5472 for (size_t i = 0; i < FormulaSize; ++i)
5474 OUString aStr = pDoc->GetString(2, i, 0);
5475 if (!aStr.equalsAscii(aChecks[i].pRes))
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);
5484 template<size_t DataSize, size_t FormulaSize, int Type>
5485 static void runTestHorizontalMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
5487 size_t nDataSize = DataSize;
5488 for (size_t i = 0; i < nDataSize; ++i)
5489 pDoc->SetString(i, 0, 0, OUString::createFromAscii(aData[i]));
5491 for (size_t i = 0; i < FormulaSize; ++i)
5493 pDoc->SetString(i, 1, 0, OUString::createFromAscii(aChecks[i].pVal));
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);
5508 pDoc->CalcAll();
5509 Test::printRange(pDoc, ScRange(0, 0, 0, FormulaSize-1, 2, 0), "MATCH");
5511 // verify the results.
5512 for (size_t i = 0; i < FormulaSize; ++i)
5514 OUString aStr = pDoc->GetString(i, 2, 0);
5515 if (!aStr.equalsAscii(aChecks[i].pRes))
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);
5524 void Test::testFuncMATCH()
5526 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5527 m_pDoc->InsertTab (0, "foo"));
5529 clearRange(m_pDoc, ScRange(0, 0, 0, 40, 40, 0));
5531 // Ascending in-exact match
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",
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" }
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));
5575 // Descending in-exact match
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",
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" }
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));
5620 // search range contains leading and trailing empty cell ranges.
5622 clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
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);
5630 // Find value 2 which is in A6.
5631 m_pDoc->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
5632 m_pDoc->CalcAll();
5634 CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc->GetString(ScAddress(1,0,0)));
5638 // Test the ReferenceOrForceArray parameter.
5640 clearRange(m_pDoc, ScRange(0,0,0,1,7,0));
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);
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)));
5656 m_pDoc->DeleteTab(0);
5659 void Test::testFuncCELL()
5661 OUString const aTabName("foo");
5662 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5663 m_pDoc->InsertTab (0, aTabName));
5665 clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
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;
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" }
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();
5690 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5692 OUString aVal = m_pDoc->GetString(0, i, 0);
5693 CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
5697 m_pDoc->DeleteTab(0);
5700 /** See also test case document fdo#44456 sheet cpearson */
5701 void Test::testFuncDATEDIF()
5703 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5704 m_pDoc->InsertTab (0, "foo"));
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
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);
5729 m_pDoc->CalcAll();
5731 for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
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]));
5738 m_pDoc->DeleteTab(0);
5741 void Test::testFuncINDIRECT()
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
5748 bool bGood = m_pDoc->GetName(0, aTabName);
5749 CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
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));
5755 OUString aPrefix = "=INDIRECT(\"";
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);
5766 m_pDoc->CalcAll();
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
5774 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5776 OUString aVal = m_pDoc->GetString(0, i, 0);
5777 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5781 ScCalcConfig aConfig;
5782 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO );
5783 m_pDoc->SetCalcConfig(aConfig);
5784 m_pDoc->CalcAll();
5786 // Explicit Calc A1 syntax
5787 const OUString* aChecks[] = {
5788 &aTest, &aRefErr, &aRefErr, &aTest
5791 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5793 OUString aVal = m_pDoc->GetString(0, i, 0);
5794 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5798 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 );
5799 m_pDoc->SetCalcConfig(aConfig);
5800 m_pDoc->CalcAll();
5802 // Excel A1 syntax
5803 const OUString* aChecks[] = {
5804 &aRefErr, &aTest, &aRefErr, &aTest
5807 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5809 OUString aVal = m_pDoc->GetString(0, i, 0);
5810 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5814 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
5815 m_pDoc->SetCalcConfig(aConfig);
5816 m_pDoc->CalcAll();
5818 // Excel R1C1 syntax
5819 const OUString* aChecks[] = {
5820 &aRefErr, &aRefErr, &aTest, &aTest
5823 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5825 OUString aVal = m_pDoc->GetString(0, i, 0);
5826 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
5830 m_pDoc->DeleteTab(0);
5833 // Test case for tdf#83365 - Access across spreadsheet returns Err:504
5835 void Test::testFuncINDIRECT2()
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"));
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);
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);
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
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)");
5884 m_pDoc->CalcAll();
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);
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);
5896 m_pDoc->DeleteTab(2);
5897 m_pDoc->DeleteTab(1);
5898 m_pDoc->DeleteTab(0);
5901 // Test for tdf#107724 do not propagate an array context from MATCH to INDIRECT
5902 // as INDIRECT returns ParamClass::Reference
5903 void Test::testFunc_MATCH_INDIRECT()
5905 CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab( 0, "foo"));
5907 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
5909 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
5910 ScRangeData* pRangeData = new ScRangeData( m_pDoc, "RoleAssignment", "$D$4:$D$13");
5911 pGlobalNames->insert(pRangeData);
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)");
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));
5924 m_pDoc->DeleteTab(0);
5927 void Test::testFormulaDepTracking()
5929 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
5931 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
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);
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);
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);
5949 clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
5951 // Now, let's test the range dependency tracking.
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);
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);
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.
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));
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));
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));
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));
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));
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));
5996 clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
5998 // Now, column-based dependency tracking. We now switch to the R1C1
5999 // syntax which is easier to use for repeated relative references.
6001 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
6003 val = 0.0;
6004 for (SCROW nRow = 1; nRow <= 9; ++nRow)
6006 // Static value in column 1.
6007 m_pDoc->SetValue(0, nRow, 0, ++val);
6009 // Formula in column 2 that references cell to the left.
6010 m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
6012 // Formula in column 3 that references cell to the left.
6013 m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
6016 // Check formula values.
6017 val = 0.0;
6018 for (SCROW nRow = 1; nRow <= 9; ++nRow)
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));
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));
6032 m_pDoc->DeleteTab(0);
6035 void Test::testFormulaDepTracking2()
6037 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
6039 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
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");
6050 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
6052 clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
6054 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
6056 m_pDoc->DeleteTab(0);
6059 void Test::testFormulaDepTracking3()
6061 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6063 m_pDoc->InsertTab(0, "Formula");
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 },
6071 insertRangeData(m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
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)));
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)));
6085 m_pDoc->DeleteTab(0);
6088 void Test::testFormulaDepTrackingDeleteRow()
6090 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6092 m_pDoc->InsertTab(0, "Test");
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);
6099 // SUM(A1:A3) in A5.
6100 m_pDoc->SetString(ScAddress(0,4,0), "=SUM(A1:A3)");
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);
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);
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)));
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);
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);
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)));
6138 m_pDoc->DeleteTab(0);
6141 void Test::testFormulaDepTrackingDeleteCol()
6143 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6145 m_pDoc->InsertTab(0, "Formula");
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
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);
6158 // Check the initial values.
6159 for (SCCOL i = 0; i <= 2; ++i)
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)));
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());
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());
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);
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!" },
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);
6197 // Undo and check the result.
6198 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6199 CPPUNIT_ASSERT(pUndoMgr);
6200 pUndoMgr->Undo();
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" },
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);
6216 // Redo and check.
6217 pUndoMgr->Redo();
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!" },
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);
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);
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" },
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);
6252 m_pDoc->DeleteTab(0);
6255 void Test::testFormulaMatrixResultUpdate()
6257 m_pDoc->InsertTab(0, "Test");
6259 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6261 // Set a numeric value to A1.
6262 m_pDoc->SetValue(ScAddress(0,0,0), 11.0);
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.
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);
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)));
6280 m_pDoc->DeleteTab(0);
6283 void Test::testExternalRef()
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);
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);
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");
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);
6329 // Sheet 2 remains empty.
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);
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);
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);
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);
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");
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)
6382 test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
6383 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
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");
6392 double pChecks[] = { 10, 11, 12, 13, 0 };
6393 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6395 m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
6396 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected cell value.", pChecks[i], val);
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");
6405 const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
6406 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6408 test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
6409 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
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");
6418 const char* pChecks[] = { "Value", "99", "98", "97" };
6419 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6421 test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
6422 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
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);
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());
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);
6448 // Unload the external document shell.
6449 xExtDocSh->DoClose();
6450 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6451 !findLoadedDocShellByName(aExtDocName));
6453 m_pDoc->DeleteTab(0);
6456 void Test::testExternalRangeName()
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);
6467 ScDocument& rExtDoc = xExtDocSh->GetDocument();
6468 rExtDoc.InsertTab(0, aExtSh1Name);
6469 rExtDoc.SetValue(0, 0, 0, 123.456);
6471 ScRangeName* pRangeName = rExtDoc.GetRangeName();
6472 ScRangeData* pRangeData = new ScRangeData(&rExtDoc, "ExternalName",
6473 "$Data1.$A$1");
6474 pRangeName->insert(pRangeData);
6476 m_pDoc->InsertTab(0, "Test Sheet");
6477 m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#ExternalName");
6479 double nVal = m_pDoc->GetValue(0, 1, 0);
6480 ASSERT_DOUBLES_EQUAL(123.456, nVal);
6482 xExtDocSh->DoClose();
6483 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6484 !findLoadedDocShellByName(aExtDocName));
6485 m_pDoc->DeleteTab(0);
6488 static void testExtRefFuncT(ScDocument* pDoc, ScDocument& rExtDoc)
6490 Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6491 Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
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();
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());
6509 static void testExtRefFuncOFFSET(ScDocument* pDoc, ScDocument& rExtDoc)
6511 Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6512 Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
6514 sc::AutoCalcSwitch aACSwitch(*pDoc, true);
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)));
6522 static void testExtRefFuncVLOOKUP(ScDocument* pDoc, ScDocument& rExtDoc)
6524 Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6525 Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
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");
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");
6540 // Put formula in the source document.
6542 pDoc->SetString(ScAddress(0,0,0), "A2");
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)));
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)));
6553 static void testExtRefConcat(ScDocument* pDoc, ScDocument& rExtDoc)
6555 Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
6556 Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
6558 sc::AutoCalcSwitch aACSwitch(*pDoc, true);
6560 // String and number
6561 rExtDoc.SetString(ScAddress(0,0,0), "Answer: ");
6562 rExtDoc.SetValue(ScAddress(0,1,0), 42);
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)));
6569 void Test::testExternalRefFunctions()
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);
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);
6586 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
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);
6604 m_pDoc->InsertTab(0, "Test");
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 }
6620 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
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);
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));
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);
6648 pRefMgr->clearCache(nFileId);
6649 testExtRefFuncT(m_pDoc, rExtDoc);
6650 testExtRefFuncOFFSET(m_pDoc, rExtDoc);
6651 testExtRefFuncVLOOKUP(m_pDoc, rExtDoc);
6652 testExtRefConcat(m_pDoc, rExtDoc);
6654 // Unload the external document shell.
6655 xExtDocSh->DoClose();
6656 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6657 !findLoadedDocShellByName(aExtDocName));
6659 m_pDoc->DeleteTab(0);
6662 void Test::testExternalRefUnresolved()
6664 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6665 m_pDoc->InsertTab(0, "Test");
6667 // Test error propagation of unresolved (not existing document) external
6668 // references. Well, let's hope no build machine has such file with sheet...
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\")" },
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);
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
6736 bool bSuccess = checkOutput(m_pDoc, aRange, aOutputCheck, "Check unresolved external reference.");
6737 CPPUNIT_ASSERT_MESSAGE("Unresolved reference check failed", bSuccess);
6739 m_pDoc->DeleteTab(0);
6742 void Test::testMatrixOp()
6744 m_pDoc->InsertTab(0, "Test");
6746 for (SCROW nRow = 0; nRow < 4; ++nRow)
6748 m_pDoc->SetValue(0, nRow, 0, nRow);
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)");
6756 double nVal = m_pDoc->GetValue(2, 0, 0);
6757 CPPUNIT_ASSERT_EQUAL(16.0, nVal);
6759 nVal = m_pDoc->GetValue(2, 1, 0);
6760 CPPUNIT_ASSERT_EQUAL(4.0, nVal);
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)
6773 CPPUNIT_ASSERT_EQUAL( fResult[i], m_pDoc->GetValue(4, i, 0));
6776 m_pDoc->DeleteTab(0);
6779 void Test::testFuncRangeOp()
6781 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6783 m_pDoc->InsertTab(0, "Sheet1");
6784 m_pDoc->InsertTab(1, "Sheet2");
6785 m_pDoc->InsertTab(2, "Sheet3");
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);
6800 // Range operator should extend concatenated literal references during
6801 // parse time already, so with this we can test ScComplexRefData::Extend()
6803 // Current sheet is Sheet1, so B1:B2 implies relative Sheet1.B1:B2
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
6858 m_pDoc->DeleteTab(2);
6859 m_pDoc->DeleteTab(1);
6860 m_pDoc->DeleteTab(0);
6863 void Test::testFuncFORMULA()
6865 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6867 m_pDoc->InsertTab(0, "Sheet1");
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)" },
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);
6880 // Checks of C1:D3, where Cy==Dy, and D4:D6
6881 const char* aChecks[] = {
6882 "=A1",
6883 "#N/A",
6884 "=A3",
6886 for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
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));
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)
6898 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i+3,0));
6901 m_pDoc->DeleteTab(0);
6904 void Test::testFuncTableRef()
6906 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
6908 m_pDoc->InsertTab(0, "Sheet1");
6909 ScMarkData aMark(MAXROW, MAXCOL);
6910 aMark.SelectOneTable(0);
6911 ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
6914 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
6915 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
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);
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" }
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);
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!" }
6964 // Insert named expressions.
6965 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
6966 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
6968 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
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);
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)
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)));
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)
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)));
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)
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)));
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)
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)));
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);
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)
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)));
7047 const char* pColumn2Formula = "=SUM(table[[#Data];[Column2]])";
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 }
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);
7066 // Verify the formula result in B6 (64+128+256=448).
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)));
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");
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");
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");
7090 // === header-less ===
7093 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
7094 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
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);
7103 // Populate "hltable" database range with data in E10:F12
7104 const char* aData[][2] = {
7105 { "1", "2" },
7106 { "4", "8" },
7107 { "16", "32" }
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);
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!" }
7138 // Insert named expressions.
7139 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
7140 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
7142 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
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);
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)
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)));
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)
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)));
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)
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)));
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)
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)));
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);
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)
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)));
7221 const char* pColumn3Formula = "=SUM(hltable[[#Data];[Column3]])";
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 }
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);
7239 // Verify the formula result in F14 (64+128+256=448).
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)));
7247 m_pDoc->DeleteTab(0);
7250 void Test::testFuncFTEST()
7252 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7254 m_pDoc->InsertTab(0, "FTest");
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);
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);
7312 m_pDoc->DeleteTab(0);
7313 m_pDoc->InsertTab(0, "FTest2");
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 }
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.
7326 The minimum variance ratio obtained in this way is 0.017342 and the maximum variance ratio is 57.665039
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 };
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 }
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)
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)");
7372 for ( SCROW nRow = 0; nRow < 10; ++nRow ) // Set B1:B10 = SQRT(C2*19/20), and B11:B20 = -SQRT(C2*19/20)
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)");
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)");
7385 ScAddress aPosRev(4, 1, 0); // E2
7386 aPos.Set(4, 0, 0); // E1
7388 for ( size_t nFirstIdx = 0; nFirstIdx < nNumParams; ++nFirstIdx )
7390 m_pDoc->SetValue(3, 0, 0, fParameter[nFirstIdx]); // Set D1
7391 for ( size_t nSecondIdx = 0; nSecondIdx < nNumParams; ++nSecondIdx )
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) );
7400 m_pDoc->DeleteTab(0);
7403 void Test::testFuncFTESTBug()
7405 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7407 m_pDoc->InsertTab(0, "FTest");
7409 ScAddress aPos(9,0,0);
7410 m_pDoc->SetString(aPos, "=FTEST(H1:H3;I1:I3)");
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);
7420 m_pDoc->DeleteTab(0);
7423 void Test::testFuncCHITEST()
7425 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7427 m_pDoc->InsertTab(0, "ChiTest");
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);
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);
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);
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);
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);
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);
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);
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);
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);
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
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);
7542 m_pDoc->DeleteTab(0);
7545 void Test::testFuncTTEST()
7547 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7549 m_pDoc->InsertTab(0, "TTest");
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);
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);
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);
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);
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);
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);
7647 m_pDoc->DeleteTab(0);
7650 void Test::testFuncSUMX2PY2()
7652 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7654 m_pDoc->InsertTab(0, "SumX2PY2 Test");
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));
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));
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));
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);
7713 m_pDoc->DeleteTab(0);
7716 void Test::testFuncSUMX2MY2()
7718 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7720 m_pDoc->InsertTab(0, "SumX2MY2 Test");
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));
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));
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));
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);
7789 m_pDoc->DeleteTab(0);
7792 void Test::testFuncGCD()
7794 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7796 m_pDoc->InsertTab(0, "GCDTest");
7798 OUString aVal;
7799 ScAddress aPos(4,0,0);
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);
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));
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));
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);
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);
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);
7887 m_pDoc->DeleteTab(0);
7890 void Test::testFuncLCM()
7892 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7894 m_pDoc->InsertTab(0, "LCMTest");
7896 OUString aVal;
7897 ScAddress aPos(4,0,0);
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);
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));
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));
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);
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);
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);
7983 m_pDoc->DeleteTab(0);
7986 void Test::testFuncSUMSQ()
7988 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7990 m_pDoc->InsertTab(0, "SUMSQTest");
7992 ScAddress aPos(4,0,0);
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));
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));
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));
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));
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);
8058 m_pDoc->DeleteTab(0);
8061 void Test::testFuncMDETERM()
8063 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
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++ )
8072 double fVal = 1.0;
8073 // Generate a singular integer matrix
8074 for( SCROW nRow = 0; nRow < static_cast<SCROW>(nSize); nRow++ )
8076 for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(nSize); nCol++ )
8078 m_pDoc->SetValue(nCol, nRow, 0, fVal);
8079 fVal += 1.0;
8082 aFormulaBuffer[12] = aColCodes[nSize-1];
8083 aFormulaBuffer[13] = static_cast<sal_Unicode>( '0' + nSize );
8084 m_pDoc->SetString(aPos, aFormulaBuffer.toString());
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
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);
8117 void Test::testFormulaErrorPropagation()
8119 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8121 m_pDoc->InsertTab(0, "Sheet1");
8123 ScMarkData aMark(MAXROW, MAXCOL);
8124 aMark.SelectOneTable(0);
8125 ScAddress aPos, aPos2;
8126 const OUString aTRUE("TRUE");
8127 const OUString aFALSE("FALSE");
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();
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));
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));
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));
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));
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));
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));
8189 m_pDoc->DeleteTab(0);
8192 namespace {
8194 class ColumnTest
8196 ScDocument * m_pDoc;
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;
8204 public:
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)
8212 void operator() ( SCCOL nColumn, const OUString& rFormula,
8213 std::function<double(SCROW )> const & lExpected ) const
8215 ScDocument aClipDoc(SCDOCMODE_CLIP);
8216 ScMarkData aMark(MAXROW, MAXCOL);
8218 ScAddress aPos(nColumn, m_nStart1, 0);
8219 m_pDoc->SetString(aPos, rFormula);
8220 ASSERT_DOUBLES_EQUAL( lExpected(m_nStart1), m_pDoc->GetValue(aPos) );
8222 // Copy formula cell to clipboard.
8223 ScClipParam aClipParam(aPos, false);
8224 aMark.SetMarkArea(aPos);
8225 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
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);
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);
8238 // Check the formula results for passed column.
8239 for( SCROW i = 0; i < m_nTotalRows; ++i )
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)));
8251 void Test::testTdf97369()
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;
8260 const double SHIFT1 = 200;
8261 const double SHIFT2 = 400;
8263 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8264 m_pDoc->InsertTab (0, "tdf97369"));
8266 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8268 // set up columns A, B, C
8269 for( SCROW i = 0; i < TOTAL_ROWS; ++i )
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
8276 const ColumnTest columnTest( m_pDoc, TOTAL_ROWS, START1, END1, START2, END2 );
8278 auto lExpectedinD = [=] (SCROW n) {
8279 return 3.0 * (n-START1) + SHIFT1 + SHIFT2;
8281 columnTest(3, "=SUM(A1:C1)", lExpectedinD);
8283 auto lExpectedinE = [=] (SCROW ) {
8284 return SHIFT1 + SHIFT2;
8286 columnTest(4, "=SUM(A$1:C$1)", lExpectedinE);
8288 auto lExpectedinF = [=] (SCROW n) {
8289 return ((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2.0;
8291 columnTest(5, "=SUM(A1:A10)", lExpectedinF);
8293 auto lExpectedinG = [=] (SCROW n) {
8294 return ((n + 1) * n) / 2.0;
8296 columnTest(6, "=SUM(A$1:A10)", lExpectedinG);
8298 auto lExpectedinH = [=] (SCROW n) {
8299 return 3.0 * (((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8301 columnTest(7, "=SUM(A1:C10)", lExpectedinH);
8303 auto lExpectedinI = [=] (SCROW ) {
8304 return 3.0 * (((2*START1 + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8306 columnTest(8, "=SUM(A$1:C$10)", lExpectedinI);
8308 m_pDoc->DeleteTab(0);
8311 void Test::testTdf97587()
8313 const SCROW TOTAL_ROWS = 150;
8314 const SCROW ROW_RANGE = 10;
8316 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8317 m_pDoc->InsertTab (0, "tdf97587"));
8319 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8321 std::set<SCROW> emptyCells = {0, 100};
8322 for( SCROW i = 0; i < ROW_RANGE; ++i )
8324 emptyCells.insert(i + TOTAL_ROWS / 3);
8325 emptyCells.insert(i + TOTAL_ROWS);
8328 // set up columns A
8329 for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8331 if( emptyCells.find(i) != emptyCells.end() )
8332 continue;
8333 m_pDoc->SetValue(ScAddress(0, i, 0), 1.0);
8336 ScDocument aClipDoc(SCDOCMODE_CLIP);
8337 ScMarkData aMark(MAXROW, MAXCOL);
8339 ScAddress aPos(1, 0, 0);
8340 m_pDoc->SetString(aPos, "=SUM(A1:A10)");
8342 // Copy formula cell to clipboard.
8343 ScClipParam aClipParam(aPos, false);
8344 aMark.SetMarkArea(aPos);
8345 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
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);
8352 // Check the formula results in column B.
8353 for( SCROW i = 0; i < TOTAL_ROWS + 1; ++i )
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)));
8360 m_pDoc->DeleteTab(0);
8363 void Test::testMatConcat()
8365 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8367 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8369 for (SCROW nRow = 0; nRow < 10; ++nRow)
8371 m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8375 ScMarkData aMark(MAXROW, MAXCOL);
8376 aMark.SelectOneTable(0);
8377 m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J10");
8379 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8381 for (SCROW nRow = 12; nRow < 22; ++nRow)
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);
8388 { // Data in A12:B16
8389 const char* aData[][2] = {
8390 { "q", "w" },
8391 { "a", "" },
8392 { "", "x" },
8393 { "", "" },
8394 { "e", "r" },
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);
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);
8421 m_pDoc->DeleteTab(0);
8424 void Test::testMatConcatReplication()
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"));
8430 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8432 for (SCROW nRow = 0; nRow < 10; ++nRow)
8434 m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8438 ScMarkData aMark(MAXROW, MAXCOL);
8439 aMark.SelectOneTable(0);
8440 m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J1");
8442 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8444 for (SCROW nRow = 12; nRow < 22; ++nRow)
8446 OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8447 CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + "0"), aStr);
8451 m_pDoc->DeleteTab(0);
8454 void Test::testRefR1C1WholeCol()
8456 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
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);
8464 CPPUNIT_ASSERT_EQUAL(OUString("L:L"), aFormula);
8466 m_pDoc->DeleteTab(0);
8469 void Test::testRefR1C1WholeRow()
8471 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
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);
8479 CPPUNIT_ASSERT_EQUAL(OUString("5:5"), aFormula);
8481 m_pDoc->DeleteTab(0);
8484 void Test::testSingleCellCopyColumnLabel()
8486 ScDocOptions aOptions = m_pDoc->GetDocOptions();
8487 aOptions.SetLookUpColRowNames(true);
8488 m_pDoc->SetDocOptions(aOptions);
8489 m_pDoc->InsertTab(0, "Test");
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'");
8497 double nVal = m_pDoc->GetValue(1, 1, 0);
8498 ASSERT_DOUBLES_EQUAL(1.0, nVal);
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);
8506 m_pDoc->DeleteTab(0);
8509 // Significant whitespace operator intersection in Excel syntax, tdf#96426
8510 void Test::testIntersectionOpExcel()
8512 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
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);
8522 m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH_XL_A1);
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));
8538 m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH);
8540 m_pDoc->DeleteTab(0);
8543 //Test Subtotal and Aggregate during hide rows #tdf93171
8544 void Test::testFuncRowsHidden()
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
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));
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));
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));
8584 m_pDoc->DeleteTab(0);
8587 // Test COUNTIFS, SUMIFS, AVERAGEIFS in array context.
8588 void Test::testFuncSUMIFS()
8590 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8591 m_pDoc->InsertTab(0, "Test");
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" },
8608 insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
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)");
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" }
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);
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\")");
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" }
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);
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\")");
8658 // Result sums and averages.
8659 std::vector<std::vector<const char*>> aCheck = {
8660 { "0", "#DIV/0!" },
8661 { "2", "2" },
8662 { "3", "1.5" }
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);
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\")");
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" }
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);
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\")");
8699 // Result sums and averages.
8700 std::vector<std::vector<const char*>> aCheck = {
8701 { "0", "#DIV/0!" },
8702 { "2", "2" },
8703 { "2", "2" }
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);
8710 // Same, but swapped normal and array criteria.
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\")");
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" }
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);
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\")");
8743 // Result sums and averages.
8744 std::vector<std::vector<const char*>> aCheck = {
8745 { "0", "#DIV/0!" },
8746 { "2", "2" },
8747 { "2", "2" }
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);
8754 m_pDoc->DeleteTab(0);
8757 // Test SUBTOTAL with reference lists in array context.
8758 void Test::testFuncRefListArraySUBTOTAL()
8760 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8761 m_pDoc->InsertTab(0, "Test");
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
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));
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));
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));
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));
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);
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));
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));
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);
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));
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));
8873 m_pDoc->DeleteTab(0);
8876 // tdf#115493 jump commands return the matrix result instead of the reference
8877 // list array.
8878 void Test::testFuncJumpMatrixArrayIF()
8880 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8881 m_pDoc->InsertTab(0, "Test");
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);
8891 ScMarkData aMark(MAXROW, MAXCOL);
8892 aMark.SelectOneTable(0);
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)));
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)));
8903 m_pDoc->DeleteTab(0);
8906 // tdf#123477 OFFSET() returns the matrix result instead of the reference list
8907 // array if result is not used as ReferenceOrRefArray.
8908 void Test::testFuncJumpMatrixArrayOFFSET()
8910 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8911 m_pDoc->InsertTab(0, "Test");
8913 std::vector<std::vector<const char*>> aData = {
8914 { "abc" },
8915 { "bcd" },
8916 { "cde" }
8918 insertRangeData(m_pDoc, ScAddress(0,0,0), aData); // A1:A3
8920 ScMarkData aMark(MAXROW, MAXCOL);
8921 aMark.SelectOneTable(0);
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)));
8930 m_pDoc->DeleteTab(0);
8933 // Test iterations with circular chain of references.
8934 void Test::testIterations()
8936 ScDocOptions aDocOpts = m_pDoc->GetDocOptions();
8937 aDocOpts.SetIter( true );
8938 m_pDoc->SetDocOptions( aDocOpts );
8940 m_pDoc->InsertTab(0, "Test");
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();
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();
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 );
8954 // Modify the formula
8955 m_pDoc->SetString( 0, 2, 0, "=COS(A2)+0.001" ); // A3
8956 m_pDoc->CalcAll();
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 );
8961 m_pDoc->DeleteTab(0);
8963 aDocOpts.SetIter( false );
8964 m_pDoc->SetDocOptions( aDocOpts );
8967 // tdf#111428 CellStoreEvent and its counter used for quick "has a column
8968 // formula cells" must point to the correct column.
8969 void Test::testInsertColCellStoreEventSwap()
8971 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8972 m_pDoc->InsertTab(0, "Test");
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));
8992 m_pDoc->DeleteTab(0);
8995 void Test::testFormulaAfterDeleteRows()
8997 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8998 m_pDoc->InsertTab(0, "Test");
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)");
9006 // Delete rows 2:69998
9007 m_pDoc->DeleteRow(ScRange(0, 1, 0, m_pDoc->MaxCol(), 69997, 0));
9009 const ScAddress aPos(0, 3, 0); // A4
9010 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A1:A3)", "Wrong formula in A4.");
9012 ASSERT_DOUBLES_EQUAL_MESSAGE("Wrong value at A4", 3.0, m_pDoc->GetValue(aPos));
9015 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */