Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / qa / unit / ucalc_formula.cxx
blob153096d6a434a4e2b90e47ebc4fc8c9207eb10e9
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 "helper/debughelper.hxx"
11 #include "helper/qahelper.hxx"
12 #include <scopetools.hxx>
13 #include <formulacell.hxx>
14 #include <docfunc.hxx>
15 #include <inputopt.hxx>
16 #include <tokenstringcontext.hxx>
17 #include <refupdatecontext.hxx>
18 #include <dbdata.hxx>
19 #include <validat.hxx>
20 #include <scitems.hxx>
21 #include <docpool.hxx>
22 #include <scmod.hxx>
23 #include <undomanager.hxx>
25 #include <formula/vectortoken.hxx>
26 #include <svl/intitem.hxx>
28 #include <memory>
29 #include <algorithm>
30 #include <vector>
32 using namespace formula;
33 using ::std::cerr;
34 using ::std::endl;
36 namespace {
37 void setExpandRefs(bool bExpand)
39 ScModule* pMod = SC_MOD();
40 ScInputOptions aOpt = pMod->GetInputOptions();
41 aOpt.SetExpandRefs(bExpand);
42 pMod->SetInputOptions(aOpt);
45 void testFormulaRefUpdateNameCopySheetCheckTab( const ScDocument* pDoc, SCTAB nTab, bool bCheckNames )
47 if (bCheckNames)
49 const ScRangeData* pName;
50 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL");
51 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL should exist", pName);
52 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL");
53 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL should exist", pName);
54 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_GLOBAL");
55 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_GLOBAL should exist", pName);
56 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_LOCAL");
57 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_LOCAL should exist", pName);
58 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED");
59 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED should exist", pName);
60 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED_NOREF");
61 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED_NOREF should not exist", !pName);
62 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_GLOBAL");
63 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_GLOBAL should exist", pName);
64 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_LOCAL");
65 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_LOCAL should exist", pName);
66 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED");
67 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED should exist", pName);
68 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED_NOREF");
69 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED_NOREF should exist", pName);
72 ScAddress aPos(0,0,0);
73 aPos.SetRow(0);
74 aPos.SetTab(nTab);
75 int nSheet = nTab + 1;
76 CPPUNIT_ASSERT_EQUAL( 1.0 * nSheet, pDoc->GetValue(aPos));
77 aPos.IncRow();
78 CPPUNIT_ASSERT_EQUAL( 11.0 * nSheet, pDoc->GetValue(aPos));
79 aPos.IncRow();
80 CPPUNIT_ASSERT_EQUAL( 100.0 * nSheet, pDoc->GetValue(aPos));
81 aPos.IncRow();
82 CPPUNIT_ASSERT_EQUAL( 11000.0 * nSheet, pDoc->GetValue(aPos));
83 aPos.IncRow();
84 CPPUNIT_ASSERT_EQUAL( 10000.0 * nSheet, pDoc->GetValue(aPos));
85 aPos.IncRow();
86 CPPUNIT_ASSERT_EQUAL( 1100000.0 * nSheet, pDoc->GetValue(aPos));
92 class TestFormula : public ScUcalcTestBase
96 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCreateStringFromTokens)
98 // Insert sheets.
99 m_pDoc->InsertTab(0, "Test");
100 m_pDoc->InsertTab(1, "Kevin's Data");
101 m_pDoc->InsertTab(2, "Past Data");
102 m_pDoc->InsertTab(3, "2013");
104 // Insert named ranges.
105 static const struct {
106 bool bGlobal;
107 const char* pName;
108 const char* pExpr;
109 } aNames[] = {
110 { true, "x", "Test.H1" },
111 { true, "y", "Test.H2" },
112 { true, "z", "Test.H3" },
114 { false, "sheetx", "Test.J1" }
117 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
118 ScRangeName* pSheetNames = m_pDoc->GetRangeName(0);
119 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
120 CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames);
122 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
124 ScRangeData* pName = new ScRangeData(
125 *m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
126 ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
128 if (aNames[i].bGlobal)
130 bool bInserted = pGlobalNames->insert(pName);
131 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
133 else
135 bool bInserted = pSheetNames->insert(pName);
136 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
140 // Insert DB ranges.
141 static const struct {
142 const char* pName;
143 SCTAB nTab;
144 SCCOL nCol1;
145 SCROW nRow1;
146 SCCOL nCol2;
147 SCROW nRow2;
148 } aDBs[] = {
149 { "Table1", 0, 0, 0, 10, 10 },
150 { "Table2", 1, 0, 0, 10, 10 },
151 { "Table3", 2, 0, 0, 10, 10 }
154 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
155 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
157 for (size_t i = 0; i < SAL_N_ELEMENTS(aDBs); ++i)
159 std::unique_ptr<ScDBData> pData( new ScDBData(
160 OUString::createFromAscii(
161 aDBs[i].pName), aDBs[i].nTab, aDBs[i].nCol1, aDBs[i].nRow1, aDBs[i].nCol2,aDBs[i].nRow2) );
162 bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
163 CPPUNIT_ASSERT_MESSAGE(
164 OString(
165 OString::Concat("Failed to insert \"") + aDBs[i].pName + "\"").getStr(),
166 bInserted);
169 const char* aTests[] = {
170 "1+2",
171 "SUM(A1:A10;B1:B10;C5;D6)",
172 "IF(Test.B10<>10;\"Good\";\"Bad\")",
173 "AVERAGE('2013'.B10:C20)",
174 "'Kevin''s Data'.B10",
175 "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
176 "x+y*z", // named ranges
177 "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
178 "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
179 "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
180 "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
182 (void) aTests;
184 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
186 // Artificially add external reference data after the context object is
187 // initialized.
188 aCxt.maExternalFileNames.emplace_back("file:///path/to/fake.file");
189 std::vector<OUString> aExtTabNames;
190 aExtTabNames.emplace_back("Sheet");
191 aCxt.maExternalCachedTabNames.emplace(0, aExtTabNames);
193 ScAddress aPos(0,0,0);
195 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
197 #if 0
198 OUString aFormula = OUString::createFromAscii(aTests[i]);
199 #endif
200 ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH);
201 #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
202 pArray.reset(aComp.CompileString(aFormula));
203 CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray.get());
205 OUString aCheck = pArray->CreateString(aCxt, aPos);
206 CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
207 #endif
210 m_pDoc->DeleteTab(3);
211 m_pDoc->DeleteTab(2);
212 m_pDoc->DeleteTab(1);
213 m_pDoc->DeleteTab(0);
216 namespace {
218 bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
220 if (rArray.mpStringArray)
222 if (rArray.mpStringArray[nPos])
223 return false;
226 if (rArray.mpNumericArray)
227 return std::isnan(rArray.mpNumericArray[nPos]);
228 else
229 return true;
232 bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
234 if (rArray.mpStringArray && rArray.mpStringArray[nPos])
235 // This is a string cell.
236 return false;
238 return rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal;
241 bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
243 if (!rArray.mpStringArray)
244 return false;
246 bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
247 if (!bEquals)
249 cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
250 cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
252 return bEquals;
257 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaParseReference)
259 OUString aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
260 m_pDoc->InsertTab(0, "Dummy"); // just to shift the sheet indices...
261 m_pDoc->InsertTab(1, aTab1); // name with a single quote.
262 m_pDoc->InsertTab(2, aTab2); // name with 2 single quotes.
263 m_pDoc->InsertTab(3, aTab3); // name without single quotes.
264 m_pDoc->InsertTab(4, aTab4); // name that doesn't require to be quoted.
266 OUString aTabName;
267 m_pDoc->GetName(1, aTabName);
268 CPPUNIT_ASSERT_EQUAL(aTab1, aTabName);
269 m_pDoc->GetName(2, aTabName);
270 CPPUNIT_ASSERT_EQUAL(aTab2, aTabName);
271 m_pDoc->GetName(3, aTabName);
272 CPPUNIT_ASSERT_EQUAL(aTab3, aTabName);
273 m_pDoc->GetName(4, aTabName);
274 CPPUNIT_ASSERT_EQUAL(aTab4, aTabName);
276 // Make sure the formula input and output match.
278 const char* aChecks[] = {
279 "='90''s Music'.B12",
280 "='90''s and 70''s'.$AB$100",
281 "='All Others'.Z$100",
282 "=NoQuote.$C111"
285 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
287 // Use the 'Dummy' sheet for this.
288 m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(aChecks[i]));
289 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString::createFromAscii(aChecks[i]), m_pDoc->GetFormula(0,0,0));
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(m_pDoc->MaxRow() == MAXROW ? OUString("B1:B1048576")
421 : OUString("B1:B16777216"),
422 *m_pDoc, formula::FormulaGrammar::CONV_OOO);
423 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
424 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
425 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
426 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
427 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
428 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
429 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
430 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
431 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
432 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
433 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
434 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
435 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
436 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
437 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
439 // Both columns at sheet bounds and relative => convert to absolute => entire row reference.
440 aRange.aStart.SetTab(0);
441 nRes = aRange.Parse("A2:" + m_pDoc->MaxColAsString() + "2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
442 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
443 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
444 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
445 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
446 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
447 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
448 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
449 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
450 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
451 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
452 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
453 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
454 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
455 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
456 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
458 // Check for reference input conversion to and display string of entire column/row.
460 const char* row1048576Checks[][2] = {
461 { "=B:B", "=B:B" },
462 { "=B1:B1048576", "=B:B" },
463 { "=B1:B$1048576", "=B1:B$1048576" },
464 { "=B$1:B1048576", "=B$1:B1048576" },
465 { "=B$1:B$1048576", "=B:B" }
467 const char* row16777216Checks[][2] = {
468 { "=B:B", "=B:B" },
469 { "=B1:B16777216", "=B:B" },
470 { "=B1:B$16777216", "=B1:B$16777216" },
471 { "=B$1:B16777216", "=B$1:B16777216" },
472 { "=B$1:B$16777216", "=B:B" }
474 const char* col1024Checks[][2] = {
475 { "=2:2", "=2:2" },
476 { "=A2:AMJ2", "=2:2" },
477 { "=A2:$AMJ2", "=A2:$AMJ2" },
478 { "=$A2:AMJ2", "=$A2:AMJ2" },
479 { "=$A2:$AMJ2", "=2:2" }
481 const char* col16384Checks[][2] = {
482 { "=2:2", "=2:2" },
483 { "=A2:XFD2", "=2:2" },
484 { "=A2:$XFD2", "=A2:$XFD2" },
485 { "=$A2:XFD2", "=$A2:XFD2" },
486 { "=$A2:$XFD2", "=2:2" }
489 if (m_pDoc->MaxRow() == 1048575)
491 for (const auto& check : row1048576Checks)
493 // Use the 'Dummy' sheet for this.
494 m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(check[0]));
495 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString::createFromAscii(check[1]), m_pDoc->GetFormula(0,0,0));
498 else
500 CPPUNIT_ASSERT_EQUAL(SCROW(16777215), m_pDoc->MaxRow());
501 for (const auto& check : row16777216Checks)
503 m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(check[0]));
504 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString::createFromAscii(check[1]), m_pDoc->GetFormula(0,0,0));
507 if (m_pDoc->MaxCol() == 1023)
509 for (const auto& check : col1024Checks)
511 m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(check[0]));
512 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString::createFromAscii(check[1]), m_pDoc->GetFormula(0,0,0));
515 else
517 CPPUNIT_ASSERT_EQUAL(SCCOL(16383), m_pDoc->MaxCol());
518 for (const auto& check : col16384Checks)
520 m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(check[0]));
521 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString::createFromAscii(check[1]), m_pDoc->GetFormula(0,0,0));
526 m_pDoc->DeleteTab(4);
527 m_pDoc->DeleteTab(3);
528 m_pDoc->DeleteTab(2);
529 m_pDoc->DeleteTab(1);
530 m_pDoc->DeleteTab(0);
533 CPPUNIT_TEST_FIXTURE(TestFormula, testFetchVectorRefArray)
535 m_pDoc->InsertTab(0, "Test");
537 // All numeric cells in Column A.
538 m_pDoc->SetValue(ScAddress(0,0,0), 1);
539 m_pDoc->SetValue(ScAddress(0,1,0), 2);
540 m_pDoc->SetValue(ScAddress(0,2,0), 3);
541 m_pDoc->SetValue(ScAddress(0,3,0), 4);
543 formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 4);
544 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
545 CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
546 CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
547 CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
548 CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
549 CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
551 aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 5);
552 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
553 CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
554 CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
555 CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
556 CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
557 CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
558 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
560 // All string cells in Column B. Note that the fetched string arrays are
561 // only to be compared case-insensitively. Right now, we use upper cased
562 // strings to achieve case-insensitive-ness, but that may change. So,
563 // don't count on that.
564 m_pDoc->SetString(ScAddress(1,0,0), "Andy");
565 m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
566 m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
567 m_pDoc->SetString(ScAddress(1,3,0), "David");
568 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 5);
569 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
570 CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
571 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Andy"));
572 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Bruce"));
573 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "Charlie"));
574 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "David"));
575 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
577 // Mixture of numeric, string, and empty cells in Column C.
578 m_pDoc->SetString(ScAddress(2,0,0), "Header");
579 m_pDoc->SetValue(ScAddress(2,1,0), 11);
580 m_pDoc->SetValue(ScAddress(2,2,0), 12);
581 m_pDoc->SetValue(ScAddress(2,3,0), 13);
582 m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
583 m_pDoc->CalcAll();
585 aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,0,0), 7);
586 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
587 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
588 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
589 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Header"));
590 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 11));
591 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 12));
592 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 13));
593 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
594 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 36));
595 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
597 // Mixed type again in Column D, but it starts with a numeric cell.
598 m_pDoc->SetValue(ScAddress(3,0,0), 10);
599 m_pDoc->SetString(ScAddress(3,1,0), "Below 10");
600 // Leave 2 empty cells.
601 m_pDoc->SetValue(ScAddress(3,4,0), 11);
602 m_pDoc->SetString(ScAddress(3,5,0), "=12");
603 m_pDoc->SetString(ScAddress(3,6,0), "=13");
604 m_pDoc->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
605 m_pDoc->CalcAll();
607 aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,0,0), 8);
608 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
609 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
610 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
611 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 10));
612 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Below 10"));
613 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 2));
614 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
615 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 11));
616 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 12));
617 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13));
618 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC"));
620 // Column E consists of formula cells whose results are all numeric.
621 for (SCROW i = 0; i <= 6; ++i)
622 m_pDoc->SetString(ScAddress(4,i,0), "=ROW()");
623 m_pDoc->CalcAll();
625 // Leave row 7 empty.
626 m_pDoc->SetString(ScAddress(4,8,0), "Andy");
627 m_pDoc->SetValue(ScAddress(4,9,0), 123);
629 // This array fits within a single formula block.
630 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 5);
631 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
632 CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray);
633 CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", !aArray.mpStringArray);
634 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
635 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
636 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
637 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
638 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
640 // This array spans over multiple blocks.
641 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 11);
642 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
643 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
644 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
645 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
646 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
647 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
648 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
649 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
650 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6));
651 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7));
652 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
653 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy"));
654 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123));
655 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10));
657 // Hit the cache but at a different start row.
658 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 3);
659 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
660 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
661 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3));
662 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4));
663 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5));
665 // Column F begins with empty rows at the top.
666 m_pDoc->SetValue(ScAddress(5,2,0), 1.1);
667 m_pDoc->SetValue(ScAddress(5,3,0), 1.2);
668 m_pDoc->SetString(ScAddress(5,4,0), "=2*8");
669 m_pDoc->CalcAll();
671 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 4);
672 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
673 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
674 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1));
675 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2));
676 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16));
677 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
679 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 3);
680 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
681 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
682 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
683 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
684 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
686 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 10);
687 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
688 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
689 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
690 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
691 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
692 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2));
693 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16));
694 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5));
695 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
696 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
697 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8));
698 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9));
700 // Get the array for F3:F4. This array should only consist of numeric array.
701 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 3);
702 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
703 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
704 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
706 // Column G consists only of strings.
707 m_pDoc->SetString(ScAddress(6,0,0), "Title");
708 m_pDoc->SetString(ScAddress(6,1,0), "foo");
709 m_pDoc->SetString(ScAddress(6,2,0), "bar");
710 m_pDoc->SetString(ScAddress(6,3,0), "foo");
711 m_pDoc->SetString(ScAddress(6,4,0), "baz");
712 m_pDoc->SetString(ScAddress(6,5,0), "quack");
713 m_pDoc->SetString(ScAddress(6,6,0), "beep");
714 m_pDoc->SetString(ScAddress(6,7,0), "kerker");
716 aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
717 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
718 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
719 CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
720 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "foo"));
721 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "bar"));
722 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "foo"));
723 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "baz"));
725 aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
726 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
727 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
728 CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
729 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "bar"));
730 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "foo"));
731 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "baz"));
732 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "quack"));
734 // Column H starts with formula cells.
735 for (SCROW i = 0; i < 10; ++i)
736 m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
738 m_pDoc->CalcAll();
739 aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
740 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
741 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
742 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
743 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 4.0));
744 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, 5.0));
745 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, 6.0));
747 aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
748 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
749 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
750 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
751 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 5.0));
753 // Clear everything and start over.
754 clearRange(m_pDoc, ScRange(0,0,0,m_pDoc->MaxCol(),m_pDoc->MaxRow(),0));
755 m_pDoc->PrepareFormulaCalc();
757 // Totally empty range in a totally empty column (Column A).
758 aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
759 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
760 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
761 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
762 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
763 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
765 // Totally empty range in a non-empty column (Column B).
766 m_pDoc->SetString(ScAddress(1,10,0), "Some text"); // B11
767 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
768 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
769 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
770 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
771 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
772 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
774 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
775 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
776 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
777 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
778 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
779 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
781 // These values come from a cache because of the call above.
782 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
783 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
784 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
785 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
786 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
787 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
789 // The column begins with a string header at row 1 (Column C).
790 m_pDoc->SetString(ScAddress(2,0,0), "MyHeader");
791 for (SCROW i = 1; i <= 9; ++i) // rows 2-10 are numeric.
792 m_pDoc->SetValue(ScAddress(2,i,0), i);
794 aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,1,0), 9); // C2:C10
795 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
796 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
797 for (size_t i = 0; i < 9; ++i)
798 CPPUNIT_ASSERT_EQUAL(double(i+1), aArray.mpNumericArray[i]);
800 // The column begins with a number, followed by a string then followed by
801 // a block of numbers (Column D).
802 m_pDoc->SetValue(ScAddress(3,0,0), 0.0);
803 m_pDoc->SetString(ScAddress(3,1,0), "Some string");
804 for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
805 m_pDoc->SetValue(ScAddress(3,i,0), i);
807 aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,2,0), 8); // D3:D10
808 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
809 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
810 for (size_t i = 0; i < 8; ++i)
811 CPPUNIT_ASSERT_EQUAL(double(i+2), aArray.mpNumericArray[i]);
813 // The column begins with a formula, followed by a string then followed by
814 // a block of numbers (Column E).
815 m_pDoc->SetString(ScAddress(4,0,0), "=1*2");
816 m_pDoc->SetString(ScAddress(4,1,0), "Some string");
817 for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
818 m_pDoc->SetValue(ScAddress(4,i,0), i*2);
820 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 8); // E3:E10
821 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
822 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
823 for (size_t i = 0; i < 8; ++i)
824 CPPUNIT_ASSERT_EQUAL(double((i+2)*2), aArray.mpNumericArray[i]);
826 m_pDoc->DeleteTab(0);
829 CPPUNIT_TEST_FIXTURE(TestFormula, testGroupConverter3D)
831 m_pDoc->InsertTab(0, "Test");
832 m_pDoc->InsertTab(1, "Test2");
834 m_pDoc->SetValue(1, 0, 0, 1.0);
835 m_pDoc->SetValue(1, 0, 1, 2.0);
837 for (SCROW nRow = 0; nRow < 200; ++nRow)
839 OUString aFormula = "=SUM(Test.B" + OUString::number(nRow+1) + ":Test2.B" + OUString::number(nRow+1) + ")";
840 m_pDoc->SetString(0, nRow, 0, aFormula);
843 double nVal = m_pDoc->GetValue(0, 0, 0);
844 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
846 m_pDoc->DeleteTab(1);
847 m_pDoc->DeleteTab(0);
850 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaTokenEquality)
852 struct FormulaTokenEqualityTest
854 const char* mpFormula1;
855 const char* mpFormula2;
856 bool mbEqual;
859 static const FormulaTokenEqualityTest aTests[] = {
860 { "R1C2", "R1C2", true },
861 { "R1C2", "R1C3", false },
862 { "R1C2", "R2C2", false },
863 { "RC2", "RC[1]", false },
864 { "R1C2:R10C2", "R1C2:R10C2", true },
865 { "R1C2:R10C2", "R1C2:R11C2", false },
866 { "1", "2", false },
867 { "RC[1]+1.2", "RC[1]+1.2", true },
868 { "RC[1]*0.2", "RC[1]*0.5", false },
869 { "\"Test1\"", "\"Test2\"", false },
870 { "\"Test\"", "\"Test\"", true },
871 { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
872 { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
875 formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
876 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
878 ScFormulaCell aCell1(*m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
879 ScFormulaCell aCell2(*m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
881 ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
882 if (aTests[i].mbEqual)
884 if (eComp == ScFormulaCell::NotEqual)
886 std::ostringstream os;
887 os << "These two formulas should be evaluated equal: '"
888 << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
889 CPPUNIT_FAIL(os.str());
892 else
894 if (eComp != ScFormulaCell::NotEqual)
896 std::ostringstream os;
897 os << "These two formulas should be evaluated non-equal: '"
898 << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
899 CPPUNIT_FAIL(os.str());
905 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefData)
907 std::unique_ptr<ScDocument> pDoc = std::make_unique<ScDocument>();
909 ScAddress aAddr(4,5,3), aPos(2,2,2);
910 ScSingleRefData aRef;
911 aRef.InitAddress(aAddr);
912 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsRowRel());
913 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsColRel());
914 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsTabRel());
915 CPPUNIT_ASSERT_EQUAL(SCCOL(4), aRef.Col());
916 CPPUNIT_ASSERT_EQUAL(SCROW(5), aRef.Row());
917 CPPUNIT_ASSERT_EQUAL(SCTAB(3), aRef.Tab());
919 aRef.SetRowRel(true);
920 aRef.SetColRel(true);
921 aRef.SetTabRel(true);
922 aRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos);
923 CPPUNIT_ASSERT_EQUAL(SCCOL(2), aRef.Col());
924 CPPUNIT_ASSERT_EQUAL(SCROW(3), aRef.Row());
925 CPPUNIT_ASSERT_EQUAL(SCTAB(1), aRef.Tab());
927 // Test extension of range reference.
929 ScComplexRefData aDoubleRef;
930 aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
932 aRef.InitAddress(ScAddress(6,5,0));
934 aDoubleRef.Extend(pDoc->GetSheetLimits(), aRef, ScAddress());
935 ScRange aTest = aDoubleRef.toAbs(*pDoc, ScAddress());
936 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(2,2,0), aTest.aStart);
937 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(6,5,0), aTest.aEnd);
939 ScComplexRefData aDoubleRef2;
940 aDoubleRef2.InitRangeRel(*pDoc, ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
941 aDoubleRef.Extend(pDoc->GetSheetLimits(), aDoubleRef2, ScAddress(5,5,0));
942 aTest = aDoubleRef.toAbs(*pDoc, ScAddress(5,5,0));
944 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(1,2,0), aTest.aStart);
945 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(8,6,0), aTest.aEnd);
948 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompiler)
950 static const struct {
951 const char* pInput; FormulaGrammar::Grammar eInputGram;
952 const char* pOutput; FormulaGrammar::Grammar eOutputGram;
953 } aTests[] = {
954 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF },
955 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE },
956 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1 },
957 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1 },
960 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
962 std::unique_ptr<ScTokenArray> pArray = compileFormula(m_pDoc, OUString::createFromAscii(aTests[i].pInput), aTests[i].eInputGram);
963 CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray);
965 ScCompiler aComp(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
966 OUStringBuffer aBuf;
967 aComp.CreateStringFromTokenArray(aBuf);
968 OUString aFormula = aBuf.makeStringAndClear();
970 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
974 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompilerJumpReordering)
976 struct TokenCheck
978 OpCode meOp;
979 StackVar meType;
983 // Compile formula string first.
984 std::unique_ptr<ScTokenArray> pCode(compileFormula(m_pDoc, "=IF(B1;12;\"text\")"));
985 CPPUNIT_ASSERT(pCode);
987 // Then generate RPN tokens.
988 ScCompiler aCompRPN(*m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
989 aCompRPN.CompileTokenArray();
991 // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
992 static const TokenCheck aCheckRPN[] =
994 { ocPush, svSingleRef },
995 { ocIf, svUnknown }, // type is context dependent, don't test it
996 { ocPush, svDouble },
997 { ocSep, svSep },
998 { ocPush, svString },
999 { ocClose, svSep },
1002 sal_uInt16 nLen = pCode->GetCodeLen();
1003 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN)), nLen);
1005 FormulaToken** ppTokens = pCode->GetCode();
1006 for (sal_uInt16 i = 0; i < nLen; ++i)
1008 const FormulaToken* p = ppTokens[i];
1009 CPPUNIT_ASSERT_EQUAL(aCheckRPN[i].meOp, p->GetOpCode());
1010 if (aCheckRPN[i].meOp != ocIf )
1011 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN[i].meType), static_cast<int>(p->GetType()));
1014 // Generate RPN tokens again, but this time no jump command reordering.
1015 pCode->DelRPN();
1016 ScCompiler aCompRPN2(*m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1017 aCompRPN2.EnableJumpCommandReorder(false);
1018 aCompRPN2.CompileTokenArray();
1020 static const TokenCheck aCheckRPN2[] =
1022 { ocPush, svSingleRef },
1023 { ocPush, svDouble },
1024 { ocPush, svString },
1025 { ocIf, svUnknown }, // type is context dependent, don't test it
1028 nLen = pCode->GetCodeLen();
1029 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN2)), nLen);
1030 ppTokens = pCode->GetCode();
1031 for (sal_uInt16 i = 0; i < nLen; ++i)
1033 const FormulaToken* p = ppTokens[i];
1034 CPPUNIT_ASSERT_EQUAL(aCheckRPN2[i].meOp, p->GetOpCode());
1035 if (aCheckRPN[i].meOp == ocPush)
1036 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN2[i].meType), static_cast<int>(p->GetType()));
1041 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompilerImplicitIntersection2Param)
1043 struct TestCaseFormula
1045 OUString aFormula;
1046 ScAddress aCellAddress;
1047 ScRange aSumRange;
1048 bool bStartColRel; // SumRange-StartCol
1049 bool bEndColRel; // SumRange-EndCol
1052 m_pDoc->InsertTab(0, "Formula");
1053 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1056 TestCaseFormula aTestCases[] =
1058 // Formula, FormulaCellAddress, SumRange with Implicit Intersection
1060 // Sumrange is single cell, address is abs
1062 OUString("=SUMIF($B$2:$B$10;F2;$D$5)"),
1063 ScAddress(7, 5, 0),
1064 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1065 false,
1066 false
1069 // Sumrange is single cell, address is relative
1071 OUString("=SUMIF($B$2:$B$10;F2;D5)"),
1072 ScAddress(7, 5, 0),
1073 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1074 true,
1075 true
1078 // Baserange(abs,abs), Sumrange(abs,abs)
1080 OUString("=SUMIF($B$2:$B$10;F2;$D$5:$D$10)"),
1081 ScAddress(7, 5, 0),
1082 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1083 false,
1084 false
1087 // Baserange(abs,rel), Sumrange(abs,abs)
1089 OUString("=SUMIF($B$2:B10;F2;$D$5:$D$10)"),
1090 ScAddress(7, 5, 0),
1091 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1092 false,
1093 false
1096 // Baserange(rel,abs), Sumrange(abs,abs)
1098 OUString("=SUMIF(B2:$B$10;F2;$D$5:$D$10)"),
1099 ScAddress(7, 5, 0),
1100 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1101 false,
1102 false
1105 // Baserange(rel,rel), Sumrange(abs,abs)
1107 OUString("=SUMIF(B2:B10;F2;$D$5:$D$10)"),
1108 ScAddress(7, 5, 0),
1109 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1110 false,
1111 false
1114 // Baserange(abs,abs), Sumrange(abs,rel)
1116 OUString("=SUMIF($B$2:$B$10;F2;$D$5:D10)"),
1117 ScAddress(7, 5, 0),
1118 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1119 false,
1120 true
1123 // Baserange(abs,abs), Sumrange(rel,abs)
1125 OUString("=SUMIF($B$2:$B$10;F2;D5:$D$10)"),
1126 ScAddress(7, 5, 0),
1127 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1128 true,
1129 false
1132 // Baserange(abs,abs), Sumrange(rel,rel)
1134 OUString("=SUMIF($B$2:$B$10;F2;D5:D10)"),
1135 ScAddress(7, 5, 0),
1136 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1137 true,
1138 true
1142 for (const auto& rCase : aTestCases)
1144 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1145 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1146 const ScTokenArray* pCode = pCell->GetCode();
1147 CPPUNIT_ASSERT(pCode);
1149 sal_uInt16 nLen = pCode->GetCodeLen();
1150 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(4), nLen);
1152 FormulaToken** ppTokens = pCode->GetCode();
1154 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(first argument to SUMIF)", svDoubleRef, ppTokens[0]->GetType());
1155 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(third argument to SUMIF)", svDoubleRef, ppTokens[2]->GetType());
1157 ScComplexRefData aSumRangeData = *ppTokens[2]->GetDoubleRef();
1158 ScRange aSumRange = aSumRangeData.toAbs(*m_pDoc, rCase.aCellAddress);
1159 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sum-range in RPN array", rCase.aSumRange, aSumRange);
1161 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for start column address in sum-range", rCase.bStartColRel, aSumRangeData.Ref1.IsColRel());
1162 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for end column address in sum-range", rCase.bEndColRel, aSumRangeData.Ref2.IsColRel());
1167 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompilerImplicitIntersection1ParamNoChange)
1169 struct TestCaseFormulaNoChange
1171 OUString aFormula;
1172 ScAddress aCellAddress;
1173 bool bMatrixFormula;
1174 bool bForcedArray;
1177 m_pDoc->InsertTab(0, "Formula");
1178 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1181 ScAddress aStartAddr(4, 5, 0);
1182 TestCaseFormulaNoChange aCasesNoChange[] =
1185 OUString("=COS(A$2:A$100)"), // No change because of abs col ref.
1186 aStartAddr,
1187 false,
1188 false
1191 OUString("=COS($A7:$A100)"), // No intersection
1192 aStartAddr,
1193 false,
1194 false
1197 OUString("=COS($A5:$C7)"), // No intersection 2-D range
1198 aStartAddr,
1199 false,
1200 false
1203 OUString("=SUMPRODUCT(COS(A6:A10))"), // COS() in forced array mode
1204 aStartAddr,
1205 false,
1206 true
1209 OUString("=COS(A6:A10)"), // Matrix formula
1210 aStartAddr,
1211 true,
1212 false
1216 for (const auto& rCase : aCasesNoChange)
1218 if (rCase.bMatrixFormula)
1220 ScMarkData aMark(m_pDoc->GetSheetLimits());
1221 aMark.SelectOneTable(0);
1222 SCCOL nColStart = rCase.aCellAddress.Col();
1223 SCROW nRowStart = rCase.aCellAddress.Row();
1224 m_pDoc->InsertMatrixFormula(nColStart, nRowStart, nColStart, nRowStart + 4,
1225 aMark, rCase.aFormula);
1227 else
1228 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1230 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1231 const ScTokenArray* pCode = pCell->GetCode();
1232 CPPUNIT_ASSERT(pCode);
1234 sal_uInt16 nRPNLen = pCode->GetCodeLen();
1235 sal_uInt16 nRawLen = pCode->GetLen();
1236 sal_uInt16 nRawArgPos;
1237 if (rCase.bForcedArray)
1239 nRawArgPos = 4;
1240 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(7), nRawLen);
1241 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(3), nRPNLen);
1243 else
1245 nRawArgPos = 2;
1246 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(4), nRawLen);
1247 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1250 FormulaToken** ppRawTokens = pCode->GetArray();
1251 FormulaToken** ppRPNTokens = pCode->GetCode();
1253 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of raw token(argument to COS)", svDoubleRef, ppRawTokens[nRawArgPos]->GetType());
1254 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svDoubleRef, ppRPNTokens[0]->GetType());
1256 ScComplexRefData aArgRangeRaw = *ppRawTokens[nRawArgPos]->GetDoubleRef();
1257 ScComplexRefData aArgRangeRPN = *ppRPNTokens[0]->GetDoubleRef();
1258 bool bRawMatchRPNToken(aArgRangeRaw == aArgRangeRPN);
1259 CPPUNIT_ASSERT_MESSAGE("raw arg token and RPN arg token contents do not match", bRawMatchRPNToken);
1264 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompilerImplicitIntersection1ParamWithChange)
1266 struct TestCaseFormula
1268 OUString aFormula;
1269 ScAddress aCellAddress;
1270 ScAddress aArgAddr;
1273 m_pDoc->InsertTab(0, "Formula");
1274 m_pDoc->InsertTab(1, "Formula1");
1275 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1278 ScAddress aStartAddr(10, 5, 0);
1279 TestCaseFormula aCasesWithChange[] =
1282 OUString("=COS($A6:$A100)"), // Corner case with intersection
1283 aStartAddr,
1284 ScAddress(0, 5, 0)
1287 OUString("=COS($A2:$A6)"), // Corner case with intersection
1288 aStartAddr,
1289 ScAddress(0, 5, 0)
1292 OUString("=COS($A2:$A100)"), // Typical 1D case
1293 aStartAddr,
1294 ScAddress(0, 5, 0)
1297 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1298 ScAddress(0, 0, 1), // Formula in sheet 1
1299 ScAddress(0, 0, 0)
1302 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1303 ScAddress(0, 2, 1), // Formula in sheet 1
1304 ScAddress(0, 2, 0)
1307 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1308 ScAddress(2, 0, 1), // Formula in sheet 1
1309 ScAddress(2, 0, 0)
1312 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1313 ScAddress(2, 2, 1), // Formula in sheet 1
1314 ScAddress(2, 2, 0)
1317 OUString("=COS($Formula.$A1:$C3)"), // Typical 2D case
1318 ScAddress(1, 1, 1), // Formula in sheet 1
1319 ScAddress(1, 1, 0)
1323 for (const auto& rCase : aCasesWithChange)
1325 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1327 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1328 const ScTokenArray* pCode = pCell->GetCode();
1329 CPPUNIT_ASSERT(pCode);
1331 sal_uInt16 nRPNLen = pCode->GetCodeLen();
1332 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1334 FormulaToken** ppRPNTokens = pCode->GetCode();
1336 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef, ppRPNTokens[0]->GetType());
1338 ScSingleRefData aArgAddrRPN = *ppRPNTokens[0]->GetSingleRef();
1339 ScAddress aArgAddrActual = aArgAddrRPN.toAbs(*m_pDoc, rCase.aCellAddress);
1340 CPPUNIT_ASSERT_EQUAL_MESSAGE("Computed implicit intersection singleref is wrong", rCase.aArgAddr, aArgAddrActual);
1345 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompilerImplicitIntersection1NoGroup)
1347 m_pDoc->InsertTab(0, "Formula");
1348 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1350 m_pDoc->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
1351 m_pDoc->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
1353 // Implicit intersection optimization in ScCompiler::HandleIIOpCode() internally changes
1354 // these to "=COS(A3)" and "=COS(A4)", but these shouldn't be merged into a formula group,
1355 // otherwise B4's formula would then be "=COS(A2:A6)".
1356 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in B3 has changed.", OUString("=COS(A1:A5)"), m_pDoc->GetFormula(1,2,0));
1357 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in B4 has changed.", OUString("=COS(A1:A5)"), m_pDoc->GetFormula(1,3,0));
1359 m_pDoc->DeleteTab(0);
1362 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaCompilerImplicitIntersectionOperators)
1364 struct TestCase
1366 OUString formula[3];
1367 double result[3];
1370 m_pDoc->InsertTab(0, "Test");
1371 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1373 m_pDoc->SetValue(2, 0, 0, 5); // C1
1374 m_pDoc->SetValue(2, 1, 0, 4); // C2
1375 m_pDoc->SetValue(2, 2, 0, 3); // C3
1376 m_pDoc->SetValue(3, 0, 0, 1); // D1
1377 m_pDoc->SetValue(3, 1, 0, 2); // D2
1378 m_pDoc->SetValue(3, 2, 0, 3); // D3
1380 TestCase tests[] =
1382 { OUString("=C:C/D:D"), OUString("=C:C/D:D"), OUString("=C:C/D:D"), 5, 2, 1 },
1383 { OUString("=C1:C2/D1:D2"), OUString("=C2:C3/D2:D3"), OUString("=C3:C4/D3:D4"), 5, 2, 1 }
1386 for (const TestCase& test : tests)
1388 for(int i = 0; i < 2; ++i )
1389 m_pDoc->SetString(ScAddress(4,i,0), test.formula[i]); // E1-3
1390 for(int i = 0; i < 2; ++i )
1391 CPPUNIT_ASSERT_EQUAL_MESSAGE(OUString( test.formula[i] + " result incorrect in row " + OUString::number(i+1)).toUtf8().getStr(),
1392 test.result[i], m_pDoc->GetValue(ScAddress(4,i,0)));
1395 m_pDoc->DeleteTab(0);
1398 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaAnnotateTrimOnDoubleRefs)
1400 m_pDoc->InsertTab(0, "Test");
1401 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1403 constexpr sal_Int32 nCols = 2;
1404 constexpr sal_Int32 nRows = 5;
1406 // Values in A1:B5
1407 constexpr sal_Int32 aMat[nRows][nCols] = {
1408 {4, 50},
1409 {5, 30},
1410 {4, 40},
1411 {0, 70},
1412 {5, 90}
1415 for (sal_Int32 nCol = 0; nCol < nCols; ++nCol)
1417 for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
1418 m_pDoc->SetValue(nCol, nRow, 0, aMat[nRow][nCol]);
1421 m_pDoc->SetValue(2, 0, 0, 4); // C1 = 4
1422 m_pDoc->SetValue(3, 0, 0, 5); // D1 = 5
1424 ScMarkData aMark(m_pDoc->GetSheetLimits());
1425 aMark.SelectOneTable(0);
1427 struct TestCase
1429 OUString aFormula;
1430 ScRange aTrimmableRange;
1431 double fResult;
1432 bool bMatrixFormula;
1435 constexpr sal_Int32 nTestCases = 5;
1436 TestCase aTestCases[nTestCases] = {
1438 "=SUM(IF($C$1=A:A;B:B)/10*D1)",
1439 ScRange(0, 0, 0, 0, 1048575, 0),
1440 45.0,
1441 true
1445 "=SUM(IF(A:A=5;B:B)/10*D1)",
1446 ScRange(0, 0, 0, 0, 1048575, 0),
1447 60.0,
1448 true
1452 "=SUM(IF($C$1=A:A;B:B;B:B)/10*D1)", // IF has else clause
1453 ScRange(-1, -1, -1, -1, -1, -1), // Has no trimmable double-ref.
1454 140.0,
1455 true
1459 "=SUM(IF($C$1=A:A;B:B)/10*D1)",
1460 ScRange(-1, -1, -1, -1, -1, -1), // Has no trimmable double-ref.
1462 false // Not in matrix mode.
1466 "=SUMPRODUCT(A:A=$C$1; 1-(A:A=$C$1))",
1467 ScRange(0, 0, 0, 0, 1048575, 0),
1468 0.0,
1469 false // Not in matrix mode.
1473 for (sal_Int32 nTestIdx = 0; nTestIdx < nTestCases; ++nTestIdx)
1475 TestCase& rTestCase = aTestCases[nTestIdx];
1476 if (rTestCase.bMatrixFormula)
1477 m_pDoc->InsertMatrixFormula(4, 0, 4, 0, aMark, rTestCase.aFormula); // Formula in E1
1478 else
1479 m_pDoc->SetString(ScAddress(4, 0, 0), rTestCase.aFormula); // Formula in E1
1481 std::string aMsgStart = "TestCase#" + std::to_string(nTestIdx + 1) + " : ";
1482 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsgStart + "Incorrect formula result", rTestCase.fResult, m_pDoc->GetValue(ScAddress(4, 0, 0)));
1484 ScFormulaCell* pCell = m_pDoc->GetFormulaCell(ScAddress(4, 0, 0));
1485 ScTokenArray* pCode = pCell->GetCode();
1486 sal_Int32 nLen = pCode->GetCodeLen();
1487 FormulaToken** pRPNArray = pCode->GetCode();
1489 for (sal_Int32 nIdx = 0; nIdx < nLen; ++nIdx)
1491 FormulaToken* pTok = pRPNArray[nIdx];
1492 if (pTok && pTok->GetType() == svDoubleRef)
1494 ScRange aRange = pTok->GetDoubleRef()->toAbs(*m_pDoc, ScAddress(4, 0, 0));
1495 if (aRange == rTestCase.aTrimmableRange)
1496 CPPUNIT_ASSERT_MESSAGE(aMsgStart + "Double ref is incorrectly flagged as not trimmable to data",
1497 pTok->GetDoubleRef()->IsTrimToData());
1498 else
1499 CPPUNIT_ASSERT_MESSAGE(aMsgStart + "Double ref is incorrectly flagged as trimmable to data",
1500 !pTok->GetDoubleRef()->IsTrimToData());
1505 m_pDoc->DeleteTab(0);
1508 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdate)
1510 m_pDoc->InsertTab(0, "Formula");
1512 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1514 m_pDoc->SetValue(ScAddress(0,0,0), 2.0); // A1
1515 m_pDoc->SetString(ScAddress(2,2,0), "=A1"); // C3
1516 m_pDoc->SetString(ScAddress(2,3,0), "=$A$1"); // C4
1518 ScAddress aPos(2,2,0);
1519 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1521 aPos = ScAddress(2,3,0);
1522 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C4.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1524 // Delete row 2 to push formula cells up (to C2:C3).
1525 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1527 aPos = ScAddress(2,1,0);
1528 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1530 aPos = ScAddress(2,2,0);
1531 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1533 // Insert one row at row 2 to move them back.
1534 m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1536 aPos = ScAddress(2,2,0);
1537 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1539 aPos = ScAddress(2,3,0);
1540 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C4.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1542 // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
1543 m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1545 aPos = ScAddress(2,4,0);
1546 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C5.", OUString("=A3"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1548 aPos = ScAddress(2,5,0);
1549 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C6.", OUString("=$A$3"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1551 // Delete 2 rows at row 1 to shift them back.
1552 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1554 aPos = ScAddress(2,2,0);
1555 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1557 aPos = ScAddress(2,3,0);
1558 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C4.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1560 // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
1561 m_pDoc->InsertCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1563 aPos = ScAddress(5,2,0);
1564 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in F3.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1566 aPos = ScAddress(5,3,0);
1567 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in F4.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1569 // Delete columns B:D to shift them back.
1570 m_pDoc->DeleteCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1572 aPos = ScAddress(2,2,0);
1573 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1575 aPos = ScAddress(2,3,0);
1576 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C4.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1578 // Insert cells over A1:A3 to only shift A1 down to A4.
1579 m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
1581 aPos = ScAddress(2,2,0);
1582 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=A4"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1584 aPos = ScAddress(2,3,0);
1585 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C4.", OUString("=$A$4"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1587 // .. and back.
1588 m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
1590 aPos = ScAddress(2,2,0);
1591 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C3.", OUString("=A1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1593 aPos = ScAddress(2,3,0);
1594 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C4.", OUString("=$A$1"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1596 // Delete row 1 which will delete the value cell (A1).
1597 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1599 aPos = ScAddress(2,1,0);
1600 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
1601 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1602 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1603 aPos = ScAddress(2,2,0);
1604 pFC = m_pDoc->GetFormulaCell(aPos);
1605 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1606 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1608 // Clear all and start over.
1609 clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
1611 // Test range updates
1613 // Fill B2:C3 with values.
1614 m_pDoc->SetValue(ScAddress(1,1,0), 1);
1615 m_pDoc->SetValue(ScAddress(1,2,0), 2);
1616 m_pDoc->SetValue(ScAddress(2,1,0), 3);
1617 m_pDoc->SetValue(ScAddress(2,2,0), 4);
1619 m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
1620 m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
1622 aPos = ScAddress(0,5,0);
1623 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(B2:C3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1625 aPos = ScAddress(0,6,0);
1626 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($B$2:$C$3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1628 // Insert a row at row 1.
1629 m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1631 aPos = ScAddress(0,6,0);
1632 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM(B3:C4)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1634 aPos = ScAddress(0,7,0);
1635 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A8.", OUString("=SUM($B$3:$C$4)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1637 // ... and back.
1638 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1640 aPos = ScAddress(0,5,0);
1641 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(B2:C3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1643 aPos = ScAddress(0,6,0);
1644 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($B$2:$C$3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1646 // Insert columns B:C to shift only the value range.
1647 m_pDoc->InsertCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1649 aPos = ScAddress(0,5,0);
1650 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(D2:E3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1652 aPos = ScAddress(0,6,0);
1653 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($D$2:$E$3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1655 // ... and back.
1656 m_pDoc->DeleteCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1658 aPos = ScAddress(0,5,0);
1659 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(B2:C3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1661 aPos = ScAddress(0,6,0);
1662 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($B$2:$C$3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1664 // Insert rows 5:6 to shift the formula cells only.
1665 m_pDoc->InsertRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1667 aPos = ScAddress(0,7,0);
1668 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A8.", OUString("=SUM(B2:C3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1670 aPos = ScAddress(0,8,0);
1671 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A9.", OUString("=SUM($B$2:$C$3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1673 // ... and back.
1674 m_pDoc->DeleteRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1676 aPos = ScAddress(0,5,0);
1677 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(B2:C3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1679 aPos = ScAddress(0,6,0);
1680 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($B$2:$C$3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1682 // Check the values of the formula cells in A6:A7.
1683 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1684 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1686 // Insert cells over B1:B2 to partially shift value range.
1687 m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
1689 // Check the values of the formula cells in A6:A7 again.
1690 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1691 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1693 // ... and shift them back.
1694 m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
1696 // The formula cell results should be back too.
1697 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1698 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1700 // Delete rows 2:3 to completely remove the referenced range.
1701 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),2,0));
1703 // Both A4 and A5 should show #REF! errors.
1704 pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
1705 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1706 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1708 pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
1709 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1710 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1712 m_pDoc->DeleteTab(0);
1715 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateRange)
1717 m_pDoc->InsertTab(0, "Formula");
1719 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1721 setExpandRefs(false);
1723 // Set values to B2:C5.
1724 m_pDoc->SetValue(ScAddress(1,1,0), 1);
1725 m_pDoc->SetValue(ScAddress(1,2,0), 2);
1726 m_pDoc->SetValue(ScAddress(1,3,0), 3);
1727 m_pDoc->SetValue(ScAddress(1,4,0), 4);
1728 m_pDoc->SetValue(ScAddress(2,1,0), 5);
1729 m_pDoc->SetValue(ScAddress(2,2,0), 6);
1730 m_pDoc->SetValue(ScAddress(2,3,0), 7);
1731 m_pDoc->SetValue(ScAddress(2,4,0), 8);
1733 // Set formula cells to A7 and A8.
1734 m_pDoc->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
1735 m_pDoc->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
1737 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM(B2:C5)"), m_pDoc->GetFormula(0,6,0));
1739 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A8.", OUString("=SUM($B$2:$C$5)"), m_pDoc->GetFormula(0,7,0));
1741 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1742 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1744 // Delete row 3. This should shrink the range references by one row.
1745 m_pDoc->DeleteRow(ScRange(0,2,0,m_pDoc->MaxCol(),2,0));
1747 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(B2:C4)"), m_pDoc->GetFormula(0,5,0));
1749 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($B$2:$C$4)"), m_pDoc->GetFormula(0,6,0));
1751 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1752 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1754 // Delete row 4 - bottom of range
1755 m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
1757 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM(B2:C3)"), m_pDoc->GetFormula(0,4,0));
1759 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM($B$2:$C$3)"), m_pDoc->GetFormula(0,5,0));
1761 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1762 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1764 // Delete row 2 - top of range
1765 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1767 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A4.", OUString("=SUM(B2:C2)"), m_pDoc->GetFormula(0,3,0));
1769 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM($B$2:$C$2)"), m_pDoc->GetFormula(0,4,0));
1771 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1772 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1774 // Clear the range and start over.
1775 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1777 // Fill C2:F3 with values.
1778 m_pDoc->SetValue(ScAddress(2,1,0), 1);
1779 m_pDoc->SetValue(ScAddress(3,1,0), 2);
1780 m_pDoc->SetValue(ScAddress(4,1,0), 3);
1781 m_pDoc->SetValue(ScAddress(5,1,0), 4);
1782 m_pDoc->SetValue(ScAddress(2,2,0), 5);
1783 m_pDoc->SetValue(ScAddress(3,2,0), 6);
1784 m_pDoc->SetValue(ScAddress(4,2,0), 7);
1785 m_pDoc->SetValue(ScAddress(5,2,0), 8);
1787 // Set formulas to A2 and A3.
1788 m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
1789 m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
1791 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C2:F3)"), m_pDoc->GetFormula(0,1,0));
1793 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$2:$F$3)"), m_pDoc->GetFormula(0,2,0));
1795 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1796 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1798 // Delete column D.
1799 m_pDoc->DeleteCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
1801 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C2:E3)"), m_pDoc->GetFormula(0,1,0));
1803 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$2:$E$3)"), m_pDoc->GetFormula(0,2,0));
1805 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1806 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1808 // Delete column E - the right edge of reference range.
1809 m_pDoc->DeleteCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
1811 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C2:D3)"), m_pDoc->GetFormula(0,1,0));
1813 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$2:$D$3)"), m_pDoc->GetFormula(0,2,0));
1815 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1816 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1818 // Delete column C - the left edge of reference range.
1819 m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1821 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C2:C3)"), m_pDoc->GetFormula(0,1,0));
1823 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$2:$C$3)"), m_pDoc->GetFormula(0,2,0));
1825 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1826 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1828 // Clear the range and start over.
1829 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1831 // Disable expansion of range reference on insertion in adjacent areas.
1832 setExpandRefs(false);
1834 // Fill C2:D3 with values.
1835 m_pDoc->SetValue(ScAddress(2,1,0), 1);
1836 m_pDoc->SetValue(ScAddress(3,1,0), 2);
1837 m_pDoc->SetValue(ScAddress(2,2,0), 3);
1838 m_pDoc->SetValue(ScAddress(3,2,0), 4);
1840 // Set formulas at A5 and A6.
1841 m_pDoc->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
1842 m_pDoc->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
1844 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM(C2:D3)"), m_pDoc->GetFormula(0,4,0));
1846 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM($C$2:$D$3)"), m_pDoc->GetFormula(0,5,0));
1848 // Insert a column at column C. This should simply shift the reference without expansion.
1849 m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1851 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM(D2:E3)"), m_pDoc->GetFormula(0,4,0));
1853 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM($D$2:$E$3)"), m_pDoc->GetFormula(0,5,0));
1855 // Shift it back.
1856 m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1858 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM(C2:D3)"), m_pDoc->GetFormula(0,4,0));
1860 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM($C$2:$D$3)"), m_pDoc->GetFormula(0,5,0));
1862 // Insert at column D. This should expand the reference by one column length.
1863 m_pDoc->InsertCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
1865 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM(C2:E3)"), m_pDoc->GetFormula(0,4,0));
1867 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM($C$2:$E$3)"), m_pDoc->GetFormula(0,5,0));
1869 // Insert at column F. No expansion should occur since the edge expansion is turned off.
1870 m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
1872 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=SUM(C2:E3)"), m_pDoc->GetFormula(0,4,0));
1874 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM($C$2:$E$3)"), m_pDoc->GetFormula(0,5,0));
1876 // Insert at row 2. No expansion should occur with edge expansion turned off.
1877 m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1879 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=SUM(C3:E4)"), m_pDoc->GetFormula(0,5,0));
1881 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM($C$3:$E$4)"), m_pDoc->GetFormula(0,6,0));
1883 // Insert at row 4 to expand the reference range.
1884 m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
1886 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A7.", OUString("=SUM(C3:E5)"), m_pDoc->GetFormula(0,6,0));
1888 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A8.", OUString("=SUM($C$3:$E$5)"), m_pDoc->GetFormula(0,7,0));
1890 // Insert at row 6. No expansion with edge expansion turned off.
1891 m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
1893 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A8.", OUString("=SUM(C3:E5)"), m_pDoc->GetFormula(0,7,0));
1895 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A9.", OUString("=SUM($C$3:$E$5)"), m_pDoc->GetFormula(0,8,0));
1897 // Clear the range and start over.
1898 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1900 // Turn edge expansion on.
1901 setExpandRefs(true);
1903 // Fill C6:D7 with values.
1904 m_pDoc->SetValue(ScAddress(2,5,0), 1);
1905 m_pDoc->SetValue(ScAddress(2,6,0), 2);
1906 m_pDoc->SetValue(ScAddress(3,5,0), 3);
1907 m_pDoc->SetValue(ScAddress(3,6,0), 4);
1909 // Set formulas at A2 and A3.
1910 m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
1911 m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
1913 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C6:D7)"), m_pDoc->GetFormula(0,1,0));
1915 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$6:$D$7)"), m_pDoc->GetFormula(0,2,0));
1917 // Insert at column E. This should expand the reference range by one column.
1918 m_pDoc->InsertCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
1920 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C6:E7)"), m_pDoc->GetFormula(0,1,0));
1922 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$6:$E$7)"), m_pDoc->GetFormula(0,2,0));
1924 // Insert at column C to edge-expand the reference range.
1925 m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
1927 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C6:F7)"), m_pDoc->GetFormula(0,1,0));
1929 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$6:$F$7)"), m_pDoc->GetFormula(0,2,0));
1931 // Insert at row 8 to edge-expand.
1932 m_pDoc->InsertRow(ScRange(0,7,0,m_pDoc->MaxCol(),7,0));
1934 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C6:F8)"), m_pDoc->GetFormula(0,1,0));
1936 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$6:$F$8)"), m_pDoc->GetFormula(0,2,0));
1938 // Insert at row 6 to edge-expand.
1939 m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
1941 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A2.", OUString("=SUM(C6:F9)"), m_pDoc->GetFormula(0,1,0));
1943 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A3.", OUString("=SUM($C$6:$F$9)"), m_pDoc->GetFormula(0,2,0));
1945 m_pDoc->InsertTab(1, "StickyRange");
1947 // A3:A18 all possible combinations of relative and absolute addressing,
1948 // leaving one row above and below unreferenced.
1949 ScAddress aPos(0,2,1);
1950 m_pDoc->SetString( aPos, "=B2:B1048575");
1951 aPos.IncRow();
1952 m_pDoc->SetString( aPos, "=B2:B$1048575");
1953 aPos.IncRow();
1954 m_pDoc->SetString( aPos, "=B2:$B1048575");
1955 aPos.IncRow();
1956 m_pDoc->SetString( aPos, "=B2:$B$1048575");
1957 aPos.IncRow();
1958 m_pDoc->SetString( aPos, "=B$2:B1048575");
1959 aPos.IncRow();
1960 m_pDoc->SetString( aPos, "=B$2:B$1048575");
1961 aPos.IncRow();
1962 m_pDoc->SetString( aPos, "=B$2:$B1048575");
1963 aPos.IncRow();
1964 m_pDoc->SetString( aPos, "=B$2:$B$1048575");
1965 aPos.IncRow();
1966 m_pDoc->SetString( aPos, "=$B2:B1048575");
1967 aPos.IncRow();
1968 m_pDoc->SetString( aPos, "=$B2:B$1048575");
1969 aPos.IncRow();
1970 m_pDoc->SetString( aPos, "=$B2:$B1048575");
1971 aPos.IncRow();
1972 m_pDoc->SetString( aPos, "=$B2:$B$1048575");
1973 aPos.IncRow();
1974 m_pDoc->SetString( aPos, "=$B$2:B1048575");
1975 aPos.IncRow();
1976 m_pDoc->SetString( aPos, "=$B$2:B$1048575");
1977 aPos.IncRow();
1978 m_pDoc->SetString( aPos, "=$B$2:$B1048575");
1979 aPos.IncRow();
1980 m_pDoc->SetString( aPos, "=$B$2:$B$1048575");
1981 aPos.IncRow();
1982 // A19 reference to two cells on one row.
1983 m_pDoc->SetString( aPos, "=B1048575:C1048575");
1984 aPos.IncRow();
1986 // Insert 2 rows in the middle to shift bottom reference down and make it
1987 // sticky.
1988 m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
1990 // A3:A18 must not result in #REF! anywhere.
1991 aPos.Set(0,2,1);
1992 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A3 after insertion.", OUString("=B2:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1993 aPos.IncRow();
1994 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A4 after insertion.", OUString("=B2:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1995 aPos.IncRow();
1996 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A5 after insertion.", OUString("=B2:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1997 aPos.IncRow();
1998 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A6 after insertion.", OUString("=B2:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
1999 aPos.IncRow();
2000 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A7 after insertion.", OUString("=B$2:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2001 aPos.IncRow();
2002 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A8 after insertion.", OUString("=B$2:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2003 aPos.IncRow();
2004 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A9 after insertion.", OUString("=B$2:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2005 aPos.IncRow();
2006 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A10 after insertion.", OUString("=B$2:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2007 aPos.IncRow();
2008 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A11 after insertion.", OUString("=$B2:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2009 aPos.IncRow();
2010 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A12 after insertion.", OUString("=$B2:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2011 aPos.IncRow();
2012 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A13 after insertion.", OUString("=$B2:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2013 aPos.IncRow();
2014 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A14 after insertion.", OUString("=$B2:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2015 aPos.IncRow();
2016 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A15 after insertion.", OUString("=$B$2:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2017 aPos.IncRow();
2018 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A16 after insertion.", OUString("=$B$2:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2019 aPos.IncRow();
2020 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A17 after insertion.", OUString("=$B$2:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2021 aPos.IncRow();
2022 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A18 after insertion.", OUString("=$B$2:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2023 aPos.IncRow();
2025 // A19 reference to one row shifted out should be #REF!
2026 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A19 after insertion.", OUString("=B#REF!:C#REF!"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2027 // A19 enter reference to last row.
2028 m_pDoc->SetString( aPos, "=B1048576:C1048576");
2029 aPos.IncRow();
2031 // Delete row 1 to shift top reference up, bottom reference stays sticky.
2032 m_pDoc->DeleteRow(ScRange(0,0,1,m_pDoc->MaxCol(),0,1));
2034 // Check sticky bottom references and display of entire column references,
2035 // now in A2:A17.
2036 aPos.Set(0,1,1);
2037 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A2 after deletion.", OUString("=B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2038 aPos.IncRow();
2039 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A3 after deletion.", OUString("=B1:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2040 aPos.IncRow();
2041 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A4 after deletion.", OUString("=B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2042 aPos.IncRow();
2043 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A5 after deletion.", OUString("=B1:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2044 aPos.IncRow();
2045 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A6 after deletion.", OUString("=B$1:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2046 aPos.IncRow();
2047 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A7 after deletion.", OUString("=B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2048 aPos.IncRow();
2049 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A8 after deletion.", OUString("=B$1:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2050 aPos.IncRow();
2051 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A9 after deletion.", OUString("=B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2052 aPos.IncRow();
2053 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A10 after deletion.", OUString("=$B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2054 aPos.IncRow();
2055 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A11 after deletion.", OUString("=$B1:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2056 aPos.IncRow();
2057 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A12 after deletion.", OUString("=$B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2058 aPos.IncRow();
2059 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A13 after deletion.", OUString("=$B1:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2060 aPos.IncRow();
2061 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A14 after deletion.", OUString("=$B$1:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2062 aPos.IncRow();
2063 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A15 after deletion.", OUString("=$B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2064 aPos.IncRow();
2065 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A16 after deletion.", OUString("=$B$1:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2066 aPos.IncRow();
2067 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A17 after deletion.", OUString("=$B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2068 aPos.IncRow();
2070 // A18 reference to one last row should be shifted up.
2071 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A18 after deletion.", OUString("=B1048575:C1048575"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2072 aPos.IncRow();
2074 // Insert 4 rows in the middle.
2075 m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+3, 1));
2076 // Delete 2 rows in the middle.
2077 m_pDoc->DeleteRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
2079 // References in A2:A17 must still be the same.
2080 aPos.Set(0,1,1);
2081 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A2 after deletion.", OUString("=B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2082 aPos.IncRow();
2083 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A3 after deletion.", OUString("=B1:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2084 aPos.IncRow();
2085 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A4 after deletion.", OUString("=B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2086 aPos.IncRow();
2087 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A5 after deletion.", OUString("=B1:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2088 aPos.IncRow();
2089 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A6 after deletion.", OUString("=B$1:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2090 aPos.IncRow();
2091 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A7 after deletion.", OUString("=B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2092 aPos.IncRow();
2093 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A8 after deletion.", OUString("=B$1:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2094 aPos.IncRow();
2095 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A9 after deletion.", OUString("=B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2096 aPos.IncRow();
2097 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A10 after deletion.", OUString("=$B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2098 aPos.IncRow();
2099 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A11 after deletion.", OUString("=$B1:B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2100 aPos.IncRow();
2101 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A12 after deletion.", OUString("=$B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2102 aPos.IncRow();
2103 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A13 after deletion.", OUString("=$B1:$B$1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2104 aPos.IncRow();
2105 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A14 after deletion.", OUString("=$B$1:B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2106 aPos.IncRow();
2107 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A15 after deletion.", OUString("=$B:B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2108 aPos.IncRow();
2109 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A16 after deletion.", OUString("=$B$1:$B1048576"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2110 aPos.IncRow();
2111 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong reference in A17 after deletion.", OUString("=$B:$B"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
2112 aPos.IncRow();
2114 // Enter values in B1 and B1048576/B16777216 (last row).
2115 m_pDoc->SetValue( 1,0,1, 1.0);
2116 m_pDoc->SetValue( 1,m_pDoc->MaxRow(),1, 2.0);
2117 // Sticky reference including last row.
2118 m_pDoc->SetString( 2,0,1, "=SUM(B:B)");
2119 // Reference to last row.
2120 CPPUNIT_ASSERT_MESSAGE("m_pDoc->MaxRow() changed, adapt unit test.",
2121 m_pDoc->MaxRow() == 1048575 || m_pDoc->MaxRow() == 16777215);
2122 m_pDoc->SetString( 2,1,1, m_pDoc->MaxRow() == 1048575 ? OUString("=SUM(B1048576:C1048576)")
2123 : OUString("=SUM(B16777216:C16777216)"));
2124 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 3.0, m_pDoc->GetValue(2,0,1));
2125 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C2.", 2.0, m_pDoc->GetValue(2,1,1));
2126 // Delete last row.
2127 m_pDoc->DeleteRow( ScRange( 0, m_pDoc->MaxRow(), 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2128 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 1.0, m_pDoc->GetValue(2,0,1));
2129 CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C2 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,1,1));
2131 // Enter values in A23 and AMJ23/XFD23 (last column).
2132 m_pDoc->SetValue( 0,22,1, 1.0);
2133 m_pDoc->SetValue( m_pDoc->MaxCol(),22,1, 2.0);
2134 // C3 with sticky reference including last column.
2135 m_pDoc->SetString( 2,2,1, "=SUM(23:23)");
2136 // C4 with reference to last column.
2137 m_pDoc->SetString( 2,3,1, "=SUM(" + m_pDoc->MaxColAsString() + "22:" + m_pDoc->MaxColAsString() + "23)");
2138 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 3.0, m_pDoc->GetValue(2,2,1));
2139 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C4.", 2.0, m_pDoc->GetValue(2,3,1));
2140 // Delete last column.
2141 m_pDoc->DeleteCol( ScRange( m_pDoc->MaxCol(), 0, 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2142 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 1.0, m_pDoc->GetValue(2,2,1));
2143 CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C4 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,3,1));
2145 m_pDoc->DeleteTab(1);
2147 m_pDoc->DeleteTab(0);
2150 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateSheets)
2152 m_pDoc->InsertTab(0, "Sheet1");
2153 m_pDoc->InsertTab(1, "Sheet2");
2155 OUString aName;
2156 m_pDoc->GetName(0, aName);
2157 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2158 m_pDoc->GetName(1, aName);
2159 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2161 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2163 // Set values to B2:C3 on sheet Sheet1.
2164 m_pDoc->SetValue(ScAddress(1,1,0), 1);
2165 m_pDoc->SetValue(ScAddress(1,2,0), 2);
2166 m_pDoc->SetValue(ScAddress(2,1,0), 3);
2167 m_pDoc->SetValue(ScAddress(2,2,0), 4);
2169 // Set formulas to B2 and B3 on sheet Sheet2.
2170 m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
2171 m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
2173 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,1));
2175 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,1));
2177 // Swap the sheets.
2178 m_pDoc->MoveTab(0, 1);
2179 m_pDoc->GetName(0, aName);
2180 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2181 m_pDoc->GetName(1, aName);
2182 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2184 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,0));
2186 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,0));
2188 // Swap back.
2189 m_pDoc->MoveTab(0, 1);
2190 m_pDoc->GetName(0, aName);
2191 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2192 m_pDoc->GetName(1, aName);
2193 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2195 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,1));
2197 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,1));
2199 // Insert a new sheet between the two.
2200 m_pDoc->InsertTab(1, "Temp");
2202 m_pDoc->GetName(1, aName);
2203 CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName);
2204 m_pDoc->GetName(2, aName);
2205 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2207 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,2));
2209 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,2));
2211 // Move the last sheet (Sheet2) to the first position.
2212 m_pDoc->MoveTab(2, 0);
2214 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,0));
2216 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,0));
2218 // Move back.
2219 m_pDoc->MoveTab(0, 2);
2221 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,2));
2223 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,2));
2225 // Move the "Temp" sheet to the last position.
2226 m_pDoc->MoveTab(1, 2);
2228 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,1));
2230 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,1));
2232 // Move back.
2233 m_pDoc->MoveTab(2, 1);
2235 // Delete the temporary sheet.
2236 m_pDoc->DeleteTab(1);
2238 m_pDoc->GetName(1, aName);
2239 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2241 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,1));
2243 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,1));
2245 // Insert a new sheet before the first one.
2246 m_pDoc->InsertTab(0, "Temp");
2248 m_pDoc->GetName(1, aName);
2249 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2250 m_pDoc->GetName(2, aName);
2251 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2253 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,2));
2255 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,2));
2257 // Delete the temporary sheet.
2258 m_pDoc->DeleteTab(0);
2260 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,1));
2262 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,1));
2264 // Append a bunch of sheets.
2265 m_pDoc->InsertTab(2, "Temp1");
2266 m_pDoc->InsertTab(3, "Temp2");
2267 m_pDoc->InsertTab(4, "Temp3");
2269 // Move these tabs around. This shouldn't affects the first 2 sheets.
2270 m_pDoc->MoveTab(2, 4);
2271 m_pDoc->MoveTab(3, 2);
2273 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(Sheet1.B2:C3)"), m_pDoc->GetFormula(1,1,1));
2275 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($Sheet1.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,1));
2277 // Delete the temp sheets.
2278 m_pDoc->DeleteTab(4);
2279 m_pDoc->DeleteTab(3);
2280 m_pDoc->DeleteTab(2);
2282 // Delete Sheet1.
2283 m_pDoc->DeleteTab(0);
2284 m_pDoc->GetName(0, aName);
2285 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2287 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B2.", OUString("=SUM(#REF!.B2:C3)"), m_pDoc->GetFormula(1,1,0));
2289 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in Sheet2.B3.", OUString("=SUM($#REF!.$B$2:$C$3)"), m_pDoc->GetFormula(1,2,0));
2291 m_pDoc->DeleteTab(0);
2294 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateInsertRows)
2296 setExpandRefs(false);
2298 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2299 m_pDoc->InsertTab(0, "Formula");
2301 // Insert raw values in B2:B4.
2302 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
2303 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
2304 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
2306 // Insert a formula in B5 to sum up B2:B4.
2307 m_pDoc->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
2309 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2311 // Insert rows over rows 1:2.
2312 ScMarkData aMark(m_pDoc->GetSheetLimits());
2313 aMark.SelectOneTable(0);
2314 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2315 rFunc.InsertCells(ScRange(0,0,0,m_pDoc->MaxCol(),1,0), &aMark, INS_INSROWS_BEFORE, false, true);
2317 // The raw data should have shifted to B4:B6.
2318 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2319 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2320 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,5,0)));
2322 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(B4:B6)"), m_pDoc->GetFormula(1,6,0));
2324 // Clear and start over.
2325 clearSheet(m_pDoc, 0);
2327 // Set raw values in A4:A6.
2328 m_pDoc->SetValue(ScAddress(0,3,0), 1.0);
2329 m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
2330 m_pDoc->SetValue(ScAddress(0,5,0), 3.0);
2332 // Set formula in A3 to reference A4:A6.
2333 m_pDoc->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
2335 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2337 // Insert 3 rows over 2:4. This should push A3:A6 to A6:A9.
2338 rFunc.InsertCells(ScRange(0,1,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
2339 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
2340 CPPUNIT_ASSERT(pFC);
2341 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula cell should not be an error.", 0, static_cast<int>(pFC->GetErrCode()));
2342 ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2344 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=MAX(A7:A9)"), m_pDoc->GetFormula(0,5,0));
2346 m_pDoc->DeleteTab(0);
2349 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateSheetsDelete)
2351 m_pDoc->InsertTab(0, "Sheet1");
2352 m_pDoc->InsertTab(1, "Sheet2");
2353 m_pDoc->InsertTab(2, "Sheet3");
2354 m_pDoc->InsertTab(3, "Sheet4");
2356 m_pDoc->SetString(ScAddress(4,1,0), "=SUM(Sheet2.A4:Sheet4.A4)");
2357 m_pDoc->SetString(ScAddress(4,2,0), "=SUM($Sheet2.A4:$Sheet4.A4)");
2358 m_pDoc->DeleteTab(1);
2360 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet3.A4:Sheet4.A4)"), m_pDoc->GetFormula(4,1,0));
2361 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet3.A4:$Sheet4.A4)"), m_pDoc->GetFormula(4,2,0));
2363 m_pDoc->InsertTab(1, "Sheet2");
2365 m_pDoc->SetString(ScAddress(5,1,3), "=SUM(Sheet1.A5:Sheet3.A5)");
2366 m_pDoc->SetString(ScAddress(5,2,3), "=SUM($Sheet1.A5:$Sheet3.A5)");
2367 m_pDoc->DeleteTab(2);
2369 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet1.A5:Sheet2.A5)"), m_pDoc->GetFormula(5,1,2));
2370 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet1.A5:$Sheet2.A5)"), m_pDoc->GetFormula(5,2,2));
2372 m_pDoc->InsertTab(2, "Sheet3");
2374 m_pDoc->SetString(ScAddress(6,1,3), "=SUM(Sheet1.A6:Sheet3.A6)");
2375 m_pDoc->SetString(ScAddress(6,2,3), "=SUM($Sheet1.A6:$Sheet3.A6)");
2376 m_pDoc->DeleteTabs(0,3);
2378 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(#REF!.A6:#REF!.A6)"), m_pDoc->GetFormula(6,1,0));
2379 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($#REF!.A6:$#REF!.A6)"), m_pDoc->GetFormula(6,2,0));
2381 m_pDoc->InsertTab(0, "Sheet1");
2382 m_pDoc->InsertTab(1, "Sheet2");
2383 m_pDoc->InsertTab(2, "Sheet3");
2385 m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.A2:Sheet3.A2");
2386 m_pDoc->SetString(ScAddress(2,1,1), "=SUM(Sheet1.A1:Sheet2.A1");
2387 m_pDoc->SetString(ScAddress(3,1,1), "=SUM(Sheet2.A3:Sheet4.A3");
2389 m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.A2:$Sheet3.A2");
2390 m_pDoc->SetString(ScAddress(2,2,1), "=SUM($Sheet1.A1:$Sheet2.A1");
2391 m_pDoc->SetString(ScAddress(3,2,1), "=SUM($Sheet2.A3:$Sheet4.A3");
2393 m_pDoc->DeleteTab(2);
2395 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet1.A2:Sheet2.A2)"), m_pDoc->GetFormula(1,1,1));
2397 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet1.A1:Sheet2.A1)"), m_pDoc->GetFormula(2,1,1));
2399 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet2.A3:Sheet4.A3)"), m_pDoc->GetFormula(3,1,1));
2401 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet1.A2:$Sheet2.A2)"), m_pDoc->GetFormula(1,2,1));
2403 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet1.A1:$Sheet2.A1)"), m_pDoc->GetFormula(2,2,1));
2405 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet2.A3:$Sheet4.A3)"), m_pDoc->GetFormula(3,2,1));
2407 m_pDoc->DeleteTab(0);
2409 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet2.A2:Sheet2.A2)"), m_pDoc->GetFormula(1,1,0));
2411 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet2.A1:Sheet2.A1)"), m_pDoc->GetFormula(2,1,0));
2413 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM(Sheet2.A3:Sheet4.A3)"), m_pDoc->GetFormula(3,1,0));
2415 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet2.A2:$Sheet2.A2)"), m_pDoc->GetFormula(1,2,0));
2417 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet2.A1:$Sheet2.A1)"), m_pDoc->GetFormula(2,2,0));
2419 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong Formula", OUString("=SUM($Sheet2.A3:$Sheet4.A3)"), m_pDoc->GetFormula(3,2,0));
2421 m_pDoc->DeleteTab(0);
2422 m_pDoc->DeleteTab(0);
2425 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateInsertColumns)
2427 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2428 setExpandRefs(false);
2430 m_pDoc->InsertTab(0, "Formula");
2432 // Set named range for B2 with absolute column and relative same row.
2433 const ScAddress aNamePos(0,1,0);
2434 bool bInserted = m_pDoc->InsertNewRangeName("RowRelativeRange", aNamePos, "$Formula.$B2");
2435 CPPUNIT_ASSERT(bInserted);
2437 // Set named range for entire absolute column B.
2438 bInserted = m_pDoc->InsertNewRangeName("EntireColumn", aNamePos, "$B:$B");
2439 CPPUNIT_ASSERT(bInserted);
2441 // Set named range for entire absolute row 2.
2442 bInserted = m_pDoc->InsertNewRangeName("EntireRow", aNamePos, "$2:$2");
2443 CPPUNIT_ASSERT(bInserted);
2445 // Set values in B1:B3.
2446 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
2447 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
2448 m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
2450 // Reference them in B4.
2451 m_pDoc->SetString(ScAddress(1,3,0), "=SUM(B1:B3)");
2452 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2454 // Use named range in C2 to reference B2.
2455 m_pDoc->SetString(ScAddress(2,1,0), "=RowRelativeRange");
2456 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
2458 // Use named range in C3 to reference column B, values in B1,B2,B3,B4
2459 m_pDoc->SetString(ScAddress(2,2,0), "=SUM(EntireColumn)");
2460 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2,2,0)));
2462 // Use named range in C4 to reference row 2, values in B2 and C2.
2463 m_pDoc->SetString(ScAddress(2,3,0), "=SUM(EntireRow)");
2464 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,3,0)));
2466 // Insert columns over A:B.
2467 ScMarkData aMark(m_pDoc->GetSheetLimits());
2468 aMark.SelectOneTable(0);
2469 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2470 rFunc.InsertCells(ScRange(0,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
2472 // Now, the original column B has moved to column D.
2473 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in D4 after column insertion.", OUString("=SUM(D1:D3)"), m_pDoc->GetFormula(3,3,0));
2475 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
2477 // Check that the named reference points to the moved cell, now D2.
2478 ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("ROWRELATIVERANGE");
2479 CPPUNIT_ASSERT(pName);
2480 OUString aSymbol = pName->GetSymbol(aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2481 CPPUNIT_ASSERT_EQUAL(OUString("$Formula.$D2"), aSymbol);
2483 // Check that the formula using the name, now in E2, still has the same result.
2484 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in E2 after column insertion.", OUString("=RowRelativeRange"), m_pDoc->GetFormula(4,1,0));
2486 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,1,0)));
2488 // Check that the named column reference points to the moved column, now D.
2489 pName = m_pDoc->GetRangeName()->findByUpperName("ENTIRECOLUMN");
2490 CPPUNIT_ASSERT(pName);
2491 aSymbol = pName->GetSymbol(aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2492 CPPUNIT_ASSERT_EQUAL(OUString("$D:$D"), aSymbol);
2494 // Check that the formula using the name, now in E3, still has the same result.
2495 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in E3 after column insertion.", OUString("=SUM(EntireColumn)"), m_pDoc->GetFormula(4,2,0));
2497 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(4,2,0)));
2499 // Check that the named row reference still points to the same entire row
2500 // and does not have a #REF! error due to inserted columns.
2501 pName = m_pDoc->GetRangeName()->findByUpperName("ENTIREROW");
2502 CPPUNIT_ASSERT(pName);
2503 aSymbol = pName->GetSymbol(aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2504 CPPUNIT_ASSERT_EQUAL(OUString("$2:$2"), aSymbol);
2506 // Check that the formula using the name, now in E4, still has the same result.
2507 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in E4 after column insertion.", OUString("=SUM(EntireRow)"), m_pDoc->GetFormula(4,3,0));
2509 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(4,3,0)));
2511 m_pDoc->DeleteTab(0);
2514 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMove)
2516 m_pDoc->InsertTab(0, "Sheet1");
2518 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2520 // Set value to B4:B6.
2521 m_pDoc->SetValue(ScAddress(1,3,0), 1);
2522 m_pDoc->SetValue(ScAddress(1,4,0), 2);
2523 m_pDoc->SetValue(ScAddress(1,5,0), 3);
2525 // Set formulas to A9:A12 that references B4:B6.
2526 m_pDoc->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
2527 m_pDoc->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
2528 m_pDoc->SetString(ScAddress(0,10,0), "=B5");
2529 m_pDoc->SetString(ScAddress(0,11,0), "=$B$6");
2531 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2532 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2533 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2534 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2536 // Move B4:B6 to D4 (two columns to the right).
2537 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2538 bool bMoved = rFunc.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
2539 CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved);
2541 // The results of the formula cells that reference the moved range should remain the same.
2542 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2543 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2544 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2545 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2547 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(D4:D6)"), m_pDoc->GetFormula(0,8,0));
2548 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM($D$4:$D$6)"), m_pDoc->GetFormula(0,9,0));
2549 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=D5"), m_pDoc->GetFormula(0,10,0));
2550 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=$D$6"), m_pDoc->GetFormula(0,11,0));
2552 // Move A9:A12 to B10:B13.
2553 bMoved = rFunc.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
2554 CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved);
2556 // The results of these formula cells should still stay the same.
2557 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,9,0));
2558 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,10,0));
2559 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2560 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2562 // Displayed formulas should stay the same since the referenced range hasn't moved.
2563 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(D4:D6)"), m_pDoc->GetFormula(1,9,0));
2564 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM($D$4:$D$6)"), m_pDoc->GetFormula(1,10,0));
2565 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=D5"), m_pDoc->GetFormula(1,11,0));
2566 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=$D$6"), m_pDoc->GetFormula(1,12,0));
2568 // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
2569 // where it is.
2570 bMoved = rFunc.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
2571 CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved);
2573 // Only the values of B10 and B11 should be updated.
2574 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,9,0));
2575 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,10,0));
2576 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2577 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2579 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(D4:D6)"), m_pDoc->GetFormula(1,9,0));
2580 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM($D$4:$D$6)"), m_pDoc->GetFormula(1,10,0));
2581 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=E5"), m_pDoc->GetFormula(1,11,0));
2582 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=$D$6"), m_pDoc->GetFormula(1,12,0));
2584 m_pDoc->DeleteTab(0);
2587 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveUndo)
2589 m_pDoc->InsertTab(0, "Test");
2591 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2593 // Set values in A1:A4.
2594 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2595 m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
2596 m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
2597 m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
2599 // Set formulas with single cell references in A6:A8.
2600 m_pDoc->SetString(ScAddress(0,5,0), "=A1");
2601 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2602 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=A1"), m_pDoc->GetFormula(0,5,0));
2604 m_pDoc->SetString(ScAddress(0,6,0), "=A1+A2+A3");
2605 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2606 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=A1+A2+A3"), m_pDoc->GetFormula(0,6,0));
2608 m_pDoc->SetString(ScAddress(0,7,0), "=A1+A3+A4");
2609 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2610 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=A1+A3+A4"), m_pDoc->GetFormula(0,7,0));
2612 // Set formulas with range references in A10:A12.
2613 m_pDoc->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
2614 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2615 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A2)"), m_pDoc->GetFormula(0,9,0));
2617 m_pDoc->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
2618 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2619 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A3)"), m_pDoc->GetFormula(0,10,0));
2621 m_pDoc->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
2622 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2623 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A4)"), m_pDoc->GetFormula(0,11,0));
2625 // Move A1:A3 to C1:C3. Note that A4 remains.
2626 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2627 bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
2628 CPPUNIT_ASSERT(bMoved);
2630 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2631 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=C1"), m_pDoc->GetFormula(0,5,0));
2633 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2634 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=C1+C2+C3"), m_pDoc->GetFormula(0,6,0));
2636 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2637 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=C1+C3+A4"), m_pDoc->GetFormula(0,7,0));
2639 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2640 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(C1:C2)"), m_pDoc->GetFormula(0,9,0));
2642 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2643 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(C1:C3)"), m_pDoc->GetFormula(0,10,0));
2645 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2646 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A4)"), m_pDoc->GetFormula(0,11,0));
2648 // Undo the move.
2649 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2650 CPPUNIT_ASSERT(pUndoMgr);
2651 pUndoMgr->Undo();
2653 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2654 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=A1"), m_pDoc->GetFormula(0,5,0));
2656 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2657 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=A1+A2+A3"), m_pDoc->GetFormula(0,6,0));
2659 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2660 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=A1+A3+A4"), m_pDoc->GetFormula(0,7,0));
2662 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2663 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A2)"), m_pDoc->GetFormula(0,9,0));
2665 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2666 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A3)"), m_pDoc->GetFormula(0,10,0));
2668 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2669 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=SUM(A1:A4)"), m_pDoc->GetFormula(0,11,0));
2671 // Make sure the broadcasters are still valid by changing the value of A1.
2672 m_pDoc->SetValue(ScAddress(0,0,0), 20);
2674 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2675 CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2676 CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2678 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2679 CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2680 CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2682 m_pDoc->DeleteTab(0);
2685 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveUndo2)
2687 m_pDoc->InsertTab(0, "Test");
2689 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2691 std::vector<std::vector<const char*>> aData = {
2692 { "1", "2", "=A2*10", "=SUM(A1:B1)" },
2693 { "3", "4", "=SUM(A2:B2)", "=SUM(A2:B2)" },
2694 { "=SUM(A1:B1)" },
2697 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2699 std::vector<std::vector<const char*>> aCheckInitial = {
2700 { "1", "2", "30", "3" },
2701 { "3", "4", "7", "7" },
2702 { "3", nullptr, nullptr, nullptr },
2705 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2706 CPPUNIT_ASSERT(bGood);
2708 // D1:D2 should be grouped.
2709 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
2710 CPPUNIT_ASSERT(pFC);
2711 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2713 // Drag A1:B1 into A2:B2 thereby overwriting the old A2:B2 content.
2714 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2715 bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,1,0,0), ScAddress(0,1,0), true, true, false, true);
2716 CPPUNIT_ASSERT(bMoved);
2718 std::vector<std::vector<const char*>> aCheckAfter = {
2719 { nullptr, nullptr, "10", "3" },
2720 { "1", "2", "3", "3" },
2721 { "3", nullptr, nullptr, nullptr },
2724 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:B1 moved to A2:B2");
2725 CPPUNIT_ASSERT(bGood);
2727 // Undo the move.
2728 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2729 CPPUNIT_ASSERT(pUndoMgr);
2730 pUndoMgr->Undo();
2732 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2733 CPPUNIT_ASSERT(bGood);
2735 // D1:D2 should be grouped.
2736 pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
2737 CPPUNIT_ASSERT(pFC);
2738 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2740 // Redo and check.
2741 pUndoMgr->Redo();
2743 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2744 CPPUNIT_ASSERT(bGood);
2746 m_pDoc->DeleteTab(0);
2749 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveUndo3NonShared)
2751 m_pDoc->InsertTab(0, "Test");
2753 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2755 std::vector<std::vector<const char*>> aData = {
2756 { "10", nullptr, nullptr },
2757 { "=A1", nullptr, nullptr },
2758 { "=A2+A1", nullptr, nullptr },
2761 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2763 std::vector<std::vector<const char*>> aCheckInitial = {
2764 { "10", nullptr, nullptr },
2765 { "10", nullptr, nullptr },
2766 { "20", nullptr, nullptr },
2769 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2770 CPPUNIT_ASSERT(bGood);
2772 // Drag A2:A3 into C2:C3.
2773 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2774 bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,2,0), ScAddress(2,1,0), true, true, false, true);
2775 CPPUNIT_ASSERT(bMoved);
2777 std::vector<std::vector<const char*>> aCheckAfter = {
2778 { "10", nullptr, nullptr},
2779 { nullptr, nullptr, "10" },
2780 { nullptr, nullptr, "20" },
2783 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A3 moved to C2:C3");
2784 CPPUNIT_ASSERT(bGood);
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 // Redo and check.
2795 pUndoMgr->Redo();
2797 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2798 CPPUNIT_ASSERT(bGood);
2800 m_pDoc->DeleteTab(0);
2803 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveUndo3Shared)
2805 m_pDoc->InsertTab(0, "Test");
2807 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2809 std::vector<std::vector<const char*>> aData = {
2810 { "10", nullptr, nullptr },
2811 { "=A1", nullptr, nullptr },
2812 { "=A2+$A$1", nullptr, nullptr },
2813 { "=A3+$A$1", nullptr, nullptr },
2816 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2818 std::vector<std::vector<const char*>> aCheckInitial = {
2819 { "10", nullptr, nullptr },
2820 { "10", nullptr, nullptr },
2821 { "20", nullptr, nullptr },
2822 { "30", nullptr, nullptr },
2825 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2826 CPPUNIT_ASSERT(bGood);
2828 // A3:A4 should be grouped.
2829 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2830 CPPUNIT_ASSERT(pFC);
2831 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2833 // Drag A2:A4 into C2:C4.
2834 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2835 bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,3,0), ScAddress(2,1,0), true, true, false, true);
2836 CPPUNIT_ASSERT(bMoved);
2838 std::vector<std::vector<const char*>> aCheckAfter = {
2839 { "10", nullptr, nullptr},
2840 { nullptr, nullptr, "10" },
2841 { nullptr, nullptr, "20" },
2842 { nullptr, nullptr, "30" },
2845 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A4 moved to C2:C4");
2846 CPPUNIT_ASSERT(bGood);
2848 // C3:C4 should be grouped.
2849 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
2850 CPPUNIT_ASSERT(pFC);
2851 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2853 // Undo the move.
2854 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2855 CPPUNIT_ASSERT(pUndoMgr);
2856 pUndoMgr->Undo();
2858 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2859 CPPUNIT_ASSERT(bGood);
2861 // A3:A4 should be grouped.
2862 pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
2863 CPPUNIT_ASSERT(pFC);
2864 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
2866 // Redo and check.
2867 pUndoMgr->Redo();
2869 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2870 CPPUNIT_ASSERT(bGood);
2872 m_pDoc->DeleteTab(0);
2875 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveUndoDependents)
2877 m_pDoc->InsertTab(0, "Test");
2879 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2880 std::vector<std::vector<const char*>> aData = {
2881 { "1" },
2882 { "22" },
2883 { "3" },
2884 { "4" },
2885 { "5" },
2886 { "=SUM(C1:C5)" },
2887 { "=C6" },
2890 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData);
2892 std::vector<std::vector<const char*>> aCheckInitial = {
2893 { "1" },
2894 { "22" },
2895 { "3" },
2896 { "4" },
2897 { "5" },
2898 { "35" },
2899 { "35" },
2902 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2903 CPPUNIT_ASSERT(bGood);
2905 // Drag C2 into D2.
2906 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2907 bool bMoved = rFunc.MoveBlock(ScRange(2, 1, 0, 2, 1, 0), ScAddress(3, 1, 0), true, true, false, true);
2908 CPPUNIT_ASSERT(bMoved);
2910 std::vector<std::vector<const char*>> aCheckAfter = {
2911 { "1" },
2912 { nullptr },
2913 { "3" },
2914 { "4" },
2915 { "5" },
2916 { "13" },
2917 { "13" },
2920 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "C2 moved to D2");
2921 CPPUNIT_ASSERT(bGood);
2923 // Undo the move.
2924 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2925 CPPUNIT_ASSERT(pUndoMgr);
2926 pUndoMgr->Undo();
2928 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2929 CPPUNIT_ASSERT(bGood);
2931 // Redo and check.
2932 pUndoMgr->Redo();
2934 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2935 CPPUNIT_ASSERT(bGood);
2937 m_pDoc->DeleteTab(0);
2940 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveUndo4)
2942 m_pDoc->InsertTab(0, "Test");
2944 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2945 std::vector<std::vector<const char*>> aData = {
2946 { "1", nullptr, "=B1", "=A1" },
2947 { "2", nullptr, "=B2", "=A2" },
2950 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
2952 std::vector<std::vector<const char*>> aCheckInitial = {
2953 { "1", nullptr, "0", "1" },
2954 { "2", nullptr, "0", "2" },
2957 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
2958 CPPUNIT_ASSERT(bGood);
2960 // Drag A1:A2 into B1:B2.
2961 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2962 bool bMoved = rFunc.MoveBlock(ScRange(0, 0, 0, 0, 1, 0), ScAddress(1, 0, 0), true, true, false, true);
2963 CPPUNIT_ASSERT(bMoved);
2965 std::vector<std::vector<const char*>> aCheckAfter = {
2966 { nullptr, "1", "1", "1" },
2967 { nullptr, "2", "2", "2" },
2970 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:A2 moved to B1:B2");
2971 CPPUNIT_ASSERT(bGood);
2973 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B1"), m_pDoc->GetFormula(2,0,0)); // C1
2974 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B2"), m_pDoc->GetFormula(2,1,0)); // C2
2975 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B1"), m_pDoc->GetFormula(3,0,0)); // D1
2976 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B2"), m_pDoc->GetFormula(3,1,0)); // D2
2978 // Undo the move.
2979 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2980 CPPUNIT_ASSERT(pUndoMgr);
2981 pUndoMgr->Undo();
2983 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
2984 CPPUNIT_ASSERT(bGood);
2986 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B1"), m_pDoc->GetFormula(2,0,0)); // C1
2987 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B2"), m_pDoc->GetFormula(2,1,0)); // C2
2988 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=A1"), m_pDoc->GetFormula(3,0,0)); // D1
2989 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=A2"), m_pDoc->GetFormula(3,1,0)); // D2
2991 // Redo and check.
2992 pUndoMgr->Redo();
2994 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
2995 CPPUNIT_ASSERT(bGood);
2997 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B1"), m_pDoc->GetFormula(2,0,0)); // C1
2998 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B2"), m_pDoc->GetFormula(2,1,0)); // C2
2999 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B1"), m_pDoc->GetFormula(3,0,0)); // D1
3000 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=B2"), m_pDoc->GetFormula(3,1,0)); // D2
3002 m_pDoc->DeleteTab(0);
3005 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateMoveToSheet)
3007 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3009 m_pDoc->InsertTab(0, "Sheet1");
3010 m_pDoc->InsertTab(1, "Sheet2");
3012 // Set values to A1:A2 on Sheet1, and B1:B2 to reference them.
3013 m_pDoc->SetValue(ScAddress(0,0,0), 11);
3014 m_pDoc->SetValue(ScAddress(0,1,0), 12);
3015 m_pDoc->SetString(ScAddress(1,0,0), "=A1");
3016 m_pDoc->SetString(ScAddress(1,1,0), "=A2");
3018 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=A1"), m_pDoc->GetFormula(1,0,0));
3019 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=A2"), m_pDoc->GetFormula(1,1,0));
3021 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
3022 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(1,1,0)));
3024 // Move A1:A2 on Sheet1 to B3:B4 on Sheet2.
3025 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3026 bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(1,2,1), true, true, false, true);
3027 CPPUNIT_ASSERT(bMoved);
3029 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=Sheet2.B3"), m_pDoc->GetFormula(1,0,0));
3030 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=Sheet2.B4"), m_pDoc->GetFormula(1,1,0));
3032 // Undo and check again.
3033 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3034 pUndoMgr->Undo();
3036 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=A1"), m_pDoc->GetFormula(1,0,0));
3037 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=A2"), m_pDoc->GetFormula(1,1,0));
3039 // Redo and check.
3040 pUndoMgr->Redo();
3042 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=Sheet2.B3"), m_pDoc->GetFormula(1,0,0));
3043 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", OUString("=Sheet2.B4"), m_pDoc->GetFormula(1,1,0));
3045 m_pDoc->DeleteTab(1);
3046 m_pDoc->DeleteTab(0);
3049 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateDeleteContent)
3051 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3053 m_pDoc->InsertTab(0, "Test");
3055 // Set value in B2.
3056 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3057 // Set formula in C2 to reference B2.
3058 m_pDoc->SetString(ScAddress(2,1,0), "=B2");
3060 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3062 // Delete B2.
3063 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3064 ScMarkData aMark(m_pDoc->GetSheetLimits());
3065 aMark.SetMarkArea(ScAddress(1,1,0));
3066 rFunc.DeleteContents(aMark, InsertDeleteFlags::CONTENTS, true, true);
3068 CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3069 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3071 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3072 CPPUNIT_ASSERT(pUndoMgr);
3074 // Undo and check the result of C2.
3075 pUndoMgr->Undo();
3076 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3077 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3079 // Redo and check.
3080 pUndoMgr->Redo();
3081 CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3082 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3084 m_pDoc->DeleteTab(0);
3087 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateDeleteAndShiftLeft)
3089 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3091 m_pDoc->InsertTab(0, "Test");
3093 // Insert 1,2,3,4,5 in C1:G1.
3094 for (SCCOL i = 0; i <= 4; ++i)
3095 m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3097 // Insert formula in H1.
3098 ScAddress aPos(7,0,0);
3099 m_pDoc->SetString(aPos, "=SUM(C1:G1)");
3101 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3103 // Delete columns D:E (middle of the reference).
3104 ScMarkData aMark(m_pDoc->GetSheetLimits());
3105 aMark.SelectOneTable(0);
3106 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3107 bool bDeleted = rFunc.DeleteCells(ScRange(3,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3108 CPPUNIT_ASSERT(bDeleted);
3110 aPos.IncCol(-2);
3111 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3112 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:E1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3114 // Undo and check.
3115 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3116 CPPUNIT_ASSERT(pUndo);
3118 pUndo->Undo();
3119 aPos.IncCol(2);
3120 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3121 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:G1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3123 // Delete columns C:D (left end of the reference).
3124 bDeleted = rFunc.DeleteCells(ScRange(2,0,0,3,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3125 CPPUNIT_ASSERT(bDeleted);
3127 aPos.IncCol(-2);
3128 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3129 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:E1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3131 // Undo and check again.
3132 pUndo->Undo();
3133 aPos.IncCol(2);
3134 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3135 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:G1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3137 // Delete columns B:E (overlaps on the left).
3138 bDeleted = rFunc.DeleteCells(ScRange(1,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3139 CPPUNIT_ASSERT(bDeleted);
3141 aPos.IncCol(-4);
3142 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3143 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(B1:C1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3145 // Undo and check again.
3146 pUndo->Undo();
3147 aPos.IncCol(4);
3148 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3149 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:G1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3151 // Start over with a new scenario.
3152 clearSheet(m_pDoc, 0);
3154 // Insert 1,2,3,4,5,6 into C1:H1.
3155 for (SCCOL i = 0; i <= 5; ++i)
3156 m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3158 // Set formula in B1.
3159 aPos = ScAddress(1,0,0);
3160 m_pDoc->SetString(aPos, "=SUM(C1:H1)");
3161 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3163 // Delete columns F:H (right end of the reference).
3164 bDeleted = rFunc.DeleteCells(ScRange(5,0,0,7,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3165 CPPUNIT_ASSERT(bDeleted);
3167 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3168 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:E1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3170 // Undo and check.
3171 pUndo->Undo();
3172 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3173 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:H1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3175 // Delete columns G:I (overlaps on the right).
3176 bDeleted = rFunc.DeleteCells(ScRange(6,0,0,8,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3177 CPPUNIT_ASSERT(bDeleted);
3179 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3180 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:F1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3182 // Undo and check again.
3183 pUndo->Undo();
3184 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3185 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(C1:H1)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3187 m_pDoc->DeleteTab(0);
3190 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateDeleteAndShiftLeft2)
3192 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3194 m_pDoc->InsertTab(0, "Test");
3196 std::vector<std::vector<const char*>> aData = {
3197 { "1", "=COUNT($A$1:$A$4)", "=COUNT(A1)" },
3198 { "2", "=COUNT($A$1:$A$4)", "=COUNT(A2)" },
3199 { "3", "=COUNT($A$1:$A$4)", "=COUNT(A3)" },
3200 { "4", "=COUNT($A$1:$A$4)", "=COUNT(A4)" },
3203 insertRangeData(m_pDoc, ScAddress(), aData);
3205 auto funcCheckOriginal = [&]()
3207 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0))); // A1
3208 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0))); // A2
3209 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0))); // A3
3210 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0))); // A4
3212 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,0,0))); // B1
3213 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3214 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0))); // B3
3215 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0))); // B4
3217 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,0,0))); // C1
3218 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3219 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,2,0))); // C3
3220 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,3,0))); // C4
3223 auto funcCheckDeleted = [&]()
3225 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,0,0))); // A1
3226 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,1,0))); // A2
3227 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,2,0))); // A3
3228 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,3,0))); // A4
3230 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,0,0))); // B1
3231 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,1,0))); // B2
3232 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,2,0))); // B3
3233 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,3,0))); // B4
3236 funcCheckOriginal();
3238 // Delete Column A.
3239 ScMarkData aMark(m_pDoc->GetSheetLimits());
3240 aMark.SelectOneTable(0);
3241 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3242 bool bDeleted = rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3243 CPPUNIT_ASSERT(bDeleted);
3245 funcCheckDeleted();
3247 // Undo and check.
3248 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3249 CPPUNIT_ASSERT(pUndo);
3251 pUndo->Undo();
3252 funcCheckOriginal();
3254 // Redo and check.
3255 pUndo->Redo();
3256 funcCheckDeleted();
3258 m_pDoc->DeleteTab(0);
3261 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateDeleteAndShiftUp)
3263 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3265 m_pDoc->InsertTab(0, "Test");
3267 // Insert 1,2,3,4,5 in A3:A7.
3268 for (SCROW i = 0; i <= 4; ++i)
3269 m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3271 // Insert formula in A8.
3272 ScAddress aPos(0,7,0);
3273 m_pDoc->SetString(aPos, "=SUM(A3:A7)");
3275 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3277 // Delete rows 4:5 (middle of the reference).
3278 ScMarkData aMark(m_pDoc->GetSheetLimits());
3279 aMark.SelectOneTable(0);
3280 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3281 bool bDeleted = rFunc.DeleteCells(ScRange(0,3,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3282 CPPUNIT_ASSERT(bDeleted);
3284 aPos.IncRow(-2);
3285 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3286 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A5)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3288 // Undo and check.
3289 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3290 CPPUNIT_ASSERT(pUndo);
3292 pUndo->Undo();
3293 aPos.IncRow(2);
3294 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3295 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A7)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3297 // Delete rows 3:4 (top end of the reference).
3298 bDeleted = rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),3,0), &aMark, DelCellCmd::CellsUp, true);
3299 CPPUNIT_ASSERT(bDeleted);
3301 aPos.IncRow(-2);
3302 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3303 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A5)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3305 // Undo and check again.
3306 pUndo->Undo();
3307 aPos.IncRow(2);
3308 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3309 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A7)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3311 // Delete rows 2:5 (overlaps on the top).
3312 bDeleted = rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3313 CPPUNIT_ASSERT(bDeleted);
3315 aPos.IncRow(-4);
3316 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3317 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A2:A3)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3319 // Undo and check again.
3320 pUndo->Undo();
3321 aPos.IncRow(4);
3322 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3323 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A7)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3325 // Start over with a new scenario.
3326 clearSheet(m_pDoc, 0);
3328 // Insert 1,2,3,4,5,6 into A3:A8.
3329 for (SCROW i = 0; i <= 5; ++i)
3330 m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3332 // Set formula in B1.
3333 aPos = ScAddress(0,1,0);
3334 m_pDoc->SetString(aPos, "=SUM(A3:A8)");
3335 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3337 // Delete rows 6:8 (bottom end of the reference).
3338 bDeleted = rFunc.DeleteCells(ScRange(0,5,0,m_pDoc->MaxCol(),7,0), &aMark, DelCellCmd::CellsUp, true);
3339 CPPUNIT_ASSERT(bDeleted);
3341 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3342 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A5)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3344 // Undo and check.
3345 pUndo->Undo();
3346 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3347 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A8)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3349 // Delete rows 7:9 (overlaps on the bottom).
3350 bDeleted = rFunc.DeleteCells(ScRange(0,6,0,m_pDoc->MaxCol(),8,0), &aMark, DelCellCmd::CellsUp, true);
3351 CPPUNIT_ASSERT(bDeleted);
3353 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3354 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A6)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3356 // Undo and check again.
3357 pUndo->Undo();
3358 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3359 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A3:A8)"), m_pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
3361 m_pDoc->DeleteTab(0);
3364 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateName)
3366 m_pDoc->InsertTab(0, "Formula");
3368 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3370 // Fill C2:C5 with values.
3371 m_pDoc->SetValue(ScAddress(2,1,0), 1);
3372 m_pDoc->SetValue(ScAddress(2,2,0), 2);
3373 m_pDoc->SetValue(ScAddress(2,3,0), 3);
3374 m_pDoc->SetValue(ScAddress(2,4,0), 4);
3376 // Add a named expression that references the immediate left cell.
3377 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
3378 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
3379 ScRangeData* pName = new ScRangeData(
3380 *m_pDoc, "ToLeft", "RC[-1]", ScAddress(2,1,0),
3381 ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
3383 bool bInserted = pGlobalNames->insert(pName);
3384 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3386 // Insert formulas in D2:D5 using the named expression.
3387 m_pDoc->SetString(ScAddress(3,1,0), "=ToLeft");
3388 m_pDoc->SetString(ScAddress(3,2,0), "=ToLeft");
3389 m_pDoc->SetString(ScAddress(3,3,0), "=ToLeft");
3390 m_pDoc->SetString(ScAddress(3,4,0), "=ToLeft");
3392 // Make sure the results are correct.
3393 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3394 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3395 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3396 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3398 // Push cells in column C down by one cell.
3399 m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
3401 // Make sure the results change accordingly.
3402 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(3,1,0));
3403 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,2,0));
3404 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,3,0));
3405 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,4,0));
3407 // Move cells back.
3408 m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
3410 // Make sure the results are back as well.
3411 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3412 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3413 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3414 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3416 // Fill B10:B12 with values.
3417 m_pDoc->SetValue(ScAddress(1,9,0), 10);
3418 m_pDoc->SetValue(ScAddress(1,10,0), 11);
3419 m_pDoc->SetValue(ScAddress(1,11,0), 12);
3421 // Insert a new named expression that references these values as absolute range.
3422 pName = new ScRangeData(
3423 *m_pDoc, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3424 bInserted = pGlobalNames->insert(pName);
3425 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3427 // Set formula at C8 that references this named expression.
3428 m_pDoc->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
3429 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3431 // Shift B10:B12 to right by 2 columns.
3432 m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
3434 // This should shift the absolute range B10:B12 that MyRange references.
3435 pName = pGlobalNames->findByUpperName("MYRANGE");
3436 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3437 OUString aExpr = pName->GetSymbol();
3438 CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr);
3440 // This move shouldn't affect the value of C8.
3441 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,7,0));
3442 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3443 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3445 // Update the value of D10 and make sure C8 gets updated.
3446 m_pDoc->SetValue(ScAddress(3,9,0), 20);
3447 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3449 // Insert a new sheet before the current.
3450 m_pDoc->InsertTab(0, "New");
3451 OUString aName;
3452 m_pDoc->GetName(1, aName);
3453 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3455 pName = pGlobalNames->findByUpperName("MYRANGE");
3456 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3458 m_pDoc->SetValue(ScAddress(3,9,1), 10);
3459 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
3461 // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
3462 m_pDoc->DeleteTab(0);
3464 aName.clear();
3465 m_pDoc->GetName(0, aName);
3466 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3468 pName = pGlobalNames->findByUpperName("MYRANGE");
3469 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3471 m_pDoc->SetValue(ScAddress(3,9,0), 11);
3472 CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3474 // Clear all and start over.
3475 clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
3476 pGlobalNames->clear();
3478 pName = new ScRangeData(
3479 *m_pDoc, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3480 bInserted = pGlobalNames->insert(pName);
3481 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3482 aExpr = pName->GetSymbol();
3483 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3485 // Insert range of cells to shift right. The range partially overlaps the named range.
3486 m_pDoc->InsertCol(ScRange(2,4,0,3,8,0));
3488 // This should not alter the range.
3489 aExpr = pName->GetSymbol();
3490 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3492 m_pDoc->DeleteTab(0);
3495 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateNameMove)
3497 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3499 m_pDoc->InsertTab(0, "Test");
3501 // Set values to B2:B4.
3502 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
3503 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
3504 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
3506 // Set named range for B2:B4.
3507 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
3508 CPPUNIT_ASSERT(bInserted);
3510 // Set formula in A10.
3511 m_pDoc->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
3512 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3514 ScRangeData* pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3515 CPPUNIT_ASSERT(pData);
3516 OUString aSymbol = pData->GetSymbol(m_pDoc->GetGrammar());
3517 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3519 // Move B2:B4 to D3.
3520 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3521 bool bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3522 CPPUNIT_ASSERT(bMoved);
3524 // The named range should have moved as well.
3525 aSymbol = pData->GetSymbol(m_pDoc->GetGrammar());
3526 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3528 // The value of A10 should remain unchanged.
3529 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3531 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3532 CPPUNIT_ASSERT(pUndoMgr);
3534 // Undo and check.
3535 pUndoMgr->Undo();
3537 pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3538 CPPUNIT_ASSERT(pData);
3539 aSymbol = pData->GetSymbol(m_pDoc->GetGrammar());
3540 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3541 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3543 // Redo and check.
3544 pUndoMgr->Redo();
3546 pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3547 CPPUNIT_ASSERT(pData);
3548 aSymbol = pData->GetSymbol(m_pDoc->GetGrammar());
3549 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3550 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3552 // Undo again to bring it back to the initial condition, and clear the undo buffer.
3553 pUndoMgr->Undo();
3554 pUndoMgr->Clear();
3556 // Add an identical formula to A11 and make a formula group over A10:A11.
3557 m_pDoc->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
3558 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,0));
3559 CPPUNIT_ASSERT(pFC);
3560 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
3561 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
3563 // Move B2:B4 to D3 again.
3564 bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3565 CPPUNIT_ASSERT(bMoved);
3567 // Values of A10 and A11 should remain the same.
3568 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3569 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
3571 // Clear and start over.
3572 clearSheet(m_pDoc, 0);
3573 m_pDoc->GetRangeName()->clear();
3575 // Set value to B2.
3576 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3578 // Define B2 as 'MyCell'.
3579 bInserted = m_pDoc->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
3580 CPPUNIT_ASSERT(bInserted);
3582 // Set formula to B3 that references B2 via MyCell.
3583 m_pDoc->SetString(ScAddress(1,2,0), "=MyCell*2");
3584 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3586 // Move B2 to D2.
3587 bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,1,0), ScAddress(3,1,0), true, true, false, true);
3588 CPPUNIT_ASSERT(bMoved);
3590 // Value in B3 should remain unchanged.
3591 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3593 m_pDoc->DeleteTab(0);
3596 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateNameExpandRef)
3598 setExpandRefs(true);
3600 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3602 m_pDoc->InsertTab(0, "Test");
3604 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
3605 CPPUNIT_ASSERT(bInserted);
3607 // Set values to A1:A3.
3608 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3609 m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
3610 m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
3612 m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
3613 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
3615 // Insert a new row at row 4, which should expand the named range to A1:A4.
3616 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3617 ScMarkData aMark(m_pDoc->GetSheetLimits());
3618 aMark.SelectOneTable(0);
3619 rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
3620 ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3621 CPPUNIT_ASSERT(pName);
3622 OUString aSymbol = pName->GetSymbol(m_pDoc->GetGrammar());
3623 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3625 // Make sure the listening area has been expanded as well. Note the
3626 // formula cell has been pushed downward by one cell.
3627 m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
3628 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3630 // Insert a new column at column 2, which should not expand the named
3631 // range as it is only one column wide.
3632 rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
3633 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3634 CPPUNIT_ASSERT(pName);
3635 aSymbol = pName->GetSymbol(m_pDoc->GetGrammar());
3636 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3638 // Make sure the referenced area has not changed.
3639 m_pDoc->SetValue(ScAddress(0,3,0), 2.0);
3640 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3641 m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
3642 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3644 // Clear the document and start over.
3645 m_pDoc->GetRangeName()->clear();
3646 clearSheet(m_pDoc, 0);
3648 // Set values to B4:B6.
3649 m_pDoc->SetValue(ScAddress(1,3,0), 1.0);
3650 m_pDoc->SetValue(ScAddress(1,4,0), 2.0);
3651 m_pDoc->SetValue(ScAddress(1,5,0), 3.0);
3653 bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
3654 CPPUNIT_ASSERT(bInserted);
3656 // Set formula to A1.
3657 m_pDoc->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
3658 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,0,0));
3660 // Insert rows over 3:5 which should expand the range by 3 rows.
3661 rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),4,0), &aMark, INS_INSROWS_BEFORE, false, true);
3663 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3664 CPPUNIT_ASSERT(pName);
3666 aSymbol = pName->GetSymbol(m_pDoc->GetGrammar());
3667 CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
3669 // Clear the document and start over.
3670 m_pDoc->GetRangeName()->clear();
3671 clearSheet(m_pDoc, 0);
3673 // Set values to A1:A3.
3674 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3675 m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
3676 m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
3678 // Name A1:A3 'MyData'.
3679 bInserted = m_pDoc->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
3680 CPPUNIT_ASSERT(bInserted);
3682 // Set formulas to C1:C2 and E1.
3683 m_pDoc->SetString(ScAddress(2,0,0), "=SUM(MyData)");
3684 m_pDoc->SetString(ScAddress(2,1,0), "=SUM(MyData)");
3685 m_pDoc->SetString(ScAddress(4,0,0), "=SUM(MyData)");
3687 // C1:C2 should be shared.
3688 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
3689 CPPUNIT_ASSERT(pFC);
3690 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
3691 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
3693 // E1 should not be shared.
3694 pFC = m_pDoc->GetFormulaCell(ScAddress(4,0,0));
3695 CPPUNIT_ASSERT(pFC);
3696 CPPUNIT_ASSERT(!pFC->IsShared());
3698 // Check the results.
3699 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,0,0)));
3700 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3701 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(4,0,0)));
3703 // Insert a new row at row 3. This should expand MyData to A1:A4.
3704 rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, INS_INSROWS_BEFORE, false, true);
3706 // Set new value to A3.
3707 m_pDoc->SetValue(ScAddress(0,2,0), 4.0);
3709 // Check the results again.
3710 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
3711 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3712 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(4,0,0)));
3714 m_pDoc->DeleteTab(0);
3717 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateNameExpandRef2)
3719 setExpandRefs(true);
3721 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3723 m_pDoc->InsertTab(0, "Test");
3725 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$B$3");
3726 CPPUNIT_ASSERT(bInserted);
3728 // Insert a new row at row 4, which should expand the named range to A1:A4.
3729 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3730 ScMarkData aMark(m_pDoc->GetSheetLimits());
3731 aMark.SelectOneTable(0);
3733 // Insert a new column at column 3, which should expand the named
3734 rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
3735 ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3736 CPPUNIT_ASSERT(pName);
3737 OUString aSymbol = pName->GetSymbol(m_pDoc->GetGrammar());
3738 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$C$3"), aSymbol);
3740 m_pDoc->DeleteTab(0);
3743 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateNameDeleteRow)
3745 m_pDoc->InsertTab(0, "Test");
3747 // Insert a new name 'MyRange' to reference B2:B4.
3748 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$2:$B$4");
3749 CPPUNIT_ASSERT(bInserted);
3751 const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3752 CPPUNIT_ASSERT(pName);
3754 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
3755 const ScTokenArray* pCode = pName->GetCode();
3756 OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3757 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3759 // Insert a new name 'MyAddress' to reference $B$3. Note absolute row.
3760 bInserted = m_pDoc->InsertNewRangeName("MyAddress", ScAddress(0,0,0), "$B$3");
3761 CPPUNIT_ASSERT(bInserted);
3763 const ScRangeData* pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3764 CPPUNIT_ASSERT(pName2);
3766 sc::TokenStringContext aCxt2(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
3767 const ScTokenArray* pCode2 = pName2->GetCode();
3768 OUString aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
3769 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3771 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3773 // Delete row 3.
3774 ScMarkData aMark(m_pDoc->GetSheetLimits());
3775 aMark.SelectOneTable(0);
3776 rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3778 // The reference in the 'MyRange' name should get updated to B2:B3.
3779 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3780 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
3782 // The reference in the 'MyAddress' name should get updated to $B$#REF!.
3783 aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
3784 CPPUNIT_ASSERT_EQUAL(OUString("$B$#REF!"), aExpr2);
3786 // Delete row 3 again.
3787 rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3788 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3789 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
3791 // Undo and check.
3792 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3793 CPPUNIT_ASSERT(pUndoMgr);
3795 pUndoMgr->Undo();
3797 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3798 CPPUNIT_ASSERT(pName);
3799 pCode = pName->GetCode();
3801 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3802 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
3804 // Undo again and check.
3805 pUndoMgr->Undo();
3807 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3808 CPPUNIT_ASSERT(pName);
3809 pCode = pName->GetCode();
3811 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3812 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3814 // Delete row 2-3.
3815 rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
3817 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3818 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
3820 // Undo and check.
3821 pUndoMgr->Undo();
3823 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3824 CPPUNIT_ASSERT(pName);
3825 pCode = pName->GetCode();
3827 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3828 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3830 pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3831 CPPUNIT_ASSERT(pName2);
3832 pCode2 = pName2->GetCode();
3834 aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
3835 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3837 m_pDoc->InsertTab(1, "test2");
3839 ScMarkData aMark2(m_pDoc->GetSheetLimits());
3840 aMark2.SelectOneTable(1);
3841 rFunc.DeleteCells(ScRange(0,2,1,m_pDoc->MaxCol(),2,1), &aMark2, DelCellCmd::CellsUp, true);
3843 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3844 CPPUNIT_ASSERT(pName);
3845 pCode = pName->GetCode();
3847 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
3848 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
3850 pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
3851 CPPUNIT_ASSERT(pName2);
3852 pCode2 = pName2->GetCode();
3854 // Deleting a range the 'MyAddress' name points into due to its implicit
3855 // relative sheet reference to the sheet where used does not invalidate
3856 // the named expression because when updating the sheet reference is
3857 // relative to its base position on sheet 0 (same for the 'MyRange' range,
3858 // which is the reason why it is not updated either).
3859 // This is a tad confusing...
3860 aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
3861 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
3863 m_pDoc->DeleteTab(1);
3864 m_pDoc->DeleteTab(0);
3867 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateNameCopySheet)
3869 m_pDoc->InsertTab(0, "Test");
3870 m_pDoc->InsertTab(1, "Test2");
3872 bool bInserted = m_pDoc->InsertNewRangeName("RED", ScAddress(0,0,0), "$Test.$B$2");
3873 CPPUNIT_ASSERT(bInserted);
3874 bInserted = m_pDoc->InsertNewRangeName("BLUE", ScAddress(0,0,0), "$Test.$B$3");
3875 CPPUNIT_ASSERT(bInserted);
3876 m_pDoc->SetValue(1, 1, 0, 1);
3877 m_pDoc->SetValue(1, 2, 0, 2);
3879 // insert formula into Test2 that is =RED+BLUE
3880 m_pDoc->SetString(ScAddress(2,2,1), "=RED+BLUE");
3882 double nVal = m_pDoc->GetValue(2, 2, 1);
3883 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3884 m_pDoc->CopyTab(1, 0);
3886 nVal = m_pDoc->GetValue(2, 2, 2);
3887 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3889 nVal = m_pDoc->GetValue(2, 2, 0);
3890 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
3892 m_pDoc->SetValue(1, 1, 1, 3);
3894 nVal = m_pDoc->GetValue(2, 2, 2);
3895 CPPUNIT_ASSERT_EQUAL(5.0, nVal);
3897 nVal = m_pDoc->GetValue(2, 2, 0);
3898 CPPUNIT_ASSERT_EQUAL(5.0, nVal);
3900 m_pDoc->DeleteTab(2);
3901 m_pDoc->DeleteTab(1);
3902 m_pDoc->DeleteTab(0);
3904 m_pDoc->InsertTab(0, "Test1");
3905 // Global name referencing sheet Test1.
3906 bInserted = m_pDoc->InsertNewRangeName("sheetnumber", ScAddress(0,0,0), "$Test1.$A$1");
3907 CPPUNIT_ASSERT(bInserted);
3908 m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
3909 m_pDoc->SetString(ScAddress(1,0,0), "=sheetnumber");
3910 nVal = m_pDoc->GetValue(1,0,0);
3911 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet number should be 1", 1.0, nVal);
3913 // Copy sheet after.
3914 m_pDoc->CopyTab(0, 1);
3915 nVal = m_pDoc->GetValue(1,0,1);
3916 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 2", 2.0, nVal);
3917 nVal = m_pDoc->GetValue(1,0,0);
3918 CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 1", 1.0, nVal);
3919 const ScRangeData* pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
3920 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
3922 // Copy sheet before, shifting following now two sheets.
3923 m_pDoc->CopyTab(0, 0);
3924 nVal = m_pDoc->GetValue(1,0,0);
3925 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
3926 pName = m_pDoc->GetRangeName(0)->findByUpperName("SHEETNUMBER");
3927 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
3928 nVal = m_pDoc->GetValue(1,0,1);
3929 CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 2", 2.0, nVal);
3930 pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
3931 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should not exist", !pName);
3932 nVal = m_pDoc->GetValue(1,0,2);
3933 CPPUNIT_ASSERT_EQUAL_MESSAGE("Old sheet number should be 3", 3.0, nVal);
3934 pName = m_pDoc->GetRangeName(2)->findByUpperName("SHEETNUMBER");
3935 CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should exist", pName);
3937 m_pDoc->DeleteTab(2);
3938 m_pDoc->DeleteTab(1);
3939 m_pDoc->DeleteTab(0);
3941 m_pDoc->InsertTab(0, "Test2");
3942 // Local name referencing sheet Test2.
3943 bInserted = m_pDoc->GetRangeName(0)->insert( new ScRangeData( *m_pDoc, "localname", "$Test2.$A$1"));
3944 CPPUNIT_ASSERT(bInserted);
3945 m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
3946 m_pDoc->SetString(ScAddress(1,0,0), "=localname");
3947 nVal = m_pDoc->GetValue(1,0,0);
3948 CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 1", 1.0, nVal);
3950 // Insert sheet before and shift sheet with local name.
3951 m_pDoc->InsertTab(0, "Test1");
3952 pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
3953 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
3954 nVal = m_pDoc->GetValue(1,0,1);
3955 CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 2", 2.0, nVal);
3957 // Copy sheet before, shifting following now two sheets.
3958 m_pDoc->CopyTab(1, 0);
3959 pName = m_pDoc->GetRangeName(0)->findByUpperName("LOCALNAME");
3960 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
3961 nVal = m_pDoc->GetValue(1,0,0);
3962 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
3963 pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
3964 CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should not exist", !pName);
3965 pName = m_pDoc->GetRangeName(2)->findByUpperName("LOCALNAME");
3966 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
3967 nVal = m_pDoc->GetValue(1,0,2);
3968 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 3", 3.0, nVal);
3970 m_pDoc->DeleteTab(2);
3971 m_pDoc->DeleteTab(1);
3972 m_pDoc->DeleteTab(0);
3973 m_pDoc->SetRangeName(nullptr);
3975 // Test nested names during copying sheet.
3977 m_pDoc->InsertTab(0, "Test2");
3978 ScAddress aPos(0,0,0);
3979 bInserted = m_pDoc->InsertNewRangeName( "global", aPos, "$Test2.$A$1");
3980 CPPUNIT_ASSERT(bInserted);
3981 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local", aPos, "$Test2.$A$2");
3982 CPPUNIT_ASSERT(bInserted);
3983 bInserted = m_pDoc->InsertNewRangeName( "global_global", aPos, "global*100");
3984 CPPUNIT_ASSERT(bInserted);
3985 bInserted = m_pDoc->InsertNewRangeName( "global_local", aPos, "local*1000");
3986 CPPUNIT_ASSERT(bInserted);
3987 bInserted = m_pDoc->InsertNewRangeName( "global_unused", aPos, "$Test2.$A$1");
3988 CPPUNIT_ASSERT(bInserted);
3989 bInserted = m_pDoc->InsertNewRangeName( "global_unused_noref", aPos, "42");
3990 CPPUNIT_ASSERT(bInserted);
3991 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_global", aPos, "global*10000");
3992 CPPUNIT_ASSERT(bInserted);
3993 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_local", aPos, "local*100000");
3994 CPPUNIT_ASSERT(bInserted);
3995 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused", aPos, "$Test2.$A$2");
3996 CPPUNIT_ASSERT(bInserted);
3997 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused_noref", aPos, "23");
3998 CPPUNIT_ASSERT(bInserted);
4000 m_pDoc->SetString(aPos, "=SHEET()");
4001 aPos.IncRow();
4002 m_pDoc->SetString(aPos, "=A1*10+SHEET()");
4003 aPos.IncRow();
4004 m_pDoc->SetString(aPos, "=global_global");
4005 aPos.IncRow();
4006 m_pDoc->SetString(aPos, "=global_local");
4007 aPos.IncRow();
4008 m_pDoc->SetString(aPos, "=local_global");
4009 aPos.IncRow();
4010 m_pDoc->SetString(aPos, "=local_local");
4012 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, false);
4014 // Copy sheet after.
4015 m_pDoc->CopyTab(0, 1);
4016 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, false);
4017 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 1, true);
4019 // Copy sheet before, shifting following now two sheets.
4020 m_pDoc->CopyTab(1, 0);
4021 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, true);
4022 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 1, false);
4023 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 2, true);
4025 m_pDoc->DeleteTab(2);
4026 m_pDoc->DeleteTab(1);
4027 m_pDoc->DeleteTab(0);
4030 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateSheetLocalMove)
4032 SCTAB nSheet1 = 0;
4033 SCTAB nSheet2 = 1;
4034 m_pDoc->InsertTab( nSheet1, "Sheet1");
4035 m_pDoc->InsertTab( nSheet2, "Sheet2");
4037 ScAddress aPos(0,0,nSheet1);
4038 bool bOk;
4039 bOk = m_pDoc->InsertNewRangeName( 0, "MyCell", aPos, "$Sheet1.$B$2");
4040 CPPUNIT_ASSERT(bOk);
4041 aPos.SetTab(nSheet2);
4042 bOk = m_pDoc->InsertNewRangeName( 1, "MyCell", aPos, "$Sheet2.$B$2");
4043 CPPUNIT_ASSERT(bOk);
4045 aPos.SetTab(nSheet1);
4046 aPos.IncCol();
4047 m_pDoc->SetString( aPos, "x");
4048 aPos.IncRow();
4049 m_pDoc->SetString( aPos, "1.0");
4050 aPos.IncRow();
4051 m_pDoc->SetString( aPos, "=MyCell");
4052 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet1.B3", 1.0, m_pDoc->GetValue(aPos));
4054 aPos.SetTab(nSheet2);
4055 aPos.SetRow(1);
4056 m_pDoc->SetString( aPos, "2.0");
4057 aPos.IncRow();
4058 m_pDoc->SetString( aPos, "=MyCell");
4059 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet2.B3", 2.0, m_pDoc->GetValue(aPos));
4061 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4062 OUString aFormula;
4064 // Move Sheet1.B1 ("x") to Sheet2.B1
4065 bOk = rFunc.MoveBlock( ScRange(1,0,nSheet1,1,0,nSheet1), ScAddress(1,0,nSheet2), true, false, false, false);
4066 CPPUNIT_ASSERT(bOk);
4067 // Results not changed.
4068 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4069 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet2.B3", 2.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4070 // Formulas not changed.
4071 aFormula = m_pDoc->GetFormula( 1,2,nSheet1);
4072 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet1.B3", OUString("=MyCell"), aFormula);
4073 aFormula = m_pDoc->GetFormula( 1,2,nSheet2);
4074 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet2.B3", OUString("=MyCell"), aFormula);
4076 // Move Sheet2.B2 ("2.0") to Sheet1.C2
4077 bOk = rFunc.MoveBlock( ScRange(1,1,nSheet2,1,1,nSheet2), ScAddress(2,1,nSheet1), true, false, false, false);
4078 CPPUNIT_ASSERT(bOk);
4079 // Results not changed.
4080 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4081 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2.B3", 2.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4082 // Formulas not changed.
4083 aFormula = m_pDoc->GetFormula( 1,2,nSheet1);
4084 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet1.B3", OUString("=MyCell"), aFormula);
4085 aFormula = m_pDoc->GetFormula( 1,2,nSheet2);
4086 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2.B3", OUString("=MyCell"), aFormula);
4088 ScRangeData* pName;
4090 // Check that the sheet-local named reference points to the moved cell, now
4091 // Sheet1.C2
4092 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4093 CPPUNIT_ASSERT(pName);
4094 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4095 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4097 // Move Sheet2.B3 ("=MyCell") to Sheet1.C3
4098 bOk = rFunc.MoveBlock( ScRange(1,2,nSheet2,1,2,nSheet2), ScAddress(2,2,nSheet1), true, false, false, false);
4099 CPPUNIT_ASSERT(bOk);
4100 // Results changed.
4101 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4102 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet2.B3", 0.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4103 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4104 // One formula identical, one adjusted.
4105 aFormula = m_pDoc->GetFormula( 1,2,nSheet1);
4106 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.B3", OUString("=MyCell"), aFormula);
4107 aFormula = m_pDoc->GetFormula( 2,2,nSheet1);
4108 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4110 // Check that the sheet-local named reference in Sheet1 still points to the
4111 // original cell Sheet1.B2
4112 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4113 CPPUNIT_ASSERT(pName);
4114 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4115 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4117 // Check that the sheet-local named reference in Sheet2 still points to the
4118 // moved cell, now Sheet1.C2
4119 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4120 CPPUNIT_ASSERT(pName);
4121 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4122 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4124 // Insert sheet before the others.
4125 m_pDoc->InsertTab(0, "Sheet0");
4126 ++nSheet1;
4127 ++nSheet2;
4129 // Nothing changed.
4130 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4131 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4132 aFormula = m_pDoc->GetFormula( 1,2,nSheet1);
4133 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.B3", OUString("=MyCell"), aFormula);
4134 aFormula = m_pDoc->GetFormula( 2,2,nSheet1);
4135 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4136 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4137 CPPUNIT_ASSERT(pName);
4138 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4139 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4140 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4141 CPPUNIT_ASSERT(pName);
4142 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4143 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4145 // Delete sheet before the others.
4146 m_pDoc->DeleteTab(0);
4147 --nSheet1;
4148 --nSheet2;
4150 // Nothing changed.
4151 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4152 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4153 aFormula = m_pDoc->GetFormula( 1,2,nSheet1);
4154 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.B3", OUString("=MyCell"), aFormula);
4155 aFormula = m_pDoc->GetFormula( 2,2,nSheet1);
4156 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4157 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4158 CPPUNIT_ASSERT(pName);
4159 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4160 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4161 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4162 CPPUNIT_ASSERT(pName);
4163 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4164 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4166 // Delete last sheet with sheet-local name.
4167 m_pDoc->DeleteTab(nSheet2);
4169 // XXX we *could* analyze whether the expression points to a different
4170 // sheet and then move the name to a remaining sheet. If so, adapt this
4171 // test.
4172 // Nothing changed except the sheet-local name and its use.
4173 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4174 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", 0.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4175 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", OUString("#NAME?"), m_pDoc->GetString(ScAddress(2,2,nSheet1)));
4176 aFormula = m_pDoc->GetFormula( 1,2,nSheet1);
4177 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.B3", OUString("=MyCell"), aFormula);
4178 aFormula = m_pDoc->GetFormula( 2,2,nSheet1);
4179 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", OUString("=#NAME?"), aFormula);
4180 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4181 CPPUNIT_ASSERT(pName);
4182 aFormula = pName->GetSymbol( ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4183 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4184 CPPUNIT_ASSERT(!m_pDoc->GetRangeName(nSheet2));
4185 nSheet2 = -1;
4187 m_pDoc->DeleteTab(0);
4190 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateNameDelete)
4192 m_pDoc->InsertTab(0, "Test");
4194 // Insert a new name 'MyRange' to reference B1
4195 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$1");
4196 CPPUNIT_ASSERT(bInserted);
4198 const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4199 CPPUNIT_ASSERT(pName);
4201 m_pDoc->DeleteCol(1, 0, 3, 0, 0, 1);
4202 const ScTokenArray* pCode = pName->GetCode();
4203 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4204 OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4205 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$1"), aExpr);
4207 m_pDoc->DeleteTab(0);
4210 CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaRefUpdateValidity)
4212 struct {
4214 bool checkList( std::vector<ScTypedStrData>& rList )
4216 double aExpected[] = { 1.0, 2.0, 3.0 }; // must be sorted.
4217 size_t nCheckSize = SAL_N_ELEMENTS(aExpected);
4219 if (rList.size() != nCheckSize)
4221 cerr << "List size is not what is expected." << endl;
4222 return false;
4225 std::sort(rList.begin(), rList.end(), ScTypedStrData::LessCaseSensitive());
4227 for (size_t i = 0; i < nCheckSize; ++i)
4229 if (aExpected[i] != rList[i].GetValue())
4231 cerr << "Incorrect value at position " << i
4232 << ": expected=" << aExpected[i] << ", actual=" << rList[i].GetValue() << endl;
4233 return false;
4237 return true;
4240 } aCheck;
4242 setExpandRefs(false);
4243 setCalcAsShown(m_pDoc, true);
4245 m_pDoc->InsertTab(0, "Formula");
4247 // Set values in C2:C4.
4248 m_pDoc->SetValue(ScAddress(2,1,0), 1.0);
4249 m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
4250 m_pDoc->SetValue(ScAddress(2,3,0), 3.0);
4252 // Set validity in A2.
4253 ScValidationData aData(
4254 SC_VALID_LIST, ScConditionMode::Equal, "C2:C4", "", *m_pDoc, ScAddress(0,1,0), "", "",
4255 m_pDoc->GetGrammar(), m_pDoc->GetGrammar());
4257 sal_uInt32 nIndex = m_pDoc->AddValidationEntry(aData);
4258 SfxUInt32Item aItem(ATTR_VALIDDATA, nIndex);
4260 ScPatternAttr aNewAttrs(
4261 SfxItemSet(*m_pDoc->GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>));
4262 aNewAttrs.GetItemSet().Put(aItem);
4264 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
4266 const ScValidationData* pData = m_pDoc->GetValidationEntry(nIndex);
4267 CPPUNIT_ASSERT(pData);
4269 // Make sure the list is correct.
4270 std::vector<ScTypedStrData> aList;
4271 pData->FillSelectionList(aList, ScAddress(0,1,0));
4272 bool bGood = aCheck.checkList(aList);
4273 CPPUNIT_ASSERT_MESSAGE("Initial list is incorrect.", bGood);
4275 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4276 ScMarkData aMark(m_pDoc->GetSheetLimits());
4277 aMark.SelectOneTable(0);
4279 // Insert a new column at Column B, to move the list from C2:C4 to D2:D4.
4280 bool bInserted = rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, true, true);
4281 CPPUNIT_ASSERT_MESSAGE("Column insertion failed.", bInserted);
4282 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4283 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4284 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,3,0)));
4286 // Check the list values again.
4287 aList.clear();
4288 pData->FillSelectionList(aList, ScAddress(0,1,0));
4289 bGood = aCheck.checkList(aList);
4290 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after column insertion.", bGood);
4292 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4293 CPPUNIT_ASSERT(pUndoMgr);
4295 // Undo and check the list content again. The list moves back to C2:C4 after the undo.
4296 pUndoMgr->Undo();
4297 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4298 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4299 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4301 aList.clear();
4302 pData->FillSelectionList(aList, ScAddress(0,1,0));
4303 bGood = aCheck.checkList(aList);
4304 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of column insertion.", bGood);
4306 // Move C2:C4 to E5:E7.
4307 bool bMoved = rFunc.MoveBlock(ScRange(2,1,0,2,3,0), ScAddress(4,4,0), false, true, false, true);
4308 CPPUNIT_ASSERT(bMoved);
4309 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4,4,0)));
4310 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,5,0)));
4311 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(4,6,0)));
4313 // Check the list again after the move.
4314 aList.clear();
4315 pData->FillSelectionList(aList, ScAddress(0,1,0));
4316 bGood = aCheck.checkList(aList);
4317 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after moving C2:C4 to E5:E7.", bGood);
4319 // Undo the move and check. The list should be back to C2:C4.
4320 pUndoMgr->Undo();
4321 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4322 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4323 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4325 aList.clear();
4326 pData->FillSelectionList(aList, ScAddress(0,1,0));
4327 bGood = aCheck.checkList(aList);
4328 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of the move.", bGood);
4330 m_pDoc->DeleteTab(0);
4333 CPPUNIT_TEST_FIXTURE(TestFormula, testTokenArrayRefUpdateMove)
4335 m_pDoc->InsertTab(0, "Sheet1");
4336 m_pDoc->InsertTab(1, "Sheet2");
4338 ScAddress aPos(0,0,0); // A1
4340 sc::TokenStringContext aCxt(*m_pDoc, m_pDoc->GetGrammar());
4342 // Emulate cell movement from Sheet1.C3 to Sheet2.C3.
4343 sc::RefUpdateContext aRefCxt(*m_pDoc);
4344 aRefCxt.meMode = URM_MOVE;
4345 aRefCxt.maRange = ScAddress(2,2,1); // C3 on Sheet2.
4346 aRefCxt.mnTabDelta = -1;
4348 std::vector<OUString> aTests = {
4349 "B1*C1",
4350 "SUM(B1:C1)",
4351 "$Sheet1.B1",
4352 "SUM(Sheet1.B1:Sheet2.B1)"
4355 // Since C3 is not referenced in any of the above formulas, moving C3 from
4356 // Sheet1 to Sheet2 should NOT change the displayed formula string at all.
4358 for (const OUString& aTest : aTests)
4360 ScCompiler aComp(*m_pDoc, aPos, m_pDoc->GetGrammar());
4361 std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aTest));
4363 OUString aStr = pArray->CreateString(aCxt, aPos);
4365 CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4367 // This formula cell isn't moving its position. The displayed formula
4368 // string should not change.
4369 pArray->AdjustReferenceOnMove(aRefCxt, aPos, aPos);
4371 aStr = pArray->CreateString(aCxt, aPos);
4372 CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4375 m_pDoc->DeleteTab(1);
4376 m_pDoc->DeleteTab(0);
4379 CPPUNIT_TEST_FIXTURE(TestFormula, testMultipleOperations)
4381 m_pDoc->InsertTab(0, "MultiOp");
4383 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4385 // Insert the reference formula at top row.
4386 m_pDoc->SetValue(ScAddress(0,0,0), 1);
4387 m_pDoc->SetString(ScAddress(1,0,0), "=A1*10");
4388 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4390 // Insert variable inputs in A3:A5.
4391 m_pDoc->SetValue(ScAddress(0,2,0), 2);
4392 m_pDoc->SetValue(ScAddress(0,3,0), 3);
4393 m_pDoc->SetValue(ScAddress(0,4,0), 4);
4395 // Set multiple operations range.
4396 ScTabOpParam aParam;
4397 aParam.aRefFormulaCell = ScRefAddress(1,0,0);
4398 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4399 aParam.aRefColCell = ScRefAddress(0,0,0);
4400 ScMarkData aMark(m_pDoc->GetSheetLimits());
4401 aMark.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
4402 m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4403 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(1,2,0));
4404 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,3,0));
4405 CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,4,0));
4407 // Clear A3:B5.
4408 clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
4410 // This time, use indirect reference formula cell.
4411 m_pDoc->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
4412 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4414 // Insert variable inputs in A3:A5.
4415 m_pDoc->SetValue(ScAddress(0,2,0), 3);
4416 m_pDoc->SetValue(ScAddress(0,3,0), 4);
4417 m_pDoc->SetValue(ScAddress(0,4,0), 5);
4419 // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
4420 aParam.aRefFormulaCell.Set(2,0,0,false,false,false);
4421 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4422 m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4423 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,2,0));
4424 CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,3,0));
4425 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(1,4,0));
4427 m_pDoc->DeleteTab(0);
4430 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncCOLUMN)
4432 m_pDoc->InsertTab(0, "Formula");
4433 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4435 m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
4436 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4438 m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4439 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4441 // Move the formula cell with COLUMN() function to change its value.
4442 m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4443 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(6,10,0)));
4445 // The cell that references the moved cell should update its value as well.
4446 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4448 // Move the column in the other direction.
4449 m_pDoc->DeleteCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4451 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4453 // The cell that references the moved cell should update its value as well.
4454 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4456 m_pDoc->DeleteTab(0);
4459 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncCOUNT)
4461 m_pDoc->InsertTab(0, "Formula");
4462 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4464 m_pDoc->SetValue(ScAddress(0,0,0), 2); // A1
4465 m_pDoc->SetValue(ScAddress(0,1,0), 4); // A2
4466 m_pDoc->SetValue(ScAddress(0,2,0), 6); // A3
4468 ScAddress aPos(1,0,0);
4469 m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
4470 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
4472 aPos.IncRow();
4473 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
4474 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
4476 aPos.IncRow();
4477 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
4478 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
4480 aPos.IncRow();
4481 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
4482 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
4484 // Matrix in C1.
4485 ScMarkData aMark(m_pDoc->GetSheetLimits());
4486 aMark.SelectOneTable(0);
4487 m_pDoc->InsertMatrixFormula(2, 0, 2, 0, aMark, "=COUNT(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4488 // Check that the #VALUE! error of "a" not found in "b" is not counted.
4489 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4491 // Matrix in C3.
4492 m_pDoc->InsertMatrixFormula(2, 2, 2, 2, aMark, "=COUNTA(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4493 // Check that the #VALUE! error of "a" not found in "b" is counted.
4494 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4496 m_pDoc->DeleteTab(0);
4499 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncCOUNTBLANK)
4501 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4502 m_pDoc->InsertTab(0, "Formula");
4504 std::vector<std::vector<const char*>> aData = {
4505 { "1", nullptr, "=B1", "=\"\"" },
4506 { "2", nullptr, "=B2", "=\"\"" },
4507 { "A", nullptr, "=B3", "=\"\"" },
4508 { "B", nullptr, "=B4", "=D3" },
4509 { nullptr, nullptr, "=B5", "=D4" },
4510 { "=COUNTBLANK(A1:A5)", "=COUNTBLANK(B1:B5)", "=COUNTBLANK(C1:C5)", "=COUNTBLANK(D1:D5)" }
4513 ScAddress aPos(0,0,0);
4514 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
4515 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4517 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4518 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,5,0)));
4519 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,5,0)));
4520 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(3,5,0)));
4522 // Test single cell reference cases.
4524 clearSheet(m_pDoc, 0);
4526 std::vector<std::vector<const char*>> aData2 = {
4527 { "1", "=COUNTBLANK(A1)" },
4528 { "A", "=COUNTBLANK(A2)" },
4529 { nullptr, "=COUNTBLANK(A3)" },
4530 { "=\"\"", "=COUNTBLANK(A4)" },
4531 { "=A4" , "=COUNTBLANK(A5)" },
4534 aRange = insertRangeData(m_pDoc, aPos, aData2);
4535 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4537 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4538 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4539 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4540 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
4541 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
4543 m_pDoc->DeleteTab(0);
4546 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncROW)
4548 m_pDoc->InsertTab(0, "Formula");
4549 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4551 m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
4552 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4554 m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4555 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4557 // Insert 2 new rows at row 4.
4558 m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4559 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(5,12,0)));
4561 // The cell that references the moved cell should update its value as well.
4562 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4564 // Delete 2 rows to move it back.
4565 m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4567 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4569 // The cell that references the moved cell should update its value as well.
4570 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4572 // Clear sheet and start over.
4573 clearSheet(m_pDoc, 0);
4575 m_pDoc->SetString(ScAddress(0,1,0), "=ROW(A5)");
4576 m_pDoc->SetString(ScAddress(1,1,0), "=ROW(B5)");
4577 m_pDoc->SetString(ScAddress(1,2,0), "=ROW(B6)");
4578 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4579 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4580 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4582 // B2:B3 should be shared.
4583 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
4584 CPPUNIT_ASSERT(pFC);
4585 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
4586 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
4588 // Insert a new row at row 4.
4589 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4590 ScMarkData aMark(m_pDoc->GetSheetLimits());
4591 aMark.SelectOneTable(0);
4592 rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
4593 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=ROW(A6)"), m_pDoc->GetFormula(0,1,0));
4594 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=ROW(B6)"), m_pDoc->GetFormula(1,1,0));
4595 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=ROW(B7)"), m_pDoc->GetFormula(1,2,0));
4597 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4598 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4599 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4601 m_pDoc->DeleteTab(0);
4604 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncSUM)
4606 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4607 m_pDoc->InsertTab (0, "foo"));
4609 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
4611 // Single argument case.
4612 m_pDoc->SetValue(ScAddress(0,0,0), 1);
4613 m_pDoc->SetValue(ScAddress(0,1,0), 1);
4614 m_pDoc->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
4615 m_pDoc->CalcAll();
4616 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4618 // Multiple argument case.
4619 m_pDoc->SetValue(ScAddress(0,0,0), 1);
4620 m_pDoc->SetValue(ScAddress(0,1,0), 22);
4621 m_pDoc->SetValue(ScAddress(0,2,0), 4);
4622 m_pDoc->SetValue(ScAddress(0,3,0), 5);
4623 m_pDoc->SetValue(ScAddress(0,4,0), 6);
4625 m_pDoc->SetValue(ScAddress(1,0,0), 3);
4626 m_pDoc->SetValue(ScAddress(1,1,0), 4);
4627 m_pDoc->SetValue(ScAddress(1,2,0), 5);
4628 m_pDoc->SetValue(ScAddress(1,3,0), 6);
4629 m_pDoc->SetValue(ScAddress(1,4,0), 7);
4631 m_pDoc->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
4632 m_pDoc->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
4633 m_pDoc->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
4634 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(ScAddress(3,0,0)));
4635 CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4636 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4638 // Clear and start over.
4639 clearRange(m_pDoc, ScRange(0,0,0,3,m_pDoc->MaxRow(),0));
4641 // SUM needs to take the first error in case the range contains an error.
4642 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4643 m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
4644 m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
4645 m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
4646 CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4648 // Set #DIV/0! error to A3. A4 should also inherit this error.
4649 m_pDoc->SetString(ScAddress(0,2,0), "=1/0");
4650 FormulaError nErr = m_pDoc->GetErrCode(ScAddress(0,2,0));
4651 CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell should have a division by zero error.",
4652 int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4653 nErr = m_pDoc->GetErrCode(ScAddress(0,3,0));
4654 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM should have also inherited a div-by-zero error.",
4655 int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4657 // Set #NA! to A2. A4 should now inherit this error.
4658 m_pDoc->SetString(ScAddress(0,1,0), "=NA()");
4659 nErr = m_pDoc->GetErrCode(ScAddress(0,1,0));
4660 CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr != FormulaError::NONE);
4661 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should have inherited the same error as A2.",
4662 static_cast<int>(nErr), static_cast<int>(m_pDoc->GetErrCode(ScAddress(0,3,0))));
4664 // Test the dreaded 0.1 + 0.2 - 0.3 != 0.0
4665 m_pDoc->SetString(ScAddress(1,0,0), "=SUM(0.1;0.2;-0.3)");
4666 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4667 // Also for +/- operators
4668 m_pDoc->SetString(ScAddress(1,1,0), "=0.1+0.2-0.3");
4669 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4671 m_pDoc->DeleteTab(0);
4674 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncPRODUCT)
4676 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4678 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab(0, "foo"));
4680 ScAddress aPos(3, 0, 0);
4681 m_pDoc->SetValue(0, 0, 0, 3.0); // A1
4682 m_pDoc->SetString(aPos, "=PRODUCT(A1)");
4683 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 3.0, m_pDoc->GetValue(aPos));
4684 m_pDoc->SetValue(0, 0, 0, -3.0); // A1
4685 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -3.0, m_pDoc->GetValue(aPos));
4686 m_pDoc->SetString(aPos, "=PRODUCT(B1)");
4687 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos));
4688 m_pDoc->SetValue(1, 0, 0, 10.0); // B1
4689 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 10.0, m_pDoc->GetValue(aPos));
4691 m_pDoc->SetString(aPos, "=PRODUCT(A1:C3)");
4692 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -30.0, m_pDoc->GetValue(aPos));
4693 m_pDoc->SetValue(1, 1, 0, -1.0); // B2
4694 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 30.0, m_pDoc->GetValue(aPos));
4695 m_pDoc->SetValue(2, 0, 0, 4.0); // C1
4696 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 120.0, m_pDoc->GetValue(aPos));
4697 m_pDoc->SetValue(0, 1, 0, -2.0); // A2
4698 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -240.0, m_pDoc->GetValue(aPos));
4699 m_pDoc->SetValue(2, 1, 0, 8.0); // C2
4700 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -1920.0, m_pDoc->GetValue(aPos));
4701 m_pDoc->SetValue(0, 2, 0, 0.2); // A3
4702 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -384.0, m_pDoc->GetValue(aPos), 10e-4);
4703 m_pDoc->SetValue(1, 2, 0, -0.25); // B3
4704 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 96.0, m_pDoc->GetValue(aPos), 10e-4);
4705 m_pDoc->SetValue(2, 2, 0, -0.125); // C3
4706 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -12.0, m_pDoc->GetValue(aPos), 10e-4);
4707 m_pDoc->SetValue(2, 2, 0, 0.0); // C3
4708 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos), 10e-4);
4710 m_pDoc->SetString(aPos, "=PRODUCT({2;3;4})");
4711 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 24.0, m_pDoc->GetValue(aPos));
4712 m_pDoc->SetString(aPos, "=PRODUCT({2;-2;2})");
4713 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -8.0, m_pDoc->GetValue(aPos));
4714 m_pDoc->SetString(aPos, "=PRODUCT({8;0.125;-1})");
4715 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -1.0, m_pDoc->GetValue(aPos));
4717 m_pDoc->SetString(aPos, "=PRODUCT({2;3};{4;5})");
4718 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 120.0, m_pDoc->GetValue(aPos));
4719 m_pDoc->SetString(aPos, "=PRODUCT({10;-8};{3;-1};{15;30};{7})");
4720 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 756000.0, m_pDoc->GetValue(aPos));
4721 m_pDoc->SetString(aPos, "=PRODUCT({10;-0.1;8};{0.125;4;0.25;2};{0.5};{1};{-1})");
4722 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 1.0, m_pDoc->GetValue(aPos));
4724 m_pDoc->DeleteTab(0);
4727 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncSUMPRODUCT)
4729 m_pDoc->InsertTab(0, "Test");
4731 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4733 ScAddress aPos(0,0,0);
4734 m_pDoc->SetString(aPos, "=SUMPRODUCT(B1:B3;C1:C3)");
4735 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
4736 m_pDoc->SetValue(ScAddress(2,0,0), 1.0); // C1
4737 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
4738 m_pDoc->SetValue(ScAddress(1,0,0), 1.0); // B1
4739 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
4740 m_pDoc->SetValue(ScAddress(1,1,0), 2.0); // B2
4741 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
4742 m_pDoc->SetValue(ScAddress(2,1,0), 3.0); // C2
4743 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(aPos));
4744 m_pDoc->SetValue(ScAddress(2,2,0), -2.0); // C3
4745 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(aPos));
4746 m_pDoc->SetValue(ScAddress(1,2,0), 5.0); // B3
4747 CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc->GetValue(aPos));
4749 // Force an error in C2 and test ForcedArray matrix error propagation.
4750 m_pDoc->SetString( 2, 1, 0, "=1/0");
4751 FormulaError nError = m_pDoc->GetErrCode(aPos);
4752 CPPUNIT_ASSERT_MESSAGE("Formula result should be a propagated error", nError != FormulaError::NONE);
4754 // Test ForceArray propagation of SUMPRODUCT parameters to ABS and + operator.
4755 // => ABS({-3,4})*({-3,4}+{-3,4}) => {3,4}*{-6,8} => {-18,32} => 14
4756 m_pDoc->SetValue(ScAddress(4,0,0), -3.0); // E1
4757 m_pDoc->SetValue(ScAddress(4,1,0), 4.0); // E2
4758 // Non-intersecting formula in F3.
4759 m_pDoc->SetString(ScAddress(5,2,0), "=SUMPRODUCT(ABS(E1:E2);E1:E2+E1:E2)");
4760 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(ScAddress(5,2,0)));
4762 m_pDoc->DeleteTab(0);
4765 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncSUMXMY2)
4767 m_pDoc->InsertTab(0, "Test SumXMY2");
4769 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4771 ScAddress aPos(0,0,0);
4772 m_pDoc->SetString(aPos, "=SUMXMY2(B1:B3;C1:C3)");
4773 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
4774 m_pDoc->SetValue(ScAddress(1,0,0), 1.0); // B1
4775 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
4776 m_pDoc->SetValue(ScAddress(1,1,0), 2.0); // B2
4777 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
4778 m_pDoc->SetValue(ScAddress(1,2,0), 3.0); // B3
4779 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(aPos));
4780 m_pDoc->SetValue(ScAddress(2,0,0), -1.0); // C1
4781 CPPUNIT_ASSERT_EQUAL(17.0, m_pDoc->GetValue(aPos));
4782 m_pDoc->SetValue(ScAddress(2,1,0), 3.0); // C2
4783 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(aPos));
4784 m_pDoc->SetValue(ScAddress(2,2,0), 1.0); // C3
4785 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
4787 m_pDoc->SetString(0, 4, 0, "=SUMXMY2({2;3;4};{4;3;2})");
4788 double result = m_pDoc->GetValue(0, 4, 0);
4789 ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of SUMXMY2 with inline arrays failed", 8.0, result);
4791 m_pDoc->DeleteTab(0);
4794 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncMIN)
4796 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
4797 m_pDoc->InsertTab(0, "Formula");
4799 // A1:A2
4800 m_pDoc->SetString(ScAddress(0,0,0), "a");
4801 m_pDoc->SetString(ScAddress(0,1,0), "b");
4803 // B1:B2
4804 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
4805 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
4807 // Matrix in C1:C2.
4808 ScMarkData aMark(m_pDoc->GetSheetLimits());
4809 aMark.SelectOneTable(0);
4810 m_pDoc->InsertMatrixFormula(2, 0, 2, 1, aMark, "=MIN(IF(A1:A2=\"c\";B1:B2))");
4812 // Formula cell in C1:C2 should be a 1x2 matrix array.
4813 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
4814 CPPUNIT_ASSERT(pFC);
4815 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be an array.", ScMatrixMode::Formula, pFC->GetMatrixFlag());
4817 SCCOL nCols;
4818 SCROW nRows;
4819 pFC->GetMatColsRows(nCols, nRows);
4820 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
4821 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRows);
4823 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C1 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,0,0))));
4824 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C2 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,1,0))));
4826 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4827 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4829 // Inline array input (A4).
4830 m_pDoc->SetString(ScAddress(0,3,0), "=MIN({-2;4;3})");
4831 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4833 // Add more values to B3:B4.
4834 m_pDoc->SetValue(ScAddress(1,2,0), 20.0);
4835 m_pDoc->SetValue(ScAddress(1,3,0), -20.0);
4837 // Get the MIN of B1:B4.
4838 m_pDoc->SetString(ScAddress(2,4,0), "=MIN(B1:B4)");
4839 CPPUNIT_ASSERT_EQUAL(-20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
4841 m_pDoc->DeleteTab(0);
4844 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncN)
4846 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4847 m_pDoc->InsertTab (0, "foo"));
4849 double result;
4851 // Clear the area first.
4852 clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
4854 // Put values to reference.
4855 double val = 0;
4856 m_pDoc->SetValue(0, 0, 0, val);
4857 m_pDoc->SetString(0, 2, 0, "Text");
4858 val = 1;
4859 m_pDoc->SetValue(0, 3, 0, val);
4860 val = -1;
4861 m_pDoc->SetValue(0, 4, 0, val);
4862 val = 12.3;
4863 m_pDoc->SetValue(0, 5, 0, val);
4864 m_pDoc->SetString(0, 6, 0, "'12.3");
4866 // Cell references
4867 m_pDoc->SetString(1, 0, 0, "=N(A1)");
4868 m_pDoc->SetString(1, 1, 0, "=N(A2)");
4869 m_pDoc->SetString(1, 2, 0, "=N(A3)");
4870 m_pDoc->SetString(1, 3, 0, "=N(A4)");
4871 m_pDoc->SetString(1, 4, 0, "=N(A5)");
4872 m_pDoc->SetString(1, 5, 0, "=N(A6)");
4873 m_pDoc->SetString(1, 6, 0, "=N(A9)");
4875 // In-line values
4876 m_pDoc->SetString(1, 7, 0, "=N(0)");
4877 m_pDoc->SetString(1, 8, 0, "=N(1)");
4878 m_pDoc->SetString(1, 9, 0, "=N(-1)");
4879 m_pDoc->SetString(1, 10, 0, "=N(123)");
4880 m_pDoc->SetString(1, 11, 0, "=N(\"\")");
4881 m_pDoc->SetString(1, 12, 0, "=N(\"12\")");
4882 m_pDoc->SetString(1, 13, 0, "=N(\"foo\")");
4884 // Range references
4885 m_pDoc->SetString(2, 2, 0, "=N(A1:A8)");
4886 m_pDoc->SetString(2, 3, 0, "=N(A1:A8)");
4887 m_pDoc->SetString(2, 4, 0, "=N(A1:A8)");
4888 m_pDoc->SetString(2, 5, 0, "=N(A1:A8)");
4890 // Calculate and check the results.
4891 m_pDoc->CalcAll();
4892 double checks1[] = {
4893 0, 0, 0, 1, -1, 12.3, 0, // cell reference
4894 0, 1, -1, 123, 0, 0, 0 // in-line values
4896 for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
4898 result = m_pDoc->GetValue(1, i, 0);
4899 bool bGood = result == checks1[i];
4900 if (!bGood)
4902 cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
4903 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4906 double checks2[] = {
4907 0, 1, -1, 12.3 // range references
4909 for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
4911 result = m_pDoc->GetValue(1, i+2, 0);
4912 bool bGood = result == checks2[i];
4913 if (!bGood)
4915 cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
4916 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4920 m_pDoc->DeleteTab(0);
4923 CPPUNIT_TEST_FIXTURE(TestFormula, testFuncCOUNTIF)
4925 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4927 // COUNTIF (test case adopted from OOo i#36381)
4929 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4930 m_pDoc->InsertTab (0, "foo"));
4932 // Empty A1:A39 first.
4933 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
4935 // Raw data (rows 1 through 9)
4936 const char* aData[] = {
4937 "1999",
4938 "2000",
4939 "0",
4940 "0",
4941 "0",
4942 "2002",
4943 "2001",
4944 "X",
4945 "2002"
4948 SCROW nRows = SAL_N_ELEMENTS(aData);
4949 for (SCROW i = 0; i < nRows; ++i)
4950 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
4952 printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
4954 // formulas and results
4955 static const struct {
4956 const char* pFormula; double fResult;
4957 } aChecks[] = {
4958 { "=COUNTIF(A1:A12;1999)", 1 },
4959 { "=COUNTIF(A1:A12;2002)", 2 },
4960 { "=COUNTIF(A1:A12;1998)", 0 },
4961 { "=COUNTIF(A1:A12;\">=1999\")", 5 },
4962 { "=COUNTIF(A1:A12;\">1999\")", 4 },
4963 { "=COUNTIF(A1:A12;\"<2001\")", 5 },
4964 { "=COUNTIF(A1:A12;\">0\")", 5 },
4965 { "=COUNTIF(A1:A12;\">=0\")", 8 },
4966 { "=COUNTIF(A1:A12;0)", 3 },
4967 { "=COUNTIF(A1:A12;\"X\")", 1 },
4968 { "=COUNTIF(A1:A12;)", 3 }
4971 nRows = SAL_N_ELEMENTS(aChecks);
4972 for (SCROW i = 0; i < nRows; ++i)
4974 SCROW nRow = 20 + i;
4975 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
4978 for (SCROW i = 0; i < nRows; ++i)
4980 SCROW nRow = 20 + i;
4981 double result = m_pDoc->GetValue(0, nRow, 0);
4982 bool bGood = result == aChecks[i].fResult;
4983 if (!bGood)
4985 cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
4986 << " expected=" << aChecks[i].fResult << " actual=" << result << endl;
4987 CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
4991 // Don't count empty strings when searching for a number.
4993 // Clear A1:A2.
4994 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
4996 m_pDoc->SetString(0, 0, 0, "=\"\"");
4997 m_pDoc->SetString(0, 1, 0, "=COUNTIF(A1;1)");
4999 double result = m_pDoc->GetValue(0, 1, 0);
5000 ASSERT_DOUBLES_EQUAL_MESSAGE("We shouldn't count empty string as valid number.", 0.0, result);
5002 // Another test case adopted from fdo#77039.
5003 clearSheet(m_pDoc, 0);
5005 // Set formula cells with blank results in A1:A4.
5006 for (SCROW i = 0; i <=3; ++i)
5007 m_pDoc->SetString(ScAddress(0,i,0), "=\"\"");
5009 // Insert formula into A5 to count all cells with empty strings.
5010 m_pDoc->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
5012 // We should correctly count with empty string key.
5013 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,4,0)));
5015 // Another test case adopted from tdf#99291, empty array elements should
5016 // not match empty cells, but cells with 0.
5017 clearSheet(m_pDoc, 0);
5018 ScMarkData aMark(m_pDoc->GetSheetLimits());
5019 aMark.SelectOneTable(0);
5020 m_pDoc->InsertMatrixFormula(0,0, 0,1, aMark, "=COUNTIF(B1:B5;C1:C2)");
5021 // As we will be testing for 0.0 values, check that formulas are actually present.
5022 OUString aFormula = m_pDoc->GetFormula(0,0,0);
5023 CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
5024 aFormula = m_pDoc->GetFormula(0,1,0);
5025 CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
5026 // The 0.0 results expected.
5027 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5028 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5029 // 0.0 in B2, 1.0 in B3 and B4
5030 m_pDoc->SetValue( ScAddress(1,1,0), 0.0);
5031 m_pDoc->SetValue( ScAddress(1,2,0), 1.0);
5032 m_pDoc->SetValue( ScAddress(1,3,0), 1.0);
5033 // Matched by 0.0 produced by empty cell in array, and 1.0 in C2.
5034 m_pDoc->SetValue( ScAddress(2,1,0), 1.0);
5035 CPPUNIT_ASSERT_EQUAL_MESSAGE("One cell with 0.0", 1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5036 CPPUNIT_ASSERT_EQUAL_MESSAGE("Two cells with 1.0", 2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5038 m_pDoc->DeleteTab(0);
5041 CPPUNIT_PLUGIN_IMPLEMENT();
5043 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */