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