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 "inputopt.hxx"
21 : #include "scmod.hxx"
22 : #include "docsh.hxx"
23 : #include "docfunc.hxx"
24 : #include "paramisc.hxx"
25 : #include "tokenstringcontext.hxx"
26 : #include "dbdata.hxx"
27 :
28 : #include "formula/vectortoken.hxx"
29 :
30 : #include <boost/scoped_ptr.hpp>
31 :
32 : using namespace formula;
33 :
34 : namespace {
35 :
36 2 : ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
37 : {
38 2 : ScRange aRange;
39 :
40 2 : vector<SCROW> aRows;
41 2 : pCacheTab->getAllRows(aRows);
42 2 : vector<SCROW>::const_iterator itrRow = aRows.begin(), itrRowEnd = aRows.end();
43 2 : bool bFirst = true;
44 11 : for (; itrRow != itrRowEnd; ++itrRow)
45 : {
46 9 : SCROW nRow = *itrRow;
47 9 : vector<SCCOL> aCols;
48 9 : pCacheTab->getAllCols(nRow, aCols);
49 9 : vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end();
50 27 : for (; itrCol != itrColEnd; ++itrCol)
51 : {
52 18 : SCCOL nCol = *itrCol;
53 18 : if (bFirst)
54 : {
55 2 : aRange.aStart = ScAddress(nCol, nRow, 0);
56 2 : aRange.aEnd = aRange.aStart;
57 2 : bFirst = false;
58 : }
59 : else
60 : {
61 16 : if (nCol < aRange.aStart.Col())
62 0 : aRange.aStart.SetCol(nCol);
63 16 : else if (aRange.aEnd.Col() < nCol)
64 2 : aRange.aEnd.SetCol(nCol);
65 :
66 16 : if (nRow < aRange.aStart.Row())
67 0 : aRange.aStart.SetRow(nRow);
68 16 : else if (aRange.aEnd.Row() < nRow)
69 7 : aRange.aEnd.SetRow(nRow);
70 : }
71 : }
72 9 : }
73 2 : return aRange;
74 : }
75 :
76 : }
77 :
78 1 : void Test::testFormulaCreateStringFromTokens()
79 : {
80 : // Insert sheets.
81 1 : OUString aTabName1("Test");
82 2 : OUString aTabName2("Kevin's Data");
83 2 : OUString aTabName3("Past Data");
84 2 : OUString aTabName4("2013");
85 1 : m_pDoc->InsertTab(0, aTabName1);
86 1 : m_pDoc->InsertTab(1, aTabName2);
87 1 : m_pDoc->InsertTab(2, aTabName3);
88 1 : m_pDoc->InsertTab(3, aTabName4);
89 :
90 : // Insert named ranges.
91 : struct {
92 : bool bGlobal;
93 : const char* pName;
94 : const char* pExpr;
95 : } aNames[] = {
96 : { true, "x", "Test.H1" },
97 : { true, "y", "Test.H2" },
98 : { true, "z", "Test.H3" },
99 :
100 : { false, "sheetx", "Test.J1" }
101 1 : };
102 :
103 1 : ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
104 1 : ScRangeName* pSheetNames = m_pDoc->GetRangeName(0);
105 1 : CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
106 1 : CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames);
107 :
108 5 : for (size_t i = 0, n = SAL_N_ELEMENTS(aNames); i < n; ++i)
109 : {
110 : ScRangeData* pName = new ScRangeData(
111 : m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
112 4 : ScAddress(0,0,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE);
113 :
114 4 : if (aNames[i].bGlobal)
115 : {
116 3 : bool bInserted = pGlobalNames->insert(pName);
117 3 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
118 : }
119 : else
120 : {
121 1 : bool bInserted = pSheetNames->insert(pName);
122 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
123 : }
124 : }
125 :
126 : // Insert DB ranges.
127 : struct {
128 : const char* pName;
129 : SCTAB nTab;
130 : SCCOL nCol1;
131 : SCROW nRow1;
132 : SCCOL nCol2;
133 : SCROW nRow2;
134 : } aDBs[] = {
135 : { "Table1", 0, 0, 0, 10, 10 },
136 : { "Table2", 1, 0, 0, 10, 10 },
137 : { "Table3", 2, 0, 0, 10, 10 }
138 1 : };
139 :
140 1 : ScDBCollection* pDBs = m_pDoc->GetDBCollection();
141 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
142 :
143 4 : for (size_t i = 0, n = SAL_N_ELEMENTS(aDBs); i < n; ++i)
144 : {
145 : ScDBData* pData = new ScDBData(
146 : OUString::createFromAscii(
147 3 : aDBs[i].pName), aDBs[i].nTab, aDBs[i].nCol1, aDBs[i].nRow1, aDBs[i].nCol2,aDBs[i].nRow2);
148 3 : bool bInserted = pDBs->getNamedDBs().insert(pData);
149 6 : CPPUNIT_ASSERT_MESSAGE(
150 : OString(
151 : "Failed to insert \"" + OString(aDBs[i].pName) + "\"").getStr(),
152 3 : bInserted);
153 : }
154 :
155 : const char* aTests[] = {
156 : "1+2",
157 : "SUM(A1:A10;B1:B10;C5;D6)",
158 : "IF(Test.B10<>10;\"Good\";\"Bad\")",
159 : "AVERAGE('2013'.B10:C20)",
160 : "'Kevin''s Data'.B10",
161 : "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
162 : "x+y*z", // named ranges
163 : "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
164 : "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
165 : "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
166 : "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
167 1 : };
168 :
169 2 : boost::scoped_ptr<ScTokenArray> pArray;
170 :
171 2 : sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
172 :
173 : // Artificially add external refererence data after the context object is
174 : // initialized.
175 1 : aCxt.maExternalFileNames.push_back("file:///path/to/fake.file");
176 2 : std::vector<OUString> aExtTabNames;
177 1 : aExtTabNames.push_back("Sheet");
178 : aCxt.maExternalCachedTabNames.insert(
179 1 : sc::TokenStringContext::IndexNamesMapType::value_type(0, aExtTabNames));
180 :
181 1 : ScAddress aPos(0,0,0);
182 :
183 12 : for (size_t i = 0, n = SAL_N_ELEMENTS(aTests); i < n; ++i)
184 : {
185 : #if 0
186 : OUString aFormula = OUString::createFromAscii(aTests[i]);
187 : #endif
188 11 : ScCompiler aComp(m_pDoc, aPos);
189 11 : aComp.SetGrammar(FormulaGrammar::GRAM_ENGLISH);
190 : #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
191 : pArray.reset(aComp.CompileString(aFormula));
192 : CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray.get());
193 :
194 : OUString aCheck = pArray->CreateString(aCxt, aPos);
195 : CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
196 : #endif
197 11 : }
198 :
199 1 : m_pDoc->DeleteTab(3);
200 1 : m_pDoc->DeleteTab(2);
201 1 : m_pDoc->DeleteTab(1);
202 2 : m_pDoc->DeleteTab(0);
203 1 : }
204 :
205 : namespace {
206 :
207 18 : bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
208 : {
209 18 : if (rArray.mpStringArray)
210 : {
211 7 : if (rArray.mpStringArray[nPos])
212 0 : return false;
213 : }
214 :
215 18 : if (rArray.mpNumericArray)
216 17 : return rtl::math::isNan(rArray.mpNumericArray[nPos]);
217 : else
218 1 : return true;
219 : }
220 :
221 35 : bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
222 : {
223 35 : if (rArray.mpStringArray && rArray.mpStringArray[nPos])
224 : // This is a string cell.
225 0 : return false;
226 :
227 35 : if (rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal)
228 35 : return true;
229 :
230 0 : return false;
231 : }
232 :
233 16 : bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
234 : {
235 16 : if (!rArray.mpStringArray)
236 0 : return false;
237 :
238 16 : bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
239 16 : if (!bEquals)
240 : {
241 0 : cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
242 0 : cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
243 : }
244 16 : return bEquals;
245 : }
246 :
247 : }
248 :
249 1 : void Test::testFormulaParseReference()
250 : {
251 2 : OUString aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
252 1 : m_pDoc->InsertTab(0, "Dummy"); // just to shift the sheet indices...
253 1 : m_pDoc->InsertTab(1, aTab1); // name with a single quote.
254 1 : m_pDoc->InsertTab(2, aTab2); // name with 2 single quotes.
255 1 : m_pDoc->InsertTab(3, aTab3); // name without single quotes.
256 1 : m_pDoc->InsertTab(4, aTab4); // name that doesn't require to be quoted.
257 :
258 2 : OUString aTabName;
259 1 : m_pDoc->GetName(1, aTabName);
260 1 : CPPUNIT_ASSERT_EQUAL(aTab1, aTabName);
261 1 : m_pDoc->GetName(2, aTabName);
262 1 : CPPUNIT_ASSERT_EQUAL(aTab2, aTabName);
263 1 : m_pDoc->GetName(3, aTabName);
264 1 : CPPUNIT_ASSERT_EQUAL(aTab3, aTabName);
265 1 : m_pDoc->GetName(4, aTabName);
266 1 : CPPUNIT_ASSERT_EQUAL(aTab4, aTabName);
267 :
268 : // Make sure the formula input and output match.
269 : {
270 : const char* aChecks[] = {
271 : "'90''s Music'.B12",
272 : "'90''s and 70''s'.$AB$100",
273 : "'All Others'.Z$100",
274 : "NoQuote.$C111"
275 1 : };
276 :
277 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
278 : {
279 : // Use the 'Dummy' sheet for this.
280 4 : OUString aInput("=");
281 4 : aInput += OUString::createFromAscii(aChecks[i]);
282 4 : m_pDoc->SetString(ScAddress(0,0,0), aInput);
283 4 : if (!checkFormula(*m_pDoc, ScAddress(0,0,0), aChecks[i]))
284 0 : CPPUNIT_FAIL("Wrong formula");
285 4 : }
286 : }
287 :
288 1 : ScAddress aPos;
289 2 : ScAddress::ExternalInfo aExtInfo;
290 1 : sal_uInt16 nRes = aPos.Parse("'90''s Music'.D10", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
291 1 : CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
292 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(1), aPos.Tab());
293 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), aPos.Col());
294 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), aPos.Row());
295 1 : CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
296 :
297 1 : nRes = aPos.Parse("'90''s and 70''s'.C100", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
298 1 : CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
299 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(2), aPos.Tab());
300 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aPos.Col());
301 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(99), aPos.Row());
302 1 : CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
303 :
304 1 : nRes = aPos.Parse("'All Others'.B3", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
305 1 : CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
306 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(3), aPos.Tab());
307 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aPos.Col());
308 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), aPos.Row());
309 1 : CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
310 :
311 1 : nRes = aPos.Parse("NoQuote.E13", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
312 1 : CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
313 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aPos.Tab());
314 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(4), aPos.Col());
315 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), aPos.Row());
316 1 : CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
317 :
318 1 : m_pDoc->DeleteTab(4);
319 1 : m_pDoc->DeleteTab(3);
320 1 : m_pDoc->DeleteTab(2);
321 1 : m_pDoc->DeleteTab(1);
322 2 : m_pDoc->DeleteTab(0);
323 1 : }
324 :
325 1 : void Test::testFetchVectorRefArray()
326 : {
327 1 : m_pDoc->InsertTab(0, "Test");
328 :
329 : // All numeric cells in Column A.
330 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1);
331 1 : m_pDoc->SetValue(ScAddress(0,1,0), 2);
332 1 : m_pDoc->SetValue(ScAddress(0,2,0), 3);
333 1 : m_pDoc->SetValue(ScAddress(0,3,0), 4);
334 :
335 1 : formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 4);
336 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
337 1 : CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
338 1 : CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
339 1 : CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
340 1 : CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
341 1 : CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
342 :
343 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 5);
344 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
345 1 : CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
346 1 : CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
347 1 : CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
348 1 : CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
349 1 : CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
350 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
351 :
352 : // All string cells in Column B. Note that the fetched string arrays are
353 : // only to be compared case-insensitively. Right now, we use upper cased
354 : // strings to achieve case-insensitive-ness, but that may change. So,
355 : // don't count on that.
356 1 : m_pDoc->SetString(ScAddress(1,0,0), "Andy");
357 1 : m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
358 1 : m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
359 1 : m_pDoc->SetString(ScAddress(1,3,0), "David");
360 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 5);
361 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
362 1 : CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
363 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Andy"));
364 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Bruce"));
365 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "Charlie"));
366 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "David"));
367 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
368 :
369 : // Mixture of numeric, string, and empty cells in Column C.
370 1 : m_pDoc->SetString(ScAddress(2,0,0), "Header");
371 1 : m_pDoc->SetValue(ScAddress(2,1,0), 11);
372 1 : m_pDoc->SetValue(ScAddress(2,2,0), 12);
373 1 : m_pDoc->SetValue(ScAddress(2,3,0), 13);
374 1 : m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
375 1 : m_pDoc->CalcAll();
376 :
377 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,0,0), 7);
378 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
379 1 : CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
380 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Header"));
381 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 11));
382 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 12));
383 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 13));
384 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
385 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 36));
386 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
387 :
388 : // Mixed type again in Column D, but it starts with a numeric cell.
389 1 : m_pDoc->SetValue(ScAddress(3,0,0), 10);
390 1 : m_pDoc->SetString(ScAddress(3,1,0), "Below 10");
391 : // Leave 2 empty cells.
392 1 : m_pDoc->SetValue(ScAddress(3,4,0), 11);
393 1 : m_pDoc->SetString(ScAddress(3,5,0), "=12");
394 1 : m_pDoc->SetString(ScAddress(3,6,0), "=13");
395 1 : m_pDoc->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
396 1 : m_pDoc->CalcAll();
397 :
398 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,0,0), 8);
399 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
400 1 : CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
401 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 10));
402 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Below 10"));
403 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 2));
404 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
405 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 11));
406 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 12));
407 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13));
408 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC"));
409 :
410 : // Column E consists of formula cells whose results are all numeric.
411 8 : for (SCROW i = 0; i <= 6; ++i)
412 7 : m_pDoc->SetString(ScAddress(4,i,0), "=ROW()");
413 1 : m_pDoc->CalcAll();
414 :
415 : // Leave row 7 empty.
416 1 : m_pDoc->SetString(ScAddress(4,8,0), "Andy");
417 1 : m_pDoc->SetValue(ScAddress(4,9,0), 123);
418 :
419 : // This array fits within a single formula block.
420 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 5);
421 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
422 1 : CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray && !aArray.mpStringArray);
423 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
424 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
425 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
426 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
427 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
428 :
429 : // This array spans over multiple blocks.
430 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 11);
431 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
432 1 : CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
433 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
434 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
435 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
436 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
437 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
438 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6));
439 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7));
440 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
441 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy"));
442 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123));
443 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10));
444 :
445 : // Hit the cache but at a different start row.
446 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 3);
447 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
448 1 : CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
449 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3));
450 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4));
451 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5));
452 :
453 : // Column F begins with empty rows at the top.
454 1 : m_pDoc->SetValue(ScAddress(5,2,0), 1.1);
455 1 : m_pDoc->SetValue(ScAddress(5,3,0), 1.2);
456 1 : m_pDoc->SetString(ScAddress(5,4,0), "=2*8");
457 1 : m_pDoc->CalcAll();
458 :
459 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 4);
460 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
461 1 : CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
462 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1));
463 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2));
464 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16));
465 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
466 :
467 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 3);
468 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
469 1 : CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
470 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
471 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
472 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
473 :
474 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 10);
475 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
476 1 : CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
477 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
478 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
479 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
480 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2));
481 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16));
482 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5));
483 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
484 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
485 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8));
486 1 : CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9));
487 :
488 : // Get the array for F3:F4. This array should only consist of numeric array.
489 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 3);
490 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
491 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
492 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
493 :
494 : // Column G consists only of strings.
495 1 : m_pDoc->SetString(ScAddress(6,0,0), "Title");
496 1 : m_pDoc->SetString(ScAddress(6,1,0), "foo");
497 1 : m_pDoc->SetString(ScAddress(6,2,0), "bar");
498 1 : m_pDoc->SetString(ScAddress(6,3,0), "foo");
499 1 : m_pDoc->SetString(ScAddress(6,4,0), "baz");
500 1 : m_pDoc->SetString(ScAddress(6,5,0), "quack");
501 1 : m_pDoc->SetString(ScAddress(6,6,0), "beep");
502 1 : m_pDoc->SetString(ScAddress(6,7,0), "kerker");
503 :
504 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
505 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
506 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
507 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
508 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "foo"));
509 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "bar"));
510 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "foo"));
511 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "baz"));
512 :
513 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
514 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
515 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
516 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
517 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "bar"));
518 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "foo"));
519 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "baz"));
520 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "quack"));
521 :
522 : // Column H starts with formula cells.
523 11 : for (SCROW i = 0; i < 10; ++i)
524 10 : m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
525 :
526 1 : m_pDoc->CalcAll();
527 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
528 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
529 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
530 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
531 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 4.0));
532 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, 5.0));
533 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, 6.0));
534 :
535 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
536 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
537 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
538 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
539 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 5.0));
540 :
541 : // Clear everything and start over.
542 1 : clearRange(m_pDoc, ScRange(0,0,0,MAXCOL,MAXROW,0));
543 1 : m_pDoc->ClearFormulaContext();
544 :
545 : // Totally empty range in a totally empty column (Column A).
546 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
547 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
548 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
549 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
550 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
551 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
552 :
553 : // Totally empty range in a non-empty column (Column B).
554 1 : m_pDoc->SetString(ScAddress(1,10,0), "Some text"); // B11
555 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
556 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
557 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
558 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
559 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
560 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
561 :
562 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
563 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
564 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
565 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
566 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
567 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
568 :
569 : // These values come from a cache because of the call above.
570 1 : aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
571 1 : CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
572 1 : CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
573 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
574 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
575 1 : CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
576 :
577 1 : m_pDoc->DeleteTab(0);
578 1 : }
579 :
580 1 : void Test::testFormulaHashAndTag()
581 : {
582 1 : m_pDoc->InsertTab(0, "Test");
583 :
584 1 : ScAddress aPos1(0,0,0), aPos2(1,0,0);
585 :
586 : // Test formula hashing.
587 :
588 : struct {
589 : const char* pFormula1; const char* pFormula2; bool bEqual;
590 : } aHashTests[] = {
591 : { "=1", "=2", false }, // different constants
592 : { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
593 : { "=C2*3", "=D2*3", true }, // relative references
594 : { "=C2*3", "=D2*4", false }, // different constants
595 : { "=C2*4", "=D2*4", true }, // relative references
596 : { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
597 : { "=$C3/2", "=$C3/2", true }, // absolute column references
598 : { "=C$3/2", "=D$3/2", true }, // absolute row references
599 : { "=$E$30/2", "=$E$30/2", true }, // absolute references
600 : { "=X20", "=$X$20", false }, // absolute vs relative
601 : { "=X20", "=X$20", false }, // absolute vs relative
602 : { "=X20", "=$X20", false }, // absolute vs relative
603 : { "=X$20", "=$X20", false }, // column absolute vs row absolute
604 : // similar enough for merging ...
605 : { "=A1", "=B1", true },
606 : { "=$A$1", "=$B$1", true },
607 : { "=A1", "=C2", true },
608 : { "=SUM(A1)", "=SUM(B1)", true },
609 : { "=A1+3", "=B1+3", true },
610 : { "=A1+7", "=B1+42", false },
611 1 : };
612 :
613 20 : for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
614 : {
615 19 : m_pDoc->SetString(aPos1, OUString::createFromAscii(aHashTests[i].pFormula1));
616 19 : m_pDoc->SetString(aPos2, OUString::createFromAscii(aHashTests[i].pFormula2));
617 19 : size_t nHashVal1 = m_pDoc->GetFormulaHash(aPos1);
618 19 : size_t nHashVal2 = m_pDoc->GetFormulaHash(aPos2);
619 :
620 19 : std::ostringstream os;
621 19 : os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
622 19 : if (aHashTests[i].bEqual)
623 : {
624 10 : os << " Error: these hashes should be equal." << endl;
625 10 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 == nHashVal2);
626 : }
627 : else
628 : {
629 9 : os << " Error: these hashes should differ." << endl;
630 9 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 != nHashVal2);
631 : }
632 :
633 19 : aPos1.IncRow();
634 19 : aPos2.IncRow();
635 19 : }
636 :
637 : // Go back to row 1.
638 1 : aPos1.SetRow(0);
639 1 : aPos2.SetRow(0);
640 :
641 : // Test formula vectorization state.
642 :
643 : struct {
644 : const char* pFormula; ScFormulaVectorState eState;
645 : } aVectorTests[] = {
646 : { "=SUM(1;2;3;4;5)", FormulaVectorEnabled },
647 : { "=NOW()", FormulaVectorDisabled },
648 : { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference },
649 : { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference },
650 : { "=MIN(10;11;22)", FormulaVectorEnabled },
651 : { "=H4", FormulaVectorCheckReference },
652 1 : };
653 :
654 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
655 : {
656 6 : m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
657 6 : ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
658 :
659 6 : if (eState != aVectorTests[i].eState)
660 : {
661 0 : std::ostringstream os;
662 0 : os << "Unexpected vectorization state: expr:" << aVectorTests[i].pFormula;
663 0 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
664 : }
665 6 : aPos1.IncRow();
666 : }
667 :
668 1 : m_pDoc->DeleteTab(0);
669 1 : }
670 :
671 1 : void Test::testFormulaTokenEquality()
672 : {
673 : struct FormulaTokenEqualityTest
674 : {
675 : const char* mpFormula1;
676 : const char* mpFormula2;
677 : bool mbEqual;
678 : };
679 :
680 : FormulaTokenEqualityTest aTests[] = {
681 : { "R1C2", "R1C2", true },
682 : { "R1C2", "R1C3", false },
683 : { "R1C2", "R2C2", false },
684 : { "RC2", "RC[1]", false },
685 : { "R1C2:R10C2", "R1C2:R10C2", true },
686 : { "R1C2:R10C2", "R1C2:R11C2", false },
687 : { "1", "2", false },
688 : { "RC[1]+1.2", "RC[1]+1.2", true },
689 : { "RC[1]*0.2", "RC[1]*0.5", false },
690 : { "\"Test1\"", "\"Test2\"", false },
691 : { "\"Test\"", "\"Test\"", true },
692 : { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
693 : { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
694 1 : };
695 :
696 1 : formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
697 14 : for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
698 : {
699 13 : ScFormulaCell aCell1(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
700 26 : ScFormulaCell aCell2(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
701 :
702 13 : ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
703 13 : if (aTests[i].mbEqual)
704 : {
705 5 : if (eComp == ScFormulaCell::NotEqual)
706 : {
707 0 : std::ostringstream os;
708 0 : os << "These two formulas should be evaluated equal: '"
709 0 : << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
710 0 : CPPUNIT_FAIL(os.str().c_str());
711 : }
712 : }
713 : else
714 : {
715 8 : if (eComp != ScFormulaCell::NotEqual)
716 : {
717 0 : std::ostringstream os;
718 0 : os << "These two formulas should be evaluated non-equal: '"
719 0 : << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
720 0 : CPPUNIT_FAIL(os.str().c_str());
721 : }
722 : }
723 13 : }
724 1 : }
725 :
726 1 : void Test::testFormulaRefData()
727 : {
728 1 : ScAddress aAddr(4,5,3), aPos(2,2,2);
729 : ScSingleRefData aRef;
730 1 : aRef.InitAddress(aAddr);
731 1 : CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsRowRel() && !aRef.IsColRel() && !aRef.IsTabRel());
732 1 : ASSERT_EQUAL_TYPE(SCCOL, 4, aRef.Col());
733 1 : ASSERT_EQUAL_TYPE(SCROW, 5, aRef.Row());
734 1 : ASSERT_EQUAL_TYPE(SCTAB, 3, aRef.Tab());
735 :
736 1 : aRef.SetRowRel(true);
737 1 : aRef.SetColRel(true);
738 1 : aRef.SetTabRel(true);
739 1 : aRef.SetAddress(aAddr, aPos);
740 1 : ASSERT_EQUAL_TYPE(SCCOL, 2, aRef.Col());
741 1 : ASSERT_EQUAL_TYPE(SCROW, 3, aRef.Row());
742 1 : ASSERT_EQUAL_TYPE(SCTAB, 1, aRef.Tab());
743 :
744 : // Test extension of range reference.
745 :
746 : ScComplexRefData aDoubleRef;
747 1 : aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
748 :
749 1 : aRef.InitAddress(ScAddress(6,5,0));
750 :
751 1 : aDoubleRef.Extend(aRef, ScAddress());
752 1 : ScRange aTest = aDoubleRef.toAbs(ScAddress());
753 1 : CPPUNIT_ASSERT_MESSAGE("Wrong start position of extended range.", aTest.aStart == ScAddress(2,2,0));
754 1 : CPPUNIT_ASSERT_MESSAGE("Wrong end position of extended range.", aTest.aEnd == ScAddress(6,5,0));
755 :
756 : ScComplexRefData aDoubleRef2;
757 1 : aDoubleRef2.InitRangeRel(ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
758 1 : aDoubleRef.Extend(aDoubleRef2, ScAddress(5,5,0));
759 1 : aTest = aDoubleRef.toAbs(ScAddress(5,5,0));
760 :
761 1 : CPPUNIT_ASSERT_MESSAGE("Wrong start position of extended range.", aTest.aStart == ScAddress(1,2,0));
762 1 : CPPUNIT_ASSERT_MESSAGE("Wrong end position of extended range.", aTest.aEnd == ScAddress(8,6,0));
763 1 : }
764 :
765 1 : void Test::testFormulaCompiler()
766 : {
767 : struct {
768 : const char* pInput; FormulaGrammar::Grammar eInputGram;
769 : const char* pOutput; FormulaGrammar::Grammar eOutputGram;
770 : } aTests[] = {
771 : { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF },
772 : { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE },
773 : { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1 },
774 : { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1 },
775 1 : };
776 :
777 5 : for (size_t i = 0, n = SAL_N_ELEMENTS(aTests); i < n; ++i)
778 : {
779 4 : boost::scoped_ptr<ScTokenArray> pArray;
780 : {
781 4 : ScCompiler aComp(m_pDoc, ScAddress());
782 4 : aComp.SetGrammar(aTests[i].eInputGram);
783 4 : pArray.reset(aComp.CompileString(OUString::createFromAscii(aTests[i].pInput)));
784 4 : CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray.get());
785 : }
786 :
787 8 : OUString aFormula = toString(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
788 4 : CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
789 4 : }
790 1 : }
791 :
792 1 : void Test::testFormulaRefUpdate()
793 : {
794 1 : m_pDoc->InsertTab(0, "Formula");
795 :
796 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
797 :
798 1 : m_pDoc->SetValue(ScAddress(0,0,0), 2.0); // A1
799 1 : m_pDoc->SetString(ScAddress(2,2,0), "=A1"); // C3
800 1 : m_pDoc->SetString(ScAddress(2,3,0), "=$A$1"); // C4
801 :
802 1 : ScAddress aPos(2,2,0);
803 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
804 0 : CPPUNIT_FAIL("Wrong formula in C3.");
805 :
806 1 : aPos = ScAddress(2,3,0);
807 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
808 0 : CPPUNIT_FAIL("Wrong formula in C4.");
809 :
810 : // Delete row 2 to push formula cells up (to C2:C3).
811 1 : m_pDoc->DeleteRow(ScRange(0,1,0,MAXCOL,1,0));
812 :
813 1 : aPos = ScAddress(2,1,0);
814 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
815 0 : CPPUNIT_FAIL("Wrong formula in C2.");
816 :
817 1 : aPos = ScAddress(2,2,0);
818 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
819 0 : CPPUNIT_FAIL("Wrong formula in C3.");
820 :
821 : // Insert one row at row 2 to move them back.
822 1 : m_pDoc->InsertRow(ScRange(0,1,0,MAXCOL,1,0));
823 :
824 1 : aPos = ScAddress(2,2,0);
825 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
826 0 : CPPUNIT_FAIL("Wrong formula in C3.");
827 :
828 1 : aPos = ScAddress(2,3,0);
829 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
830 0 : CPPUNIT_FAIL("Wrong formula in C4.");
831 :
832 : // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
833 1 : m_pDoc->InsertRow(ScRange(0,0,0,MAXCOL,1,0));
834 :
835 1 : aPos = ScAddress(2,4,0);
836 1 : if (!checkFormula(*m_pDoc, aPos, "A3"))
837 0 : CPPUNIT_FAIL("Wrong formula in C5.");
838 :
839 1 : aPos = ScAddress(2,5,0);
840 1 : if (!checkFormula(*m_pDoc, aPos, "$A$3"))
841 0 : CPPUNIT_FAIL("Wrong formula in C6.");
842 :
843 : // Delete 2 rows at row 1 to shift them back.
844 1 : m_pDoc->DeleteRow(ScRange(0,0,0,MAXCOL,1,0));
845 :
846 1 : aPos = ScAddress(2,2,0);
847 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
848 0 : CPPUNIT_FAIL("Wrong formula in C3.");
849 :
850 1 : aPos = ScAddress(2,3,0);
851 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
852 0 : CPPUNIT_FAIL("Wrong formula in C4.");
853 :
854 : // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
855 1 : m_pDoc->InsertCol(ScRange(1,0,0,3,MAXROW,0));
856 :
857 1 : aPos = ScAddress(5,2,0);
858 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
859 0 : CPPUNIT_FAIL("Wrong formula in F3.");
860 :
861 1 : aPos = ScAddress(5,3,0);
862 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
863 0 : CPPUNIT_FAIL("Wrong formula in F4.");
864 :
865 : // Delete columns B:D to shift them back.
866 1 : m_pDoc->DeleteCol(ScRange(1,0,0,3,MAXROW,0));
867 :
868 1 : aPos = ScAddress(2,2,0);
869 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
870 0 : CPPUNIT_FAIL("Wrong formula in C3.");
871 :
872 1 : aPos = ScAddress(2,3,0);
873 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
874 0 : CPPUNIT_FAIL("Wrong formula in C4.");
875 :
876 : // Insert cells over A1:A3 to only shift A1 down to A4.
877 1 : m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
878 :
879 1 : aPos = ScAddress(2,2,0);
880 1 : if (!checkFormula(*m_pDoc, aPos, "A4"))
881 0 : CPPUNIT_FAIL("Wrong formula in C3.");
882 :
883 1 : aPos = ScAddress(2,3,0);
884 1 : if (!checkFormula(*m_pDoc, aPos, "$A$4"))
885 0 : CPPUNIT_FAIL("Wrong formula in C4.");
886 :
887 : // .. and back.
888 1 : m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
889 :
890 1 : aPos = ScAddress(2,2,0);
891 1 : if (!checkFormula(*m_pDoc, aPos, "A1"))
892 0 : CPPUNIT_FAIL("Wrong formula in C3.");
893 :
894 1 : aPos = ScAddress(2,3,0);
895 1 : if (!checkFormula(*m_pDoc, aPos, "$A$1"))
896 0 : CPPUNIT_FAIL("Wrong formula in C4.");
897 :
898 : // Delete row 1 which will delete the value cell (A1).
899 1 : m_pDoc->DeleteRow(ScRange(0,0,0,MAXCOL,0,0));
900 :
901 1 : aPos = ScAddress(2,1,0);
902 1 : ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
903 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
904 1 : CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
905 1 : aPos = ScAddress(2,2,0);
906 1 : pFC = m_pDoc->GetFormulaCell(aPos);
907 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
908 1 : CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
909 :
910 : // Clear all and start over.
911 1 : clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
912 :
913 :
914 : // Test range updates
915 :
916 :
917 : // Fill B2:C3 with values.
918 1 : m_pDoc->SetValue(ScAddress(1,1,0), 1);
919 1 : m_pDoc->SetValue(ScAddress(1,2,0), 2);
920 1 : m_pDoc->SetValue(ScAddress(2,1,0), 3);
921 1 : m_pDoc->SetValue(ScAddress(2,2,0), 4);
922 :
923 1 : m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
924 1 : m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
925 :
926 1 : aPos = ScAddress(0,5,0);
927 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
928 0 : CPPUNIT_FAIL("Wrong formula in A6.");
929 :
930 1 : aPos = ScAddress(0,6,0);
931 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
932 0 : CPPUNIT_FAIL("Wrong formula in A7.");
933 :
934 : // Insert a row at row 1.
935 1 : m_pDoc->InsertRow(ScRange(0,0,0,MAXCOL,0,0));
936 :
937 1 : aPos = ScAddress(0,6,0);
938 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(B3:C4)"))
939 0 : CPPUNIT_FAIL("Wrong formula in A7.");
940 :
941 1 : aPos = ScAddress(0,7,0);
942 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($B$3:$C$4)"))
943 0 : CPPUNIT_FAIL("Wrong formula in A8.");
944 :
945 : // ... and back.
946 1 : m_pDoc->DeleteRow(ScRange(0,0,0,MAXCOL,0,0));
947 :
948 1 : aPos = ScAddress(0,5,0);
949 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
950 0 : CPPUNIT_FAIL("Wrong formula in A6.");
951 :
952 1 : aPos = ScAddress(0,6,0);
953 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
954 0 : CPPUNIT_FAIL("Wrong formula in A7.");
955 :
956 : // Insert columns B:C to shift only the value range.
957 1 : m_pDoc->InsertCol(ScRange(1,0,0,2,MAXROW,0));
958 :
959 1 : aPos = ScAddress(0,5,0);
960 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(D2:E3)"))
961 0 : CPPUNIT_FAIL("Wrong formula in A6.");
962 :
963 1 : aPos = ScAddress(0,6,0);
964 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($D$2:$E$3)"))
965 0 : CPPUNIT_FAIL("Wrong formula in A7.");
966 :
967 : // ... and back.
968 1 : m_pDoc->DeleteCol(ScRange(1,0,0,2,MAXROW,0));
969 :
970 1 : aPos = ScAddress(0,5,0);
971 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
972 0 : CPPUNIT_FAIL("Wrong formula in A6.");
973 :
974 1 : aPos = ScAddress(0,6,0);
975 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
976 0 : CPPUNIT_FAIL("Wrong formula in A7.");
977 :
978 : // Insert rows 5:6 to shift the formula cells only.
979 1 : m_pDoc->InsertRow(ScRange(0,4,0,MAXCOL,5,0));
980 :
981 1 : aPos = ScAddress(0,7,0);
982 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
983 0 : CPPUNIT_FAIL("Wrong formula in A8.");
984 :
985 1 : aPos = ScAddress(0,8,0);
986 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
987 0 : CPPUNIT_FAIL("Wrong formula in A9.");
988 :
989 : // ... and back.
990 1 : m_pDoc->DeleteRow(ScRange(0,4,0,MAXCOL,5,0));
991 :
992 1 : aPos = ScAddress(0,5,0);
993 1 : if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
994 0 : CPPUNIT_FAIL("Wrong formula in A6.");
995 :
996 1 : aPos = ScAddress(0,6,0);
997 1 : if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
998 0 : CPPUNIT_FAIL("Wrong formula in A7.");
999 :
1000 : // Check the values of the formula cells in A6:A7.
1001 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1002 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1003 :
1004 : // Insert cells over B1:B2 to partially shift value range.
1005 1 : m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
1006 :
1007 : // Check the values of the formula cells in A6:A7 again.
1008 1 : CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1009 1 : CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1010 :
1011 : // ... and shift them back.
1012 1 : m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
1013 :
1014 : // The formula cell results should be back too.
1015 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1016 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1017 :
1018 : // Delete rows 2:3 to completely remove the referenced range.
1019 1 : m_pDoc->DeleteRow(ScRange(0,1,0,MAXCOL,2,0));
1020 :
1021 : // Both A4 and A5 should show #REF! errors.
1022 1 : pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
1023 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1024 1 : CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
1025 :
1026 1 : pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
1027 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1028 1 : CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
1029 :
1030 1 : m_pDoc->DeleteTab(0);
1031 1 : }
1032 :
1033 1 : void Test::testFormulaRefUpdateRange()
1034 : {
1035 1 : m_pDoc->InsertTab(0, "Formula");
1036 :
1037 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1038 :
1039 1 : ScModule* pMod = SC_MOD();
1040 2 : ScInputOptions aOpt = pMod->GetInputOptions();
1041 1 : aOpt.SetExpandRefs(false);
1042 1 : pMod->SetInputOptions(aOpt);
1043 :
1044 : // Set values to B2:C5.
1045 1 : m_pDoc->SetValue(ScAddress(1,1,0), 1);
1046 1 : m_pDoc->SetValue(ScAddress(1,2,0), 2);
1047 1 : m_pDoc->SetValue(ScAddress(1,3,0), 3);
1048 1 : m_pDoc->SetValue(ScAddress(1,4,0), 4);
1049 1 : m_pDoc->SetValue(ScAddress(2,1,0), 5);
1050 1 : m_pDoc->SetValue(ScAddress(2,2,0), 6);
1051 1 : m_pDoc->SetValue(ScAddress(2,3,0), 7);
1052 1 : m_pDoc->SetValue(ScAddress(2,4,0), 8);
1053 :
1054 : // Set formula cells to A7 and A8.
1055 1 : m_pDoc->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
1056 1 : m_pDoc->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
1057 :
1058 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM(B2:C5)"))
1059 0 : CPPUNIT_FAIL("Wrong formula in A7.");
1060 :
1061 1 : if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "SUM($B$2:$C$5)"))
1062 0 : CPPUNIT_FAIL("Wrong formula in A8.");
1063 :
1064 1 : CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1065 1 : CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1066 :
1067 : // Delete row 3. This should shrink the range references by one row.
1068 1 : m_pDoc->DeleteRow(ScRange(0,2,0,MAXCOL,2,0));
1069 :
1070 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM(B2:C4)"))
1071 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1072 :
1073 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM($B$2:$C$4)"))
1074 0 : CPPUNIT_FAIL("Wrong formula in A7.");
1075 :
1076 1 : CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1077 1 : CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1078 :
1079 : // Delete row 4 - bottom of range
1080 1 : m_pDoc->DeleteRow(ScRange(0,3,0,MAXCOL,3,0));
1081 :
1082 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(B2:C3)"))
1083 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1084 :
1085 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($B$2:$C$3)"))
1086 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1087 :
1088 1 : CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1089 1 : CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1090 :
1091 : // Delete row 2 - top of range
1092 1 : m_pDoc->DeleteRow(ScRange(0,1,0,MAXCOL,1,0));
1093 :
1094 1 : if (!checkFormula(*m_pDoc, ScAddress(0,3,0), "SUM(B2:C2)"))
1095 0 : CPPUNIT_FAIL("Wrong formula in A4.");
1096 :
1097 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM($B$2:$C$2)"))
1098 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1099 :
1100 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1101 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1102 :
1103 : // Clear the range and start over.
1104 1 : clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1105 :
1106 : // Fill C2:F3 with values.
1107 1 : m_pDoc->SetValue(ScAddress(2,1,0), 1);
1108 1 : m_pDoc->SetValue(ScAddress(3,1,0), 2);
1109 1 : m_pDoc->SetValue(ScAddress(4,1,0), 3);
1110 1 : m_pDoc->SetValue(ScAddress(5,1,0), 4);
1111 1 : m_pDoc->SetValue(ScAddress(2,2,0), 5);
1112 1 : m_pDoc->SetValue(ScAddress(3,2,0), 6);
1113 1 : m_pDoc->SetValue(ScAddress(4,2,0), 7);
1114 1 : m_pDoc->SetValue(ScAddress(5,2,0), 8);
1115 :
1116 : // Set formulas to A2 and A3.
1117 1 : m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
1118 1 : m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
1119 :
1120 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:F3)"))
1121 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1122 :
1123 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$F$3)"))
1124 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1125 :
1126 1 : CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1127 1 : CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1128 :
1129 : // Delete column D.
1130 1 : m_pDoc->DeleteCol(ScRange(3,0,0,3,MAXROW,0));
1131 :
1132 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:E3)"))
1133 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1134 :
1135 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$E$3)"))
1136 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1137 :
1138 1 : CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1139 1 : CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1140 :
1141 : // Delete column E - the right edge of reference range.
1142 1 : m_pDoc->DeleteCol(ScRange(4,0,0,4,MAXROW,0));
1143 :
1144 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:D3)"))
1145 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1146 :
1147 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$D$3)"))
1148 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1149 :
1150 1 : CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1151 1 : CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1152 :
1153 : // Delete column C - the left edge of reference range.
1154 1 : m_pDoc->DeleteCol(ScRange(2,0,0,2,MAXROW,0));
1155 :
1156 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:C3)"))
1157 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1158 :
1159 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$C$3)"))
1160 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1161 :
1162 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1163 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1164 :
1165 : // Clear the range and start over.
1166 1 : clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1167 :
1168 : // Disable expansion of range reference on insertion in adjacent areas.
1169 1 : m_pDoc->SetExpandRefs(false);
1170 :
1171 : // Fill C2:D3 with values.
1172 1 : m_pDoc->SetValue(ScAddress(2,1,0), 1);
1173 1 : m_pDoc->SetValue(ScAddress(3,1,0), 2);
1174 1 : m_pDoc->SetValue(ScAddress(2,2,0), 3);
1175 1 : m_pDoc->SetValue(ScAddress(3,2,0), 4);
1176 :
1177 : // Set formulas at A5 and A6.
1178 1 : m_pDoc->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
1179 1 : m_pDoc->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
1180 :
1181 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)"))
1182 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1183 :
1184 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)"))
1185 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1186 :
1187 : // Insert a column at column C. This should simply shift the reference without expansion.
1188 1 : m_pDoc->InsertCol(ScRange(2,0,0,2,MAXROW,0));
1189 :
1190 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(D2:E3)"))
1191 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1192 :
1193 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($D$2:$E$3)"))
1194 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1195 :
1196 : // Shift it back.
1197 1 : m_pDoc->DeleteCol(ScRange(2,0,0,2,MAXROW,0));
1198 :
1199 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)"))
1200 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1201 :
1202 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)"))
1203 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1204 :
1205 : // Insert at column D. This should expand the reference by one column length.
1206 1 : m_pDoc->InsertCol(ScRange(3,0,0,3,MAXROW,0));
1207 :
1208 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)"))
1209 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1210 :
1211 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)"))
1212 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1213 :
1214 : // Insert at column F. No expansion should occur since the edge expansion is turned off.
1215 1 : m_pDoc->InsertCol(ScRange(5,0,0,5,MAXROW,0));
1216 :
1217 1 : if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)"))
1218 0 : CPPUNIT_FAIL("Wrong formula in A5.");
1219 :
1220 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)"))
1221 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1222 :
1223 : // Insert at row 2. No expansion should occur with edge expansion turned off.
1224 1 : m_pDoc->InsertRow(ScRange(0,1,0,MAXCOL,1,0));
1225 :
1226 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM(C3:E4)"))
1227 0 : CPPUNIT_FAIL("Wrong formula in A6.");
1228 :
1229 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM($C$3:$E$4)"))
1230 0 : CPPUNIT_FAIL("Wrong formula in A7.");
1231 :
1232 : // Insert at row 4 to expand the reference range.
1233 1 : m_pDoc->InsertRow(ScRange(0,3,0,MAXCOL,3,0));
1234 :
1235 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM(C3:E5)"))
1236 0 : CPPUNIT_FAIL("Wrong formula in A7.");
1237 :
1238 1 : if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "SUM($C$3:$E$5)"))
1239 0 : CPPUNIT_FAIL("Wrong formula in A8.");
1240 :
1241 : // Insert at row 6. No expansion with edge expansion turned off.
1242 1 : m_pDoc->InsertRow(ScRange(0,5,0,MAXCOL,5,0));
1243 :
1244 1 : if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "SUM(C3:E5)"))
1245 0 : CPPUNIT_FAIL("Wrong formula in A8.");
1246 :
1247 1 : if (!checkFormula(*m_pDoc, ScAddress(0,8,0), "SUM($C$3:$E$5)"))
1248 0 : CPPUNIT_FAIL("Wrong formula in A9.");
1249 :
1250 : // Clear the range and start over.
1251 1 : clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
1252 :
1253 : // Turn edge expansion on.
1254 1 : aOpt.SetExpandRefs(true);
1255 1 : pMod->SetInputOptions(aOpt);
1256 :
1257 : // Fill C6:D7 with values.
1258 1 : m_pDoc->SetValue(ScAddress(2,5,0), 1);
1259 1 : m_pDoc->SetValue(ScAddress(2,6,0), 2);
1260 1 : m_pDoc->SetValue(ScAddress(3,5,0), 3);
1261 1 : m_pDoc->SetValue(ScAddress(3,6,0), 4);
1262 :
1263 : // Set formulas at A2 and A3.
1264 1 : m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
1265 1 : m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
1266 :
1267 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:D7)"))
1268 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1269 :
1270 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$D$7)"))
1271 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1272 :
1273 : // Insert at column E. This should expand the reference range by one column.
1274 1 : m_pDoc->InsertCol(ScRange(4,0,0,4,MAXROW,0));
1275 :
1276 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:E7)"))
1277 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1278 :
1279 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$E$7)"))
1280 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1281 :
1282 : // Insert at column C to edge-expand the reference range.
1283 1 : m_pDoc->InsertCol(ScRange(2,0,0,2,MAXROW,0));
1284 :
1285 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F7)"))
1286 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1287 :
1288 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$7)"))
1289 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1290 :
1291 : // Insert at row 8 to edge-expand.
1292 1 : m_pDoc->InsertRow(ScRange(0,7,0,MAXCOL,7,0));
1293 :
1294 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F8)"))
1295 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1296 :
1297 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$8)"))
1298 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1299 :
1300 : // Insert at row 6 to edge-expand.
1301 1 : m_pDoc->InsertRow(ScRange(0,5,0,MAXCOL,5,0));
1302 :
1303 1 : if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F9)"))
1304 0 : CPPUNIT_FAIL("Wrong formula in A2.");
1305 :
1306 1 : if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$9)"))
1307 0 : CPPUNIT_FAIL("Wrong formula in A3.");
1308 :
1309 2 : m_pDoc->DeleteTab(0);
1310 1 : }
1311 :
1312 1 : void Test::testFormulaRefUpdateSheets()
1313 : {
1314 1 : m_pDoc->InsertTab(0, "Sheet1");
1315 1 : m_pDoc->InsertTab(1, "Sheet2");
1316 :
1317 1 : OUString aName;
1318 1 : m_pDoc->GetName(0, aName);
1319 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
1320 1 : m_pDoc->GetName(1, aName);
1321 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1322 :
1323 2 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1324 :
1325 : // Set values to B2:C3 on sheet Sheet1.
1326 1 : m_pDoc->SetValue(ScAddress(1,1,0), 1);
1327 1 : m_pDoc->SetValue(ScAddress(1,2,0), 2);
1328 1 : m_pDoc->SetValue(ScAddress(2,1,0), 3);
1329 1 : m_pDoc->SetValue(ScAddress(2,2,0), 4);
1330 :
1331 : // Set formulas to B2 and B3 on sheet Sheet2.
1332 1 : m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
1333 1 : m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
1334 :
1335 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
1336 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1337 :
1338 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
1339 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1340 :
1341 : // Swap the sheets.
1342 1 : m_pDoc->MoveTab(0, 1);
1343 1 : m_pDoc->GetName(0, aName);
1344 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1345 1 : m_pDoc->GetName(1, aName);
1346 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
1347 :
1348 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)"))
1349 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1350 :
1351 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)"))
1352 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1353 :
1354 : // Swap back.
1355 1 : m_pDoc->MoveTab(0, 1);
1356 1 : m_pDoc->GetName(0, aName);
1357 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
1358 1 : m_pDoc->GetName(1, aName);
1359 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1360 :
1361 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
1362 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1363 :
1364 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
1365 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1366 :
1367 : // Insert a new sheet between the two.
1368 1 : m_pDoc->InsertTab(1, "Temp");
1369 :
1370 1 : m_pDoc->GetName(1, aName);
1371 1 : CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName);
1372 1 : m_pDoc->GetName(2, aName);
1373 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1374 :
1375 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)"))
1376 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1377 :
1378 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)"))
1379 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1380 :
1381 : // Move the last sheet (Sheet2) to the first position.
1382 1 : m_pDoc->MoveTab(2, 0);
1383 :
1384 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)"))
1385 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1386 :
1387 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)"))
1388 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1389 :
1390 : // Move back.
1391 1 : m_pDoc->MoveTab(0, 2);
1392 :
1393 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)"))
1394 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1395 :
1396 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)"))
1397 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1398 :
1399 : // Move the "Temp" sheet to the last position.
1400 1 : m_pDoc->MoveTab(1, 2);
1401 :
1402 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
1403 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1404 :
1405 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
1406 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1407 :
1408 : // Move back.
1409 1 : m_pDoc->MoveTab(2, 1);
1410 :
1411 : // Delete the temporary sheet.
1412 1 : m_pDoc->DeleteTab(1);
1413 :
1414 1 : m_pDoc->GetName(1, aName);
1415 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1416 :
1417 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
1418 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1419 :
1420 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
1421 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1422 :
1423 : // Insert a new sheet before the first one.
1424 1 : m_pDoc->InsertTab(0, "Temp");
1425 :
1426 1 : m_pDoc->GetName(1, aName);
1427 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
1428 1 : m_pDoc->GetName(2, aName);
1429 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1430 :
1431 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)"))
1432 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1433 :
1434 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)"))
1435 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1436 :
1437 : // Delete the temporary sheet.
1438 1 : m_pDoc->DeleteTab(0);
1439 :
1440 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
1441 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1442 :
1443 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
1444 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1445 :
1446 : // Append a bunch of sheets.
1447 1 : m_pDoc->InsertTab(2, "Temp1");
1448 1 : m_pDoc->InsertTab(3, "Temp2");
1449 1 : m_pDoc->InsertTab(4, "Temp3");
1450 :
1451 : // Move these tabs around. This shouldn't affects the first 2 sheets.
1452 1 : m_pDoc->MoveTab(2, 4);
1453 1 : m_pDoc->MoveTab(3, 2);
1454 :
1455 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
1456 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1457 :
1458 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
1459 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1460 :
1461 : // Delete the temp sheets.
1462 1 : m_pDoc->DeleteTab(4);
1463 1 : m_pDoc->DeleteTab(3);
1464 1 : m_pDoc->DeleteTab(2);
1465 :
1466 : // Delete Sheet1.
1467 1 : m_pDoc->DeleteTab(0);
1468 1 : m_pDoc->GetName(0, aName);
1469 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
1470 :
1471 1 : if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(#REF!.B2:C3)"))
1472 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
1473 :
1474 1 : if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)"))
1475 0 : CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
1476 :
1477 2 : m_pDoc->DeleteTab(0);
1478 1 : }
1479 :
1480 1 : void Test::testFormulaRefUpdateMove()
1481 : {
1482 1 : m_pDoc->InsertTab(0, "Sheet1");
1483 :
1484 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1485 :
1486 : // Set value to B4:B6.
1487 1 : m_pDoc->SetValue(ScAddress(1,3,0), 1);
1488 1 : m_pDoc->SetValue(ScAddress(1,4,0), 2);
1489 1 : m_pDoc->SetValue(ScAddress(1,5,0), 3);
1490 :
1491 : // Set formulas to A9:A12 that references B4:B6.
1492 1 : m_pDoc->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
1493 1 : m_pDoc->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
1494 1 : m_pDoc->SetString(ScAddress(0,10,0), "=B5");
1495 1 : m_pDoc->SetString(ScAddress(0,11,0), "=$B$6");
1496 :
1497 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
1498 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
1499 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
1500 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
1501 :
1502 : // Move B4:B6 to D4 (two columsn to the right).
1503 1 : ScDocFunc& rFunc = getDocShell().GetDocFunc();
1504 1 : bool bMoved = rFunc.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
1505 1 : CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved);
1506 :
1507 : // The results of the formula cells that reference the moved range should remain the same.
1508 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
1509 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
1510 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
1511 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
1512 :
1513 1 : if (!checkFormula(*m_pDoc, ScAddress(0,8,0), "SUM(D4:D6)"))
1514 0 : CPPUNIT_FAIL("Wrong formula.");
1515 :
1516 1 : if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM($D$4:$D$6)"))
1517 0 : CPPUNIT_FAIL("Wrong formula.");
1518 :
1519 1 : if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "D5"))
1520 0 : CPPUNIT_FAIL("Wrong formula.");
1521 :
1522 1 : if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "$D$6"))
1523 0 : CPPUNIT_FAIL("Wrong formula.");
1524 :
1525 : // Move A9:A12 to B10:B13.
1526 1 : bMoved = rFunc.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
1527 1 : CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved);
1528 :
1529 : // The results of these formula cells should still stay the same.
1530 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,9,0));
1531 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,10,0));
1532 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
1533 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
1534 :
1535 : // Displayed formulas should stay the same since the referenced range hasn't moved.
1536 1 : if (!checkFormula(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)"))
1537 0 : CPPUNIT_FAIL("Wrong formula.");
1538 :
1539 1 : if (!checkFormula(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)"))
1540 0 : CPPUNIT_FAIL("Wrong formula.");
1541 :
1542 1 : if (!checkFormula(*m_pDoc, ScAddress(1,11,0), "D5"))
1543 0 : CPPUNIT_FAIL("Wrong formula.");
1544 :
1545 1 : if (!checkFormula(*m_pDoc, ScAddress(1,12,0), "$D$6"))
1546 0 : CPPUNIT_FAIL("Wrong formula.");
1547 :
1548 : // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
1549 : // where it is.
1550 1 : bMoved = rFunc.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
1551 1 : CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved);
1552 :
1553 : // Only the values of B10 and B11 should be updated.
1554 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,9,0));
1555 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,10,0));
1556 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
1557 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
1558 :
1559 1 : if (!checkFormula(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)"))
1560 0 : CPPUNIT_FAIL("Wrong formula.");
1561 :
1562 1 : if (!checkFormula(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)"))
1563 0 : CPPUNIT_FAIL("Wrong formula.");
1564 :
1565 1 : if (!checkFormula(*m_pDoc, ScAddress(1,11,0), "E5"))
1566 0 : CPPUNIT_FAIL("Wrong formula.");
1567 :
1568 1 : if (!checkFormula(*m_pDoc, ScAddress(1,12,0), "$D$6"))
1569 0 : CPPUNIT_FAIL("Wrong formula.");
1570 :
1571 1 : m_pDoc->DeleteTab(0);
1572 1 : }
1573 :
1574 1 : void Test::testFormulaRefUpdateMoveUndo()
1575 : {
1576 1 : m_pDoc->InsertTab(0, "Test");
1577 :
1578 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1579 :
1580 : // Set values in A1:A4.
1581 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
1582 1 : m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
1583 1 : m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
1584 1 : m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
1585 :
1586 : // Set formulas with single cell references in A6:A8.
1587 1 : m_pDoc->SetString(ScAddress(0,5,0), "=A1");
1588 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1589 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "A1"))
1590 0 : CPPUNIT_FAIL("Wrong formula.");
1591 :
1592 1 : m_pDoc->SetString(ScAddress(0,6,0), "=A1+A2+A3");
1593 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1594 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3"))
1595 0 : CPPUNIT_FAIL("Wrong formula.");
1596 :
1597 1 : m_pDoc->SetString(ScAddress(0,7,0), "=A1+A3+A4");
1598 1 : CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1599 1 : if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4"))
1600 0 : CPPUNIT_FAIL("Wrong formula.");
1601 :
1602 : // Set formulas with range references in A10:A12.
1603 1 : m_pDoc->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
1604 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1605 1 : if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)"))
1606 0 : CPPUNIT_FAIL("Wrong formula.");
1607 :
1608 1 : m_pDoc->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
1609 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
1610 1 : if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)"))
1611 0 : CPPUNIT_FAIL("Wrong formula.");
1612 :
1613 1 : m_pDoc->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
1614 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
1615 1 : if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)"))
1616 0 : CPPUNIT_FAIL("Wrong formula.");
1617 :
1618 : // Move A1:A3 to C1:C3. Note that A4 remains.
1619 1 : ScDocFunc& rFunc = getDocShell().GetDocFunc();
1620 1 : bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
1621 1 : CPPUNIT_ASSERT(bMoved);
1622 :
1623 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1624 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "C1"))
1625 0 : CPPUNIT_FAIL("Wrong formula.");
1626 :
1627 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1628 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "C1+C2+C3"))
1629 0 : CPPUNIT_FAIL("Wrong formula.");
1630 :
1631 1 : CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1632 1 : if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "C1+C3+A4"))
1633 0 : CPPUNIT_FAIL("Wrong formula.");
1634 :
1635 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1636 1 : if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM(C1:C2)"))
1637 0 : CPPUNIT_FAIL("Wrong formula.");
1638 :
1639 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
1640 1 : if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "SUM(C1:C3)"))
1641 0 : CPPUNIT_FAIL("Wrong formula.");
1642 :
1643 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,11,0)));
1644 1 : if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)"))
1645 0 : CPPUNIT_FAIL("Wrong formula.");
1646 :
1647 : // Undo the move.
1648 1 : SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1649 1 : CPPUNIT_ASSERT(pUndoMgr);
1650 1 : pUndoMgr->Undo();
1651 :
1652 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1653 1 : if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "A1"))
1654 0 : CPPUNIT_FAIL("Wrong formula.");
1655 :
1656 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1657 1 : if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3"))
1658 0 : CPPUNIT_FAIL("Wrong formula.");
1659 :
1660 1 : CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1661 1 : if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4"))
1662 0 : CPPUNIT_FAIL("Wrong formula.");
1663 :
1664 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1665 1 : if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)"))
1666 0 : CPPUNIT_FAIL("Wrong formula.");
1667 :
1668 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
1669 1 : if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)"))
1670 0 : CPPUNIT_FAIL("Wrong formula.");
1671 :
1672 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
1673 1 : if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)"))
1674 0 : CPPUNIT_FAIL("Wrong formula.");
1675 :
1676 : // Make sure the broadcasters are still valid by changing the value of A1.
1677 1 : m_pDoc->SetValue(ScAddress(0,0,0), 20);
1678 :
1679 1 : CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1680 1 : CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1681 1 : CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc->GetValue(ScAddress(0,7,0)));
1682 :
1683 1 : CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1684 1 : CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,10,0)));
1685 1 : CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc->GetValue(ScAddress(0,11,0)));
1686 :
1687 1 : m_pDoc->DeleteTab(0);
1688 1 : }
1689 :
1690 1 : void Test::testFormulaRefUpdateNamedExpression()
1691 : {
1692 1 : m_pDoc->InsertTab(0, "Formula");
1693 :
1694 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1695 :
1696 : // Fill C2:C5 with values.
1697 1 : m_pDoc->SetValue(ScAddress(2,1,0), 1);
1698 1 : m_pDoc->SetValue(ScAddress(2,2,0), 2);
1699 1 : m_pDoc->SetValue(ScAddress(2,3,0), 3);
1700 1 : m_pDoc->SetValue(ScAddress(2,4,0), 4);
1701 :
1702 : // Add a named expression that references the immediate left cell.
1703 1 : ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
1704 1 : CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
1705 : ScRangeData* pName = new ScRangeData(
1706 1 : m_pDoc, "ToLeft", "RC[-1]", ScAddress(2,1,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
1707 :
1708 1 : bool bInserted = pGlobalNames->insert(pName);
1709 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
1710 :
1711 : // Insert formulas in D2:D5 using the named expression.
1712 1 : m_pDoc->SetString(ScAddress(3,1,0), "=ToLeft");
1713 1 : m_pDoc->SetString(ScAddress(3,2,0), "=ToLeft");
1714 1 : m_pDoc->SetString(ScAddress(3,3,0), "=ToLeft");
1715 1 : m_pDoc->SetString(ScAddress(3,4,0), "=ToLeft");
1716 :
1717 : // Make sure the results are correct.
1718 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
1719 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
1720 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
1721 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
1722 :
1723 : // Push cells in column C down by one cell.
1724 1 : m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
1725 :
1726 : // Make sure the results change accordingly.
1727 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(3,1,0));
1728 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,2,0));
1729 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,3,0));
1730 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,4,0));
1731 :
1732 : // Move cells back.
1733 1 : m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
1734 :
1735 : // Make sure the results are back as well.
1736 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
1737 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
1738 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
1739 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
1740 :
1741 : // Fill B10:B12 with values.
1742 1 : m_pDoc->SetValue(ScAddress(1,9,0), 10);
1743 1 : m_pDoc->SetValue(ScAddress(1,10,0), 11);
1744 1 : m_pDoc->SetValue(ScAddress(1,11,0), 12);
1745 :
1746 : // Insert a new named expression that references these values as absolute range.
1747 : pName = new ScRangeData(
1748 1 : m_pDoc, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE);
1749 1 : bInserted = pGlobalNames->insert(pName);
1750 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
1751 :
1752 : // Set formula at C8 that references this named expression.
1753 1 : m_pDoc->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
1754 1 : CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
1755 :
1756 : // Shift B10:B12 to right by 2 columns.
1757 1 : m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
1758 :
1759 : // This should shift the absolute range B10:B12 that MyRange references.
1760 1 : pName = pGlobalNames->findByUpperName("MYRANGE");
1761 1 : CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
1762 2 : OUString aExpr;
1763 1 : pName->GetSymbol(aExpr);
1764 1 : CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr);
1765 :
1766 : // This move shouldn't affect the value of C8.
1767 1 : ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,7,0));
1768 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1769 1 : CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
1770 :
1771 : // Update the value of D10 and make sure C8 gets updated.
1772 1 : m_pDoc->SetValue(ScAddress(3,9,0), 20);
1773 1 : CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(2,7,0)));
1774 :
1775 : // Insert a new sheet before the current.
1776 1 : m_pDoc->InsertTab(0, "New");
1777 2 : OUString aName;
1778 1 : m_pDoc->GetName(1, aName);
1779 1 : CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
1780 :
1781 1 : pName = pGlobalNames->findByUpperName("MYRANGE");
1782 1 : CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
1783 :
1784 1 : m_pDoc->SetValue(ScAddress(3,9,1), 10);
1785 1 : CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
1786 :
1787 : // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
1788 1 : m_pDoc->DeleteTab(0);
1789 :
1790 1 : aName = OUString();
1791 1 : m_pDoc->GetName(0, aName);
1792 1 : CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
1793 :
1794 1 : pName = pGlobalNames->findByUpperName("MYRANGE");
1795 1 : CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
1796 :
1797 1 : m_pDoc->SetValue(ScAddress(3,9,0), 11);
1798 1 : CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
1799 :
1800 : // Clear all and start over.
1801 1 : clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
1802 1 : pGlobalNames->clear();
1803 :
1804 : pName = new ScRangeData(
1805 1 : m_pDoc, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE);
1806 1 : bInserted = pGlobalNames->insert(pName);
1807 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
1808 1 : pName->GetSymbol(aExpr);
1809 1 : CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
1810 :
1811 : // Insert range of cells to shift right. The range partially overlaps the named range.
1812 1 : m_pDoc->InsertCol(ScRange(2,4,0,3,8,0));
1813 :
1814 : // This should not alter the range.
1815 1 : pName->GetSymbol(aExpr);
1816 1 : CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
1817 :
1818 2 : m_pDoc->DeleteTab(0);
1819 1 : }
1820 :
1821 1 : void Test::testFormulaRefUpdateNamedExpressionMove()
1822 : {
1823 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1824 :
1825 1 : m_pDoc->InsertTab(0, "Test");
1826 :
1827 : // Set values to B2:B4.
1828 1 : m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
1829 1 : m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
1830 1 : m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
1831 :
1832 : // Set named range for B2:B4.
1833 1 : bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
1834 1 : CPPUNIT_ASSERT(bInserted);
1835 :
1836 : // Set formula in A10.
1837 1 : m_pDoc->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
1838 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1839 :
1840 1 : ScRangeData* pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
1841 1 : CPPUNIT_ASSERT(pData);
1842 2 : OUString aSymbol;
1843 1 : pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
1844 1 : CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
1845 :
1846 : // Move B2:B4 to D3.
1847 1 : ScDocFunc& rFunc = getDocShell().GetDocFunc();
1848 1 : bool bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
1849 1 : CPPUNIT_ASSERT(bMoved);
1850 :
1851 : // The named range should have moved as well.
1852 1 : pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
1853 1 : CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
1854 :
1855 : // The value of A10 should remain unchanged.
1856 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1857 :
1858 1 : SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1859 1 : CPPUNIT_ASSERT(pUndoMgr);
1860 :
1861 : // Undo and check.
1862 1 : pUndoMgr->Undo();
1863 :
1864 1 : pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
1865 1 : CPPUNIT_ASSERT(pData);
1866 1 : pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
1867 1 : CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
1868 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1869 :
1870 : // Redo and check.
1871 1 : pUndoMgr->Redo();
1872 :
1873 1 : pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
1874 1 : CPPUNIT_ASSERT(pData);
1875 1 : pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
1876 1 : CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
1877 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1878 :
1879 : // Undo again to bring it back to the initial condition, and clear the undo buffer.
1880 1 : pUndoMgr->Undo();
1881 1 : pUndoMgr->Clear();
1882 :
1883 : // Add an identical formula to A11 and make a formula group over A10:A11.
1884 1 : m_pDoc->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
1885 1 : ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,0));
1886 1 : CPPUNIT_ASSERT(pFC);
1887 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
1888 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
1889 :
1890 : // Move B2:B4 to D3 again.
1891 1 : bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
1892 1 : CPPUNIT_ASSERT(bMoved);
1893 :
1894 : // Values of A10 and A11 should remain the same.
1895 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
1896 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
1897 :
1898 2 : m_pDoc->DeleteTab(0);
1899 1 : }
1900 :
1901 1 : void Test::testFormulaRefUpdateNamedExpressionExpandRef()
1902 : {
1903 1 : m_pDoc->InsertTab(0, "Test");
1904 1 : m_pDoc->SetExpandRefs(true); // turn on automatic range expansion.
1905 :
1906 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1907 :
1908 1 : bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
1909 1 : CPPUNIT_ASSERT(bInserted);
1910 :
1911 : // Set values to A1:A3.
1912 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
1913 1 : m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
1914 1 : m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
1915 :
1916 1 : m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
1917 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1918 :
1919 : // Insert a new row at row 4, which should expand the named range to A1:A4.
1920 1 : ScDocFunc& rFunc = getDocShell().GetDocFunc();
1921 2 : ScMarkData aMark;
1922 1 : aMark.SelectOneTable(0);
1923 1 : rFunc.InsertCells(ScRange(0,3,0,MAXCOL,3,0), &aMark, INS_INSROWS, false, true, false);
1924 1 : ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
1925 1 : CPPUNIT_ASSERT(pName);
1926 2 : OUString aSymbol;
1927 1 : pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
1928 1 : CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
1929 :
1930 : // Make sure the listening area has been expanded as well. Note the
1931 : // formula cell has been pushed downward by one cell.
1932 1 : m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
1933 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
1934 :
1935 : // Clear the document and start over.
1936 1 : m_pDoc->GetRangeName()->clear();
1937 1 : clearSheet(m_pDoc, 0);
1938 :
1939 : // Set values to B4:B6.
1940 1 : m_pDoc->SetValue(ScAddress(1,3,0), 1.0);
1941 1 : m_pDoc->SetValue(ScAddress(1,4,0), 2.0);
1942 1 : m_pDoc->SetValue(ScAddress(1,5,0), 3.0);
1943 :
1944 1 : bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
1945 1 : CPPUNIT_ASSERT(bInserted);
1946 :
1947 : // Set formula to A1.
1948 1 : m_pDoc->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
1949 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,0,0));
1950 :
1951 : // Insert rows over 3:5 which should expand the range by 3 rows.
1952 1 : rFunc.InsertCells(ScRange(0,2,0,MAXCOL,4,0), &aMark, INS_INSROWS, false, true, false);
1953 :
1954 1 : pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
1955 1 : CPPUNIT_ASSERT(pName);
1956 :
1957 1 : pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
1958 1 : CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
1959 :
1960 2 : m_pDoc->DeleteTab(0);
1961 1 : }
1962 :
1963 1 : void Test::testMultipleOperations()
1964 : {
1965 1 : m_pDoc->InsertTab(0, "MultiOp");
1966 :
1967 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1968 :
1969 : // Insert the reference formula at top row.
1970 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1);
1971 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1*10");
1972 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
1973 :
1974 : // Insert variable inputs in A3:A5.
1975 1 : m_pDoc->SetValue(ScAddress(0,2,0), 2);
1976 1 : m_pDoc->SetValue(ScAddress(0,3,0), 3);
1977 1 : m_pDoc->SetValue(ScAddress(0,4,0), 4);
1978 :
1979 : // Set multiple operations range.
1980 1 : ScTabOpParam aParam;
1981 1 : aParam.aRefFormulaCell = ScRefAddress(1,0,0,false,false,false);
1982 1 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
1983 1 : aParam.aRefColCell = ScRefAddress(0,0,0,false,false,false);
1984 2 : ScMarkData aMark;
1985 1 : aMark.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
1986 1 : m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
1987 1 : CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(1,2,0));
1988 1 : CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,3,0));
1989 1 : CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,4,0));
1990 :
1991 : // Clear A3:B5.
1992 1 : clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
1993 :
1994 : // This time, use indirect reference formula cell.
1995 1 : m_pDoc->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
1996 1 : CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
1997 :
1998 : // Insert variable inputs in A3:A5.
1999 1 : m_pDoc->SetValue(ScAddress(0,2,0), 3);
2000 1 : m_pDoc->SetValue(ScAddress(0,3,0), 4);
2001 1 : m_pDoc->SetValue(ScAddress(0,4,0), 5);
2002 :
2003 : // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
2004 1 : aParam.aRefFormulaCell.Set(2,0,0,false,false,false);
2005 1 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
2006 1 : m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
2007 1 : CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,2,0));
2008 1 : CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,3,0));
2009 1 : CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(1,4,0));
2010 :
2011 2 : m_pDoc->DeleteTab(0);
2012 1 : }
2013 :
2014 1 : void Test::testFuncCOLUMN()
2015 : {
2016 1 : m_pDoc->InsertTab(0, "Formula");
2017 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2018 :
2019 1 : m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
2020 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
2021 :
2022 1 : m_pDoc->SetString(ScAddress(0,1,0), "=F11");
2023 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2024 :
2025 : // Move the formula cell with COLUMN() function to change its value.
2026 1 : m_pDoc->InsertCol(ScRange(5,0,0,5,MAXROW,0));
2027 1 : CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(6,10,0)));
2028 :
2029 : // The cell that references the moved cell should update its value as well.
2030 1 : CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2031 :
2032 : // Move the column in the other direction.
2033 1 : m_pDoc->DeleteCol(ScRange(5,0,0,5,MAXROW,0));
2034 :
2035 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
2036 :
2037 : // The cell that references the moved cell should update its value as well.
2038 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2039 :
2040 1 : m_pDoc->DeleteTab(0);
2041 1 : }
2042 :
2043 1 : void Test::testFuncCOUNT()
2044 : {
2045 1 : m_pDoc->InsertTab(0, "Formula");
2046 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2047 :
2048 1 : m_pDoc->SetValue(ScAddress(0,0,0), 2); // A1
2049 1 : m_pDoc->SetValue(ScAddress(0,1,0), 4); // A2
2050 1 : m_pDoc->SetValue(ScAddress(0,2,0), 6); // A3
2051 :
2052 1 : ScAddress aPos(1,0,0);
2053 1 : m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
2054 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
2055 :
2056 1 : aPos.IncRow();
2057 1 : m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
2058 1 : CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
2059 :
2060 1 : aPos.IncRow();
2061 1 : m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
2062 1 : CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
2063 :
2064 1 : aPos.IncRow();
2065 1 : m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
2066 1 : CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
2067 :
2068 1 : m_pDoc->DeleteTab(0);
2069 1 : }
2070 :
2071 1 : void Test::testFuncROW()
2072 : {
2073 1 : m_pDoc->InsertTab(0, "Formula");
2074 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2075 :
2076 1 : m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
2077 1 : CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
2078 :
2079 1 : m_pDoc->SetString(ScAddress(0,1,0), "=F11");
2080 1 : CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2081 :
2082 : // Insert 2 new rows at row 4.
2083 1 : m_pDoc->InsertRow(ScRange(0,3,0,MAXCOL,4,0));
2084 1 : CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(5,12,0)));
2085 :
2086 : // The cell that references the moved cell should update its value as well.
2087 1 : CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2088 :
2089 : // Delete 2 rows to move it back.
2090 1 : m_pDoc->DeleteRow(ScRange(0,3,0,MAXCOL,4,0));
2091 :
2092 1 : CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
2093 :
2094 : // The cell that references the moved cell should update its value as well.
2095 1 : CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2096 :
2097 1 : m_pDoc->DeleteTab(0);
2098 1 : }
2099 :
2100 1 : void Test::testFuncSUM()
2101 : {
2102 1 : OUString aTabName("foo");
2103 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2104 1 : m_pDoc->InsertTab (0, aTabName));
2105 :
2106 2 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
2107 :
2108 : // Single argument case.
2109 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1);
2110 1 : m_pDoc->SetValue(ScAddress(0,1,0), 1);
2111 1 : m_pDoc->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
2112 1 : m_pDoc->CalcAll();
2113 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2114 :
2115 : // Multiple argument case.
2116 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1);
2117 1 : m_pDoc->SetValue(ScAddress(0,1,0), 22);
2118 1 : m_pDoc->SetValue(ScAddress(0,2,0), 4);
2119 1 : m_pDoc->SetValue(ScAddress(0,3,0), 5);
2120 1 : m_pDoc->SetValue(ScAddress(0,4,0), 6);
2121 :
2122 1 : m_pDoc->SetValue(ScAddress(1,0,0), 3);
2123 1 : m_pDoc->SetValue(ScAddress(1,1,0), 4);
2124 1 : m_pDoc->SetValue(ScAddress(1,2,0), 5);
2125 1 : m_pDoc->SetValue(ScAddress(1,3,0), 6);
2126 1 : m_pDoc->SetValue(ScAddress(1,4,0), 7);
2127 :
2128 1 : m_pDoc->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
2129 1 : m_pDoc->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
2130 1 : m_pDoc->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
2131 1 : CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(ScAddress(3,0,0)));
2132 1 : CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc->GetValue(ScAddress(3,1,0)));
2133 1 : CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(3,2,0)));
2134 :
2135 : // Clear and start over.
2136 1 : clearRange(m_pDoc, ScRange(0,0,0,3,MAXROW,0));
2137 :
2138 : // SUM needs to take the first error in case the range contains an error.
2139 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2140 1 : m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
2141 1 : m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
2142 1 : m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
2143 1 : CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(ScAddress(0,3,0)));
2144 :
2145 : // Set #DIV/0! error to A3. A4 should also inherit this error.
2146 1 : m_pDoc->SetString(ScAddress(0,2,0), "=1/0");
2147 1 : sal_uInt16 nErr = m_pDoc->GetErrCode(ScAddress(0,2,0));
2148 2 : CPPUNIT_ASSERT_MESSAGE("Cell should have a division by zero error.",
2149 1 : nErr == errDivisionByZero);
2150 1 : nErr = m_pDoc->GetErrCode(ScAddress(0,3,0));
2151 2 : CPPUNIT_ASSERT_MESSAGE("SUM should have also inherited a div-by-zero error.",
2152 1 : nErr == errDivisionByZero);
2153 :
2154 : // Set #NA! to A2. A4 should now inherit this error.
2155 1 : m_pDoc->SetString(ScAddress(0,1,0), "=NA()");
2156 1 : nErr = m_pDoc->GetErrCode(ScAddress(0,1,0));
2157 1 : CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr);
2158 2 : CPPUNIT_ASSERT_MESSAGE("A4 should have inherited the same error as A2.",
2159 1 : nErr == m_pDoc->GetErrCode(ScAddress(0,3,0)));
2160 :
2161 2 : m_pDoc->DeleteTab(0);
2162 1 : }
2163 :
2164 1 : void Test::testFuncPRODUCT()
2165 : {
2166 1 : OUString aTabName("foo");
2167 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2168 1 : m_pDoc->InsertTab (0, aTabName));
2169 :
2170 1 : double val = 1;
2171 : double result;
2172 1 : m_pDoc->SetValue(0, 0, 0, val);
2173 1 : val = 2;
2174 1 : m_pDoc->SetValue(0, 1, 0, val);
2175 1 : val = 3;
2176 1 : m_pDoc->SetValue(0, 2, 0, val);
2177 1 : m_pDoc->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
2178 1 : m_pDoc->CalcAll();
2179 1 : m_pDoc->GetValue(0, 3, 0, result);
2180 1 : CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT failed", result == 6.0);
2181 :
2182 1 : m_pDoc->SetString(0, 4, 0, OUString("=PRODUCT({1;2;3})"));
2183 1 : m_pDoc->CalcAll();
2184 1 : m_pDoc->GetValue(0, 4, 0, result);
2185 1 : CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT with inline array failed", result == 6.0);
2186 :
2187 1 : m_pDoc->DeleteTab(0);
2188 1 : }
2189 :
2190 1 : void Test::testFuncSUMPRODUCT()
2191 : {
2192 1 : m_pDoc->InsertTab(0, "Test");
2193 :
2194 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
2195 :
2196 1 : ScAddress aPos(0,0,0);
2197 1 : m_pDoc->SetString(aPos, "=SUMPRODUCT(B1:B3;C1:C3)");
2198 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
2199 1 : m_pDoc->SetValue(ScAddress(2,0,0), 1.0); // C1
2200 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
2201 1 : m_pDoc->SetValue(ScAddress(1,0,0), 1.0); // B1
2202 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
2203 1 : m_pDoc->SetValue(ScAddress(1,1,0), 2.0); // B2
2204 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
2205 1 : m_pDoc->SetValue(ScAddress(2,1,0), 3.0); // C2
2206 1 : CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(aPos));
2207 1 : m_pDoc->SetValue(ScAddress(2,2,0), -2.0); // C3
2208 1 : CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(aPos));
2209 1 : m_pDoc->SetValue(ScAddress(1,2,0), 5.0); // B3
2210 1 : CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc->GetValue(aPos));
2211 :
2212 1 : m_pDoc->DeleteTab(0);
2213 1 : }
2214 :
2215 1 : void Test::testFuncN()
2216 : {
2217 1 : OUString aTabName("foo");
2218 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2219 1 : m_pDoc->InsertTab (0, aTabName));
2220 :
2221 : double result;
2222 :
2223 : // Clear the area first.
2224 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
2225 :
2226 : // Put values to reference.
2227 1 : double val = 0;
2228 1 : m_pDoc->SetValue(0, 0, 0, val);
2229 1 : m_pDoc->SetString(0, 2, 0, OUString("Text"));
2230 1 : val = 1;
2231 1 : m_pDoc->SetValue(0, 3, 0, val);
2232 1 : val = -1;
2233 1 : m_pDoc->SetValue(0, 4, 0, val);
2234 1 : val = 12.3;
2235 1 : m_pDoc->SetValue(0, 5, 0, val);
2236 1 : m_pDoc->SetString(0, 6, 0, OUString("'12.3"));
2237 :
2238 : // Cell references
2239 1 : m_pDoc->SetString(1, 0, 0, OUString("=N(A1)"));
2240 1 : m_pDoc->SetString(1, 1, 0, OUString("=N(A2)"));
2241 1 : m_pDoc->SetString(1, 2, 0, OUString("=N(A3)"));
2242 1 : m_pDoc->SetString(1, 3, 0, OUString("=N(A4)"));
2243 1 : m_pDoc->SetString(1, 4, 0, OUString("=N(A5)"));
2244 1 : m_pDoc->SetString(1, 5, 0, OUString("=N(A6)"));
2245 1 : m_pDoc->SetString(1, 6, 0, OUString("=N(A9)"));
2246 :
2247 : // In-line values
2248 1 : m_pDoc->SetString(1, 7, 0, OUString("=N(0)"));
2249 1 : m_pDoc->SetString(1, 8, 0, OUString("=N(1)"));
2250 1 : m_pDoc->SetString(1, 9, 0, OUString("=N(-1)"));
2251 1 : m_pDoc->SetString(1, 10, 0, OUString("=N(123)"));
2252 1 : m_pDoc->SetString(1, 11, 0, OUString("=N(\"\")"));
2253 1 : m_pDoc->SetString(1, 12, 0, OUString("=N(\"12\")"));
2254 1 : m_pDoc->SetString(1, 13, 0, OUString("=N(\"foo\")"));
2255 :
2256 : // Range references
2257 1 : m_pDoc->SetString(2, 2, 0, OUString("=N(A1:A8)"));
2258 1 : m_pDoc->SetString(2, 3, 0, OUString("=N(A1:A8)"));
2259 1 : m_pDoc->SetString(2, 4, 0, OUString("=N(A1:A8)"));
2260 1 : m_pDoc->SetString(2, 5, 0, OUString("=N(A1:A8)"));
2261 :
2262 : // Calculate and check the results.
2263 1 : m_pDoc->CalcAll();
2264 : double checks1[] = {
2265 : 0, 0, 0, 1, -1, 12.3, 0, // cell reference
2266 : 0, 1, -1, 123, 0, 0, 0 // in-line values
2267 1 : };
2268 15 : for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
2269 : {
2270 14 : m_pDoc->GetValue(1, i, 0, result);
2271 14 : bool bGood = result == checks1[i];
2272 14 : if (!bGood)
2273 : {
2274 0 : cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
2275 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
2276 : }
2277 : }
2278 : double checks2[] = {
2279 : 0, 1, -1, 12.3 // range references
2280 1 : };
2281 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
2282 : {
2283 4 : m_pDoc->GetValue(1, i+2, 0, result);
2284 4 : bool bGood = result == checks2[i];
2285 4 : if (!bGood)
2286 : {
2287 0 : cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
2288 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
2289 : }
2290 : }
2291 :
2292 1 : m_pDoc->DeleteTab(0);
2293 1 : }
2294 :
2295 1 : void Test::testFuncCOUNTIF()
2296 : {
2297 : // COUNTIF (test case adopted from OOo i#36381)
2298 :
2299 1 : OUString aTabName("foo");
2300 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2301 1 : m_pDoc->InsertTab (0, aTabName));
2302 :
2303 : // Empty A1:A39 first.
2304 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
2305 :
2306 : // Raw data (rows 1 through 9)
2307 : const char* aData[] = {
2308 : "1999",
2309 : "2000",
2310 : "0",
2311 : "0",
2312 : "0",
2313 : "2002",
2314 : "2001",
2315 : "X",
2316 : "2002"
2317 1 : };
2318 :
2319 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
2320 10 : for (SCROW i = 0; i < nRows; ++i)
2321 9 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
2322 :
2323 1 : printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
2324 :
2325 : // formulas and results
2326 : struct {
2327 : const char* pFormula; double fResult;
2328 : } aChecks[] = {
2329 : { "=COUNTIF(A1:A12;1999)", 1 },
2330 : { "=COUNTIF(A1:A12;2002)", 2 },
2331 : { "=COUNTIF(A1:A12;1998)", 0 },
2332 : { "=COUNTIF(A1:A12;\">=1999\")", 5 },
2333 : { "=COUNTIF(A1:A12;\">1999\")", 4 },
2334 : { "=COUNTIF(A1:A12;\"<2001\")", 5 },
2335 : { "=COUNTIF(A1:A12;\">0\")", 5 },
2336 : { "=COUNTIF(A1:A12;\">=0\")", 8 },
2337 : { "=COUNTIF(A1:A12;0)", 3 },
2338 : { "=COUNTIF(A1:A12;\"X\")", 1 },
2339 : { "=COUNTIF(A1:A12;)", 3 }
2340 1 : };
2341 :
2342 1 : nRows = SAL_N_ELEMENTS(aChecks);
2343 12 : for (SCROW i = 0; i < nRows; ++i)
2344 : {
2345 11 : SCROW nRow = 20 + i;
2346 11 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
2347 : }
2348 1 : m_pDoc->CalcAll();
2349 :
2350 12 : for (SCROW i = 0; i < nRows; ++i)
2351 : {
2352 : double result;
2353 11 : SCROW nRow = 20 + i;
2354 11 : m_pDoc->GetValue(0, nRow, 0, result);
2355 11 : bool bGood = result == aChecks[i].fResult;
2356 11 : if (!bGood)
2357 : {
2358 0 : cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
2359 0 : << " expected=" << aChecks[i].fResult << " actual=" << result << endl;
2360 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
2361 : }
2362 : }
2363 :
2364 : // Don't count empty strings when searching for a number.
2365 :
2366 : // Clear A1:A2.
2367 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
2368 :
2369 1 : m_pDoc->SetString(0, 0, 0, OUString("=\"\""));
2370 1 : m_pDoc->SetString(0, 1, 0, OUString("=COUNTIF(A1;1)"));
2371 1 : m_pDoc->CalcAll();
2372 :
2373 1 : double result = m_pDoc->GetValue(0, 1, 0);
2374 1 : CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
2375 :
2376 1 : m_pDoc->DeleteTab(0);
2377 1 : }
2378 :
2379 1 : void Test::testFuncIFERROR()
2380 : {
2381 : // IFERROR/IFNA (fdo#56124)
2382 :
2383 1 : OUString aTabName("foo");
2384 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2385 1 : m_pDoc->InsertTab (0, aTabName));
2386 :
2387 : // Empty A1:A39 first.
2388 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
2389 :
2390 : // Raw data (rows 1 through 12)
2391 : const char* aData[] = {
2392 : "1",
2393 : "e",
2394 : "=SQRT(4)",
2395 : "=SQRT(-2)",
2396 : "=A4",
2397 : "=1/0",
2398 : "=NA()",
2399 : "bar",
2400 : "4",
2401 : "gee",
2402 : "=1/0",
2403 : "23"
2404 1 : };
2405 :
2406 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
2407 13 : for (SCROW i = 0; i < nRows; ++i)
2408 12 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
2409 :
2410 1 : printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
2411 :
2412 : // formulas and results
2413 : struct {
2414 : const char* pFormula; const char* pResult;
2415 : } aChecks[] = {
2416 : { "=IFERROR(A1;9)", "1" },
2417 : { "=IFERROR(A2;9)", "e" },
2418 : { "=IFERROR(A3;9)", "2" },
2419 : { "=IFERROR(A4;-7)", "-7" },
2420 : { "=IFERROR(A5;-7)", "-7" },
2421 : { "=IFERROR(A6;-7)", "-7" },
2422 : { "=IFERROR(A7;-7)", "-7" },
2423 : { "=IFNA(A6;9)", "#DIV/0!" },
2424 : { "=IFNA(A7;-7)", "-7" },
2425 : { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)", "4" },
2426 : { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
2427 : { "{=IFERROR(3*A11:A12;1998)}[0]", "1998" }, // um.. this is not the correct way to insert a
2428 : { "{=IFERROR(3*A11:A12;1998)}[1]", "69" } // matrix formula, just a place holder, see below
2429 1 : };
2430 :
2431 1 : nRows = SAL_N_ELEMENTS(aChecks);
2432 12 : for (SCROW i = 0; i < nRows-2; ++i)
2433 : {
2434 11 : SCROW nRow = 20 + i;
2435 11 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
2436 : }
2437 :
2438 : // Create a matrix range in last two rows of the range above, actual data
2439 : // of the placeholders.
2440 2 : ScMarkData aMark;
2441 1 : aMark.SelectOneTable(0);
2442 1 : m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)", NULL);
2443 :
2444 1 : m_pDoc->CalcAll();
2445 :
2446 14 : for (SCROW i = 0; i < nRows; ++i)
2447 : {
2448 13 : SCROW nRow = 20 + i;
2449 13 : OUString aResult = m_pDoc->GetString(0, nRow, 0);
2450 26 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
2451 13 : aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
2452 13 : }
2453 :
2454 2 : m_pDoc->DeleteTab(0);
2455 1 : }
2456 :
2457 1 : void Test::testFuncSHEET()
2458 : {
2459 1 : OUString aTabName1("test1");
2460 2 : OUString aTabName2("test2");
2461 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2462 1 : m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
2463 :
2464 1 : m_pDoc->SetString(0, 0, 0, OUString("=SHEETS()"));
2465 1 : m_pDoc->CalcFormulaTree(false, false);
2466 : double original;
2467 1 : m_pDoc->GetValue(0, 0, 0, original);
2468 :
2469 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
2470 1 : static_cast<SCTAB>(original) == m_pDoc->GetTableCount());
2471 :
2472 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2473 1 : m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
2474 :
2475 : double modified;
2476 1 : m_pDoc->GetValue(0, 0, 0, modified);
2477 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
2478 1 : modified - original == 1.0);
2479 :
2480 1 : SCTAB nTabCount = m_pDoc->GetTableCount();
2481 1 : m_pDoc->DeleteTab(--nTabCount);
2482 :
2483 1 : m_pDoc->GetValue(0, 0, 0, modified);
2484 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
2485 1 : modified - original == 0.0);
2486 :
2487 2 : m_pDoc->DeleteTab(--nTabCount);
2488 1 : }
2489 :
2490 1 : void Test::testFuncNOW()
2491 : {
2492 1 : OUString aTabName("foo");
2493 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2494 1 : m_pDoc->InsertTab (0, aTabName));
2495 :
2496 1 : double val = 1;
2497 1 : m_pDoc->SetValue(0, 0, 0, val);
2498 1 : m_pDoc->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
2499 : double now1;
2500 1 : m_pDoc->GetValue(0, 1, 0, now1);
2501 1 : CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
2502 :
2503 1 : val = 0;
2504 1 : m_pDoc->SetValue(0, 0, 0, val);
2505 1 : m_pDoc->CalcFormulaTree(false, false);
2506 : double zero;
2507 1 : m_pDoc->GetValue(0, 1, 0, zero);
2508 1 : CPPUNIT_ASSERT_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", zero == 0.0);
2509 :
2510 1 : val = 1;
2511 1 : m_pDoc->SetValue(0, 0, 0, val);
2512 1 : m_pDoc->CalcFormulaTree(false, false);
2513 : double now2;
2514 1 : m_pDoc->GetValue(0, 1, 0, now2);
2515 1 : CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
2516 :
2517 1 : m_pDoc->DeleteTab(0);
2518 1 : }
2519 :
2520 1 : void Test::testFuncNUMBERVALUE()
2521 : {
2522 : // NUMBERVALUE fdo#57180
2523 :
2524 1 : OUString aTabName("foo");
2525 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2526 1 : m_pDoc->InsertTab (0, aTabName));
2527 :
2528 : // Empty A1:A39 first.
2529 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
2530 :
2531 : // Raw data (rows 1 through 6)
2532 : const char* aData[] = {
2533 : "1ag9a9b9",
2534 : "1ag34 5g g6 78b9%%",
2535 : "1 234d56E-2",
2536 : "d4",
2537 : "54.4",
2538 : "1a2b3e1%"
2539 1 : };
2540 :
2541 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
2542 7 : for (SCROW i = 0; i < nRows; ++i)
2543 6 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
2544 :
2545 1 : printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
2546 :
2547 : // formulas and results
2548 : struct {
2549 : const char* pFormula; const char* pResult;
2550 : } aChecks[] = {
2551 : { "=NUMBERVALUE(A1;\"b\";\"ag\")", "199.9" },
2552 : { "=NUMBERVALUE(A2;\"b\";\"ag\")", "134.56789" },
2553 : { "=NUMBERVALUE(A2;\"b\";\"g\")", "#VALUE!" },
2554 : { "=NUMBERVALUE(A3;\"d\")", "12.3456" },
2555 : { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
2556 : { "=NUMBERVALUE(A4;)", "Err:502" },
2557 : { "=NUMBERVALUE(A5;)", "Err:502" },
2558 : { "=NUMBERVALUE(A6;\"b\";\"a\")", "1.23" }
2559 1 : };
2560 :
2561 1 : nRows = SAL_N_ELEMENTS(aChecks);
2562 9 : for (SCROW i = 0; i < nRows; ++i)
2563 : {
2564 8 : SCROW nRow = 20 + i;
2565 8 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
2566 : }
2567 1 : m_pDoc->CalcAll();
2568 :
2569 9 : for (SCROW i = 0; i < nRows; ++i)
2570 : {
2571 8 : SCROW nRow = 20 + i;
2572 8 : OUString aResult = m_pDoc->GetString(0, nRow, 0);
2573 16 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
2574 8 : aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
2575 8 : }
2576 :
2577 1 : m_pDoc->DeleteTab(0);
2578 1 : }
2579 :
2580 1 : void Test::testFuncLOOKUP()
2581 : {
2582 1 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
2583 :
2584 1 : m_pDoc->InsertTab(0, "Test");
2585 :
2586 : // Raw data
2587 : const char* aData[][2] = {
2588 : { "=CONCATENATE(\"A\")", "1" },
2589 : { "=CONCATENATE(\"B\")", "2" },
2590 : { "=CONCATENATE(\"C\")", "3" },
2591 : { 0, 0 } // terminator
2592 1 : };
2593 :
2594 : // Insert raw data into A1:B3.
2595 4 : for (SCROW i = 0; aData[i][0]; ++i)
2596 : {
2597 3 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
2598 3 : m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
2599 : }
2600 :
2601 : const char* aData2[][2] = {
2602 : { "A", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
2603 : { "B", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
2604 : { "C", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
2605 : { 0, 0 } // terminator
2606 1 : };
2607 :
2608 : // Insert check formulas into A5:B7.
2609 4 : for (SCROW i = 0; aData2[i][0]; ++i)
2610 : {
2611 3 : m_pDoc->SetString(0, i+4, 0, OUString::createFromAscii(aData2[i][0]));
2612 3 : m_pDoc->SetString(1, i+4, 0, OUString::createFromAscii(aData2[i][1]));
2613 : }
2614 :
2615 1 : printRange(m_pDoc, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
2616 :
2617 : // Values for B5:B7 should be 1, 2, and 3.
2618 1 : CPPUNIT_ASSERT_MESSAGE("This formula should not have an error code.", m_pDoc->GetErrCode(ScAddress(1,4,0)) == 0);
2619 1 : CPPUNIT_ASSERT_MESSAGE("This formula should not have an error code.", m_pDoc->GetErrCode(ScAddress(1,5,0)) == 0);
2620 1 : CPPUNIT_ASSERT_MESSAGE("This formula should not have an error code.", m_pDoc->GetErrCode(ScAddress(1,6,0)) == 0);
2621 :
2622 1 : CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2623 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,5,0)));
2624 1 : CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,6,0)));
2625 :
2626 1 : m_pDoc->DeleteTab(0);
2627 1 : }
2628 :
2629 1 : void Test::testFuncVLOOKUP()
2630 : {
2631 : // VLOOKUP
2632 :
2633 1 : OUString aTabName("foo");
2634 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2635 1 : m_pDoc->InsertTab (0, aTabName));
2636 :
2637 : // Clear A1:F40.
2638 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
2639 :
2640 : // Raw data
2641 : const char* aData[][2] = {
2642 : { "Key", "Val" },
2643 : { "10", "3" },
2644 : { "20", "4" },
2645 : { "30", "5" },
2646 : { "40", "6" },
2647 : { "50", "7" },
2648 : { "60", "8" },
2649 : { "70", "9" },
2650 : { "B", "10" },
2651 : { "B", "11" },
2652 : { "C", "12" },
2653 : { "D", "13" },
2654 : { "E", "14" },
2655 : { "F", "15" },
2656 : { 0, 0 } // terminator
2657 1 : };
2658 :
2659 : // Insert raw data into A1:B14.
2660 15 : for (SCROW i = 0; aData[i][0]; ++i)
2661 : {
2662 14 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
2663 14 : m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
2664 : }
2665 :
2666 1 : printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
2667 :
2668 : // Formula data
2669 : struct {
2670 : const char* pLookup; const char* pFormula; const char* pRes;
2671 : } aChecks[] = {
2672 : { "Lookup", "Formula", 0 },
2673 : { "12", "=VLOOKUP(D2;A2:B14;2;1)", "3" },
2674 : { "29", "=VLOOKUP(D3;A2:B14;2;1)", "4" },
2675 : { "31", "=VLOOKUP(D4;A2:B14;2;1)", "5" },
2676 : { "45", "=VLOOKUP(D5;A2:B14;2;1)", "6" },
2677 : { "56", "=VLOOKUP(D6;A2:B14;2;1)", "7" },
2678 : { "65", "=VLOOKUP(D7;A2:B14;2;1)", "8" },
2679 : { "78", "=VLOOKUP(D8;A2:B14;2;1)", "9" },
2680 : { "Andy", "=VLOOKUP(D9;A2:B14;2;1)", "#N/A" },
2681 : { "Bruce", "=VLOOKUP(D10;A2:B14;2;1)", "11" },
2682 : { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)", "12" },
2683 : { "David", "=VLOOKUP(D12;A2:B14;2;1)", "13" },
2684 : { "Edward", "=VLOOKUP(D13;A2:B14;2;1)", "14" },
2685 : { "Frank", "=VLOOKUP(D14;A2:B14;2;1)", "15" },
2686 : { "Henry", "=VLOOKUP(D15;A2:B14;2;1)", "15" },
2687 : { "100", "=VLOOKUP(D16;A2:B14;2;1)", "9" },
2688 : { "1000", "=VLOOKUP(D17;A2:B14;2;1)", "9" },
2689 : { "Zena", "=VLOOKUP(D18;A2:B14;2;1)", "15" }
2690 1 : };
2691 :
2692 : // Insert formula data into D1:E18.
2693 19 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
2694 : {
2695 18 : m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
2696 18 : m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
2697 : }
2698 1 : m_pDoc->CalcAll();
2699 1 : printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
2700 :
2701 : // Verify results.
2702 19 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
2703 : {
2704 18 : if (i == 0)
2705 : // Skip the header row.
2706 1 : continue;
2707 :
2708 17 : OUString aRes = m_pDoc->GetString(4, i, 0);
2709 17 : bool bGood = aRes.equalsAscii(aChecks[i].pRes);
2710 17 : if (!bGood)
2711 : {
2712 0 : cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
2713 0 : << "' expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
2714 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
2715 : }
2716 17 : }
2717 :
2718 : // Clear the sheet and start over.
2719 1 : clearSheet(m_pDoc, 0);
2720 :
2721 : // Lookup on sorted data intersparsed with empty cells.
2722 :
2723 : // A1:B8 is the search range.
2724 1 : m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
2725 1 : m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
2726 1 : m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
2727 1 : m_pDoc->SetString(ScAddress(1,2,0), "One");
2728 1 : m_pDoc->SetString(ScAddress(1,4,0), "Two");
2729 1 : m_pDoc->SetString(ScAddress(1,7,0), "Four");
2730 :
2731 : // D1:D5 contain match values.
2732 1 : m_pDoc->SetValue(ScAddress(3,0,0), 1.0);
2733 1 : m_pDoc->SetValue(ScAddress(3,1,0), 2.0);
2734 1 : m_pDoc->SetValue(ScAddress(3,2,0), 3.0);
2735 1 : m_pDoc->SetValue(ScAddress(3,3,0), 4.0);
2736 1 : m_pDoc->SetValue(ScAddress(3,4,0), 5.0);
2737 :
2738 : // E1:E5 contain formulas.
2739 1 : m_pDoc->SetString(ScAddress(4,0,0), "=VLOOKUP(D1;$A$1:$B$8;2)");
2740 1 : m_pDoc->SetString(ScAddress(4,1,0), "=VLOOKUP(D2;$A$1:$B$8;2)");
2741 1 : m_pDoc->SetString(ScAddress(4,2,0), "=VLOOKUP(D3;$A$1:$B$8;2)");
2742 1 : m_pDoc->SetString(ScAddress(4,3,0), "=VLOOKUP(D4;$A$1:$B$8;2)");
2743 1 : m_pDoc->SetString(ScAddress(4,4,0), "=VLOOKUP(D5;$A$1:$B$8;2)");
2744 1 : m_pDoc->CalcAll();
2745 :
2746 : // Check the formula results in E1:E5.
2747 1 : CPPUNIT_ASSERT_EQUAL(OUString("One"), m_pDoc->GetString(ScAddress(4,0,0)));
2748 1 : CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,1,0)));
2749 1 : CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,2,0)));
2750 1 : CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,3,0)));
2751 1 : CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,4,0)));
2752 :
2753 : // Start over again.
2754 1 : clearSheet(m_pDoc, 0);
2755 :
2756 : // Set A,B,....,G to A1:A7.
2757 1 : m_pDoc->SetString(ScAddress(0,0,0), "A");
2758 1 : m_pDoc->SetString(ScAddress(0,1,0), "B");
2759 1 : m_pDoc->SetString(ScAddress(0,2,0), "C");
2760 1 : m_pDoc->SetString(ScAddress(0,3,0), "D");
2761 1 : m_pDoc->SetString(ScAddress(0,4,0), "E");
2762 1 : m_pDoc->SetString(ScAddress(0,5,0), "F");
2763 1 : m_pDoc->SetString(ScAddress(0,6,0), "G");
2764 :
2765 : // Set the formula in C1.
2766 1 : m_pDoc->SetString(ScAddress(2,0,0), "=VLOOKUP(\"C\";A1:A16;1)");
2767 1 : CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc->GetString(ScAddress(2,0,0)));
2768 :
2769 1 : m_pDoc->DeleteTab(0);
2770 1 : }
2771 :
2772 : struct NumStrCheck {
2773 : double fVal;
2774 : const char* pRes;
2775 : };
2776 :
2777 : struct StrStrCheck {
2778 : const char* pVal;
2779 : const char* pRes;
2780 : };
2781 :
2782 : template<size_t _DataSize, size_t _FormulaSize, int _Type>
2783 2 : void runTestMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
2784 : {
2785 2 : size_t nDataSize = _DataSize;
2786 26 : for (size_t i = 0; i < nDataSize; ++i)
2787 24 : pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
2788 :
2789 33 : for (size_t i = 0; i < _FormulaSize; ++i)
2790 : {
2791 31 : pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
2792 :
2793 31 : OUStringBuffer aBuf;
2794 31 : aBuf.appendAscii("=MATCH(B");
2795 31 : aBuf.append(static_cast<sal_Int32>(i+1));
2796 31 : aBuf.appendAscii(";A1:A");
2797 31 : aBuf.append(static_cast<sal_Int32>(nDataSize));
2798 31 : aBuf.appendAscii(";");
2799 31 : aBuf.append(static_cast<sal_Int32>(_Type));
2800 31 : aBuf.appendAscii(")");
2801 62 : OUString aFormula = aBuf.makeStringAndClear();
2802 31 : pDoc->SetString(2, i, 0, aFormula);
2803 : }
2804 :
2805 2 : pDoc->CalcAll();
2806 2 : Test::printRange(pDoc, ScRange(0, 0, 0, 2, _FormulaSize-1, 0), "MATCH");
2807 :
2808 : // verify the results.
2809 33 : for (size_t i = 0; i < _FormulaSize; ++i)
2810 : {
2811 31 : OUString aStr = pDoc->GetString(2, i, 0);
2812 31 : if (!aStr.equalsAscii(aChecks[i].pRes))
2813 : {
2814 0 : cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
2815 0 : << " criterion='" << aChecks[i].pVal << "'" << endl;
2816 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
2817 : }
2818 : }
2819 2 : }
2820 :
2821 : template<size_t _DataSize, size_t _FormulaSize, int _Type>
2822 2 : void runTestHorizontalMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
2823 : {
2824 2 : size_t nDataSize = _DataSize;
2825 26 : for (size_t i = 0; i < nDataSize; ++i)
2826 24 : pDoc->SetString(i, 0, 0, OUString::createFromAscii(aData[i]));
2827 :
2828 33 : for (size_t i = 0; i < _FormulaSize; ++i)
2829 : {
2830 31 : pDoc->SetString(i, 1, 0, OUString::createFromAscii(aChecks[i].pVal));
2831 :
2832 : // Assume we don't have more than 26 data columns..
2833 31 : OUStringBuffer aBuf;
2834 31 : aBuf.appendAscii("=MATCH(");
2835 31 : aBuf.append(static_cast<sal_Unicode>('A'+i));
2836 31 : aBuf.appendAscii("2;A1:");
2837 31 : aBuf.append(static_cast<sal_Unicode>('A'+nDataSize));
2838 31 : aBuf.appendAscii("1;");
2839 31 : aBuf.append(static_cast<sal_Int32>(_Type));
2840 31 : aBuf.appendAscii(")");
2841 62 : OUString aFormula = aBuf.makeStringAndClear();
2842 31 : pDoc->SetString(i, 2, 0, aFormula);
2843 : }
2844 :
2845 2 : pDoc->CalcAll();
2846 2 : Test::printRange(pDoc, ScRange(0, 0, 0, _FormulaSize-1, 2, 0), "MATCH");
2847 :
2848 : // verify the results.
2849 33 : for (size_t i = 0; i < _FormulaSize; ++i)
2850 : {
2851 31 : OUString aStr = pDoc->GetString(i, 2, 0);
2852 31 : if (!aStr.equalsAscii(aChecks[i].pRes))
2853 : {
2854 0 : cerr << "column " << char('A'+i) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
2855 0 : << " criterion='" << aChecks[i].pVal << "'" << endl;
2856 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for horizontal MATCH", false);
2857 : }
2858 : }
2859 2 : }
2860 :
2861 1 : void Test::testFuncMATCH()
2862 : {
2863 1 : OUString aTabName("foo");
2864 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2865 1 : m_pDoc->InsertTab (0, aTabName));
2866 :
2867 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 40, 40, 0));
2868 : {
2869 : // Ascending in-exact match
2870 :
2871 : // data range (A1:A9)
2872 : const char* aData[] = {
2873 : "1",
2874 : "2",
2875 : "3",
2876 : "4",
2877 : "5",
2878 : "6",
2879 : "7",
2880 : "8",
2881 : "9",
2882 : "B",
2883 : "B",
2884 : "C",
2885 1 : };
2886 :
2887 : // formula (B1:C12)
2888 : StrStrCheck aChecks[] = {
2889 : { "0.8", "#N/A" },
2890 : { "1.2", "1" },
2891 : { "2.3", "2" },
2892 : { "3.9", "3" },
2893 : { "4.1", "4" },
2894 : { "5.99", "5" },
2895 : { "6.1", "6" },
2896 : { "7.2", "7" },
2897 : { "8.569", "8" },
2898 : { "9.59", "9" },
2899 : { "10", "9" },
2900 : { "100", "9" },
2901 : { "Andy", "#N/A" },
2902 : { "Bruce", "11" },
2903 : { "Charlie", "12" }
2904 1 : };
2905 :
2906 1 : runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
2907 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
2908 1 : runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
2909 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
2910 : }
2911 :
2912 : {
2913 : // Descending in-exact match
2914 :
2915 : // data range (A1:A9)
2916 : const char* aData[] = {
2917 : "D",
2918 : "C",
2919 : "B",
2920 : "9",
2921 : "8",
2922 : "7",
2923 : "6",
2924 : "5",
2925 : "4",
2926 : "3",
2927 : "2",
2928 : "1"
2929 1 : };
2930 :
2931 : // formula (B1:C12)
2932 : StrStrCheck aChecks[] = {
2933 : { "10", "#N/A" },
2934 : { "8.9", "4" },
2935 : { "7.8", "5" },
2936 : { "6.7", "6" },
2937 : { "5.5", "7" },
2938 : { "4.6", "8" },
2939 : { "3.3", "9" },
2940 : { "2.2", "10" },
2941 : { "1.1", "11" },
2942 : { "0.8", "12" },
2943 : { "0", "12" },
2944 : { "-2", "12" },
2945 : { "Andy", "3" },
2946 : { "Bruce", "2" },
2947 : { "Charlie", "1" },
2948 : { "David", "#N/A" }
2949 1 : };
2950 :
2951 1 : runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
2952 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
2953 1 : runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
2954 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
2955 : }
2956 :
2957 : {
2958 : // search range contains leading and trailing empty cell ranges.
2959 :
2960 1 : clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
2961 :
2962 : // A5:A8 contains sorted values.
2963 1 : m_pDoc->SetValue(ScAddress(0,4,0), 1.0);
2964 1 : m_pDoc->SetValue(ScAddress(0,5,0), 2.0);
2965 1 : m_pDoc->SetValue(ScAddress(0,6,0), 3.0);
2966 1 : m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
2967 :
2968 : // Find value 2 which is in A6.
2969 1 : m_pDoc->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
2970 1 : m_pDoc->CalcAll();
2971 :
2972 1 : CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc->GetString(ScAddress(1,0,0)));
2973 : }
2974 :
2975 1 : m_pDoc->DeleteTab(0);
2976 1 : }
2977 :
2978 1 : void Test::testFuncCELL()
2979 : {
2980 1 : OUString aTabName("foo");
2981 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2982 1 : m_pDoc->InsertTab (0, aTabName));
2983 :
2984 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
2985 :
2986 : {
2987 1 : const char* pContent = "Some random text";
2988 1 : m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
2989 1 : double val = 1.2;
2990 1 : m_pDoc->SetValue(2, 0, 0, val); // Set numeric value to C1;
2991 :
2992 : // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
2993 : StrStrCheck aChecks[] = {
2994 : { "=CELL(\"COL\";C10)", "3" },
2995 : { "=CELL(\"ROW\";C10)", "10" },
2996 : { "=CELL(\"SHEET\";C10)", "1" },
2997 : { "=CELL(\"ADDRESS\";C10)", "$C$10" },
2998 : { "=CELL(\"CONTENTS\";C10)", pContent },
2999 : { "=CELL(\"COLOR\";C10)", "0" },
3000 : { "=CELL(\"TYPE\";C9)", "b" },
3001 : { "=CELL(\"TYPE\";C10)", "l" },
3002 : { "=CELL(\"TYPE\";C1)", "v" },
3003 : { "=CELL(\"PARENTHESES\";C10)", "0" }
3004 1 : };
3005 :
3006 11 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3007 10 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
3008 1 : m_pDoc->CalcAll();
3009 :
3010 11 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3011 : {
3012 10 : OUString aVal = m_pDoc->GetString(0, i, 0);
3013 10 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
3014 10 : }
3015 : }
3016 :
3017 1 : m_pDoc->DeleteTab(0);
3018 1 : }
3019 :
3020 : /** See also test case document fdo#44456 sheet cpearson */
3021 1 : void Test::testFuncDATEDIF()
3022 : {
3023 1 : OUString aTabName("foo");
3024 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
3025 1 : m_pDoc->InsertTab (0, aTabName));
3026 :
3027 : const char* aData[][5] = {
3028 : { "2007-01-01", "2007-01-10", "d", "9", "=DATEDIF(A1;B1;C1)" } ,
3029 : { "2007-01-01", "2007-01-31", "m", "0", "=DATEDIF(A2;B2;C2)" } ,
3030 : { "2007-01-01", "2007-02-01", "m", "1", "=DATEDIF(A3;B3;C3)" } ,
3031 : { "2007-01-01", "2007-02-28", "m", "1", "=DATEDIF(A4;B4;C4)" } ,
3032 : { "2007-01-01", "2007-12-31", "d", "364", "=DATEDIF(A5;B5;C5)" } ,
3033 : { "2007-01-01", "2007-01-31", "y", "0", "=DATEDIF(A6;B6;C6)" } ,
3034 : { "2007-01-01", "2008-07-01", "d", "547", "=DATEDIF(A7;B7;C7)" } ,
3035 : { "2007-01-01", "2008-07-01", "m", "18", "=DATEDIF(A8;B8;C8)" } ,
3036 : { "2007-01-01", "2008-07-01", "ym", "6", "=DATEDIF(A9;B9;C9)" } ,
3037 : { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
3038 : { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
3039 : { "2007-01-01", "2007-01-31", "md", "30", "=DATEDIF(A12;B12;C12)" } ,
3040 : { "2007-02-01", "2009-03-01", "md", "0", "=DATEDIF(A13;B13;C13)" } ,
3041 : { "2008-02-01", "2009-03-01", "md", "0", "=DATEDIF(A14;B14;C14)" } ,
3042 : { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" } // fail date1 > date2
3043 1 : };
3044 :
3045 1 : clearRange( m_pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
3046 1 : ScAddress aPos(0,0,0);
3047 1 : ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3048 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3049 :
3050 1 : m_pDoc->CalcAll();
3051 :
3052 16 : for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
3053 : {
3054 15 : OUString aVal = m_pDoc->GetString( 4, i, 0);
3055 : //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
3056 15 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
3057 15 : }
3058 :
3059 1 : m_pDoc->DeleteTab(0);
3060 1 : }
3061 :
3062 1 : void Test::testFuncINDIRECT()
3063 : {
3064 1 : OUString aTabName("foo");
3065 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
3066 1 : m_pDoc->InsertTab (0, aTabName));
3067 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
3068 :
3069 1 : bool bGood = m_pDoc->GetName(0, aTabName);
3070 1 : CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
3071 :
3072 2 : OUString aTest = "Test", aRefErr = "#REF!";
3073 1 : m_pDoc->SetString(0, 10, 0, aTest);
3074 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", m_pDoc->GetString(0,10,0) == aTest);
3075 :
3076 2 : OUString aPrefix = "=INDIRECT(\"";
3077 :
3078 2 : OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
3079 1 : m_pDoc->SetString(0, 0, 0, aFormula);
3080 1 : aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
3081 1 : m_pDoc->SetString(0, 1, 0, aFormula);
3082 1 : aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
3083 1 : m_pDoc->SetString(0, 2, 0, aFormula);
3084 1 : aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
3085 1 : m_pDoc->SetString(0, 3, 0, aFormula);
3086 :
3087 1 : m_pDoc->CalcAll();
3088 : {
3089 : // Default is to use the current formula syntax, which is Calc A1.
3090 : const OUString* aChecks[] = {
3091 : &aTest, &aRefErr, &aRefErr, &aTest
3092 1 : };
3093 :
3094 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3095 : {
3096 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
3097 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
3098 4 : }
3099 : }
3100 :
3101 2 : ScCalcConfig aConfig;
3102 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_OOO;
3103 1 : m_pDoc->SetCalcConfig(aConfig);
3104 1 : m_pDoc->CalcAll();
3105 : {
3106 : // Explicit Calc A1 syntax
3107 : const OUString* aChecks[] = {
3108 : &aTest, &aRefErr, &aRefErr, &aTest
3109 1 : };
3110 :
3111 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3112 : {
3113 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
3114 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
3115 4 : }
3116 : }
3117 :
3118 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_A1;
3119 1 : m_pDoc->SetCalcConfig(aConfig);
3120 1 : m_pDoc->CalcAll();
3121 : {
3122 : // Excel A1 syntax
3123 : const OUString* aChecks[] = {
3124 : &aRefErr, &aTest, &aRefErr, &aTest
3125 1 : };
3126 :
3127 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3128 : {
3129 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
3130 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
3131 4 : }
3132 : }
3133 :
3134 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_R1C1;
3135 1 : m_pDoc->SetCalcConfig(aConfig);
3136 1 : m_pDoc->CalcAll();
3137 : {
3138 : // Excel R1C1 syntax
3139 : const OUString* aChecks[] = {
3140 : &aRefErr, &aRefErr, &aTest, &aTest
3141 1 : };
3142 :
3143 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3144 : {
3145 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
3146 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
3147 4 : }
3148 : }
3149 :
3150 2 : m_pDoc->DeleteTab(0);
3151 1 : }
3152 :
3153 1 : void Test::testExternalRef()
3154 : {
3155 1 : ScDocShellRef xExtDocSh = new ScDocShell;
3156 2 : OUString aExtDocName("file:///extdata.fake");
3157 2 : OUString aExtSh1Name("Data1");
3158 2 : OUString aExtSh2Name("Data2");
3159 2 : OUString aExtSh3Name("Data3");
3160 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
3161 1 : xExtDocSh->DoInitNew(pMed);
3162 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
3163 1 : findLoadedDocShellByName(aExtDocName) != NULL);
3164 :
3165 : // Populate the external source document.
3166 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
3167 1 : pExtDoc->InsertTab(0, aExtSh1Name);
3168 1 : pExtDoc->InsertTab(1, aExtSh2Name);
3169 1 : pExtDoc->InsertTab(2, aExtSh3Name);
3170 :
3171 2 : OUString name("Name");
3172 2 : OUString value("Value");
3173 2 : OUString andy("Andy");
3174 2 : OUString bruce("Bruce");
3175 2 : OUString charlie("Charlie");
3176 2 : OUString david("David");
3177 2 : OUString edward("Edward");
3178 2 : OUString frank("Frank");
3179 2 : OUString george("George");
3180 2 : OUString henry("Henry");
3181 :
3182 : // Sheet 1
3183 1 : pExtDoc->SetString(0, 0, 0, name);
3184 1 : pExtDoc->SetString(0, 1, 0, andy);
3185 1 : pExtDoc->SetString(0, 2, 0, bruce);
3186 1 : pExtDoc->SetString(0, 3, 0, charlie);
3187 1 : pExtDoc->SetString(0, 4, 0, david);
3188 1 : pExtDoc->SetString(1, 0, 0, value);
3189 1 : double val = 10;
3190 1 : pExtDoc->SetValue(1, 1, 0, val);
3191 1 : val = 11;
3192 1 : pExtDoc->SetValue(1, 2, 0, val);
3193 1 : val = 12;
3194 1 : pExtDoc->SetValue(1, 3, 0, val);
3195 1 : val = 13;
3196 1 : pExtDoc->SetValue(1, 4, 0, val);
3197 :
3198 : // Sheet 2 remains empty.
3199 :
3200 : // Sheet 3
3201 1 : pExtDoc->SetString(0, 0, 2, name);
3202 1 : pExtDoc->SetString(0, 1, 2, edward);
3203 1 : pExtDoc->SetString(0, 2, 2, frank);
3204 1 : pExtDoc->SetString(0, 3, 2, george);
3205 1 : pExtDoc->SetString(0, 4, 2, henry);
3206 1 : pExtDoc->SetString(1, 0, 2, value);
3207 1 : val = 99;
3208 1 : pExtDoc->SetValue(1, 1, 2, val);
3209 1 : val = 98;
3210 1 : pExtDoc->SetValue(1, 2, 2, val);
3211 1 : val = 97;
3212 1 : pExtDoc->SetValue(1, 3, 2, val);
3213 1 : val = 96;
3214 1 : pExtDoc->SetValue(1, 4, 2, val);
3215 :
3216 : // Test external refernces on the main document while the external
3217 : // document is still in memory.
3218 1 : m_pDoc->InsertTab(0, OUString("Test Sheet"));
3219 1 : m_pDoc->SetString(0, 0, 0, OUString("='file:///extdata.fake'#Data1.A1"));
3220 2 : OUString test = m_pDoc->GetString(0, 0, 0);
3221 1 : CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(name));
3222 :
3223 : // After the initial access to the external document, the external ref
3224 : // manager should create sheet cache entries for *all* sheets from that
3225 : // document. Note that the doc may have more than 3 sheets but ensure
3226 : // that the first 3 are what we expect.
3227 1 : ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
3228 1 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
3229 2 : vector<OUString> aTabNames;
3230 1 : pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
3231 1 : CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
3232 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[0].equals(aExtSh1Name));
3233 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[1].equals(aExtSh2Name));
3234 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[2].equals(aExtSh3Name));
3235 :
3236 1 : m_pDoc->SetString(1, 0, 0, OUString("='file:///extdata.fake'#Data1.B1"));
3237 1 : test = m_pDoc->GetString(1, 0, 0);
3238 1 : CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(value));
3239 :
3240 1 : m_pDoc->SetString(0, 1, 0, OUString("='file:///extdata.fake'#Data1.A2"));
3241 1 : m_pDoc->SetString(0, 2, 0, OUString("='file:///extdata.fake'#Data1.A3"));
3242 1 : m_pDoc->SetString(0, 3, 0, OUString("='file:///extdata.fake'#Data1.A4"));
3243 1 : m_pDoc->SetString(0, 4, 0, OUString("='file:///extdata.fake'#Data1.A5"));
3244 1 : m_pDoc->SetString(0, 5, 0, OUString("='file:///extdata.fake'#Data1.A6"));
3245 :
3246 : {
3247 : // Referencing an empty cell should display '0'.
3248 1 : const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
3249 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3250 : {
3251 5 : test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
3252 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
3253 : }
3254 : }
3255 1 : m_pDoc->SetString(1, 1, 0, OUString("='file:///extdata.fake'#Data1.B2"));
3256 1 : m_pDoc->SetString(1, 2, 0, OUString("='file:///extdata.fake'#Data1.B3"));
3257 1 : m_pDoc->SetString(1, 3, 0, OUString("='file:///extdata.fake'#Data1.B4"));
3258 1 : m_pDoc->SetString(1, 4, 0, OUString("='file:///extdata.fake'#Data1.B5"));
3259 1 : m_pDoc->SetString(1, 5, 0, OUString("='file:///extdata.fake'#Data1.B6"));
3260 : {
3261 1 : double pChecks[] = { 10, 11, 12, 13, 0 };
3262 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3263 : {
3264 5 : m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
3265 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", val == pChecks[i]);
3266 : }
3267 : }
3268 :
3269 1 : m_pDoc->SetString(2, 0, 0, OUString("='file:///extdata.fake'#Data3.A1"));
3270 1 : m_pDoc->SetString(2, 1, 0, OUString("='file:///extdata.fake'#Data3.A2"));
3271 1 : m_pDoc->SetString(2, 2, 0, OUString("='file:///extdata.fake'#Data3.A3"));
3272 1 : m_pDoc->SetString(2, 3, 0, OUString("='file:///extdata.fake'#Data3.A4"));
3273 : {
3274 1 : const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
3275 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3276 : {
3277 4 : test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
3278 4 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
3279 : }
3280 : }
3281 :
3282 1 : m_pDoc->SetString(3, 0, 0, OUString("='file:///extdata.fake'#Data3.B1"));
3283 1 : m_pDoc->SetString(3, 1, 0, OUString("='file:///extdata.fake'#Data3.B2"));
3284 1 : m_pDoc->SetString(3, 2, 0, OUString("='file:///extdata.fake'#Data3.B3"));
3285 1 : m_pDoc->SetString(3, 3, 0, OUString("='file:///extdata.fake'#Data3.B4"));
3286 : {
3287 1 : const char* pChecks[] = { "Value", "99", "98", "97" };
3288 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3289 : {
3290 4 : test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
3291 4 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
3292 : }
3293 : }
3294 :
3295 : // At this point, all accessed cell data from the external document should
3296 : // have been cached.
3297 : ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
3298 2 : nFileId, aExtSh1Name, false);
3299 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != NULL);
3300 1 : ScRange aCachedRange = getCachedRange(pCacheTab);
3301 2 : CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
3302 : aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
3303 1 : aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
3304 :
3305 : // Sheet2 is not referenced at all; the cache table shouldn't even exist.
3306 1 : pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
3307 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", pCacheTab.get() == NULL);
3308 :
3309 : // Sheet3's row 5 is not referenced; it should not be cached.
3310 1 : pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
3311 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != NULL);
3312 1 : aCachedRange = getCachedRange(pCacheTab);
3313 2 : CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
3314 : aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
3315 1 : aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
3316 :
3317 : // Unload the external document shell.
3318 1 : xExtDocSh->DoClose();
3319 2 : CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
3320 1 : findLoadedDocShellByName(aExtDocName) == NULL);
3321 :
3322 2 : m_pDoc->DeleteTab(0);
3323 1 : }
3324 :
3325 1 : void testExtRefFuncT(ScDocument* pDoc, ScDocument* pExtDoc)
3326 : {
3327 1 : Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
3328 1 : Test::clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
3329 :
3330 1 : pExtDoc->SetString(0, 0, 0, OUString("'1.2"));
3331 1 : pExtDoc->SetString(0, 1, 0, OUString("Foo"));
3332 1 : pExtDoc->SetValue(0, 2, 0, 12.3);
3333 1 : pDoc->SetString(0, 0, 0, OUString("=T('file:///extdata.fake'#Data.A1)"));
3334 1 : pDoc->SetString(0, 1, 0, OUString("=T('file:///extdata.fake'#Data.A2)"));
3335 1 : pDoc->SetString(0, 2, 0, OUString("=T('file:///extdata.fake'#Data.A3)"));
3336 1 : pDoc->CalcAll();
3337 :
3338 1 : OUString aRes = pDoc->GetString(0, 0, 0);
3339 1 : CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "1.2" );
3340 1 : aRes = pDoc->GetString(0, 1, 0);
3341 1 : CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "Foo" );
3342 1 : aRes = pDoc->GetString(0, 2, 0);
3343 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
3344 1 : }
3345 :
3346 1 : void testExtRefFuncOFFSET(ScDocument* pDoc, ScDocument* pExtDoc)
3347 : {
3348 1 : Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
3349 1 : Test::clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
3350 :
3351 1 : sc::AutoCalcSwitch aACSwitch(*pDoc, true);
3352 :
3353 : // External document has sheet named 'Data', and the internal doc has sheet named 'Test'.
3354 1 : pExtDoc->SetValue(ScAddress(0,1,0), 1.2); // Set 1.2 to A2.
3355 1 : pDoc->SetString(ScAddress(0,0,0), "=OFFSET('file:///extdata.fake'#Data.$A$1;1;0;1;1)");
3356 1 : CPPUNIT_ASSERT_EQUAL(1.2, pDoc->GetValue(ScAddress(0,0,0)));
3357 1 : }
3358 :
3359 1 : void testExtRefFuncVLOOKUP(ScDocument* pDoc, ScDocument* pExtDoc)
3360 : {
3361 1 : Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
3362 1 : Test::clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
3363 :
3364 : // Populate the external document.
3365 1 : pExtDoc->SetString(ScAddress(0,0,0), "A1");
3366 1 : pExtDoc->SetString(ScAddress(0,1,0), "A2");
3367 1 : pExtDoc->SetString(ScAddress(0,2,0), "A3");
3368 1 : pExtDoc->SetString(ScAddress(0,3,0), "A4");
3369 1 : pExtDoc->SetString(ScAddress(0,4,0), "A5");
3370 :
3371 1 : pExtDoc->SetString(ScAddress(1,0,0), "B1");
3372 1 : pExtDoc->SetString(ScAddress(1,1,0), "B2");
3373 1 : pExtDoc->SetString(ScAddress(1,2,0), "B3");
3374 1 : pExtDoc->SetString(ScAddress(1,3,0), "B4");
3375 1 : pExtDoc->SetString(ScAddress(1,4,0), "B5");
3376 :
3377 : // Put formula in the source document.
3378 :
3379 1 : pDoc->SetString(ScAddress(0,0,0), "A2");
3380 :
3381 : // Sort order TRUE
3382 1 : pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;1)");
3383 1 : CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
3384 :
3385 : // Sort order FALSE. It should return the same result.
3386 1 : pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;0)");
3387 1 : CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
3388 1 : }
3389 :
3390 1 : void Test::testExternalRefFunctions()
3391 : {
3392 1 : ScDocShellRef xExtDocSh = new ScDocShell;
3393 2 : OUString aExtDocName("file:///extdata.fake");
3394 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
3395 1 : xExtDocSh->DoInitNew(pMed);
3396 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
3397 1 : findLoadedDocShellByName(aExtDocName) != NULL);
3398 :
3399 1 : ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
3400 1 : CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
3401 1 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
3402 1 : const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
3403 2 : CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
3404 1 : pFileName && pFileName->equals(aExtDocName));
3405 :
3406 2 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
3407 :
3408 : // Populate the external source document.
3409 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
3410 1 : pExtDoc->InsertTab(0, OUString("Data"));
3411 1 : double val = 1;
3412 1 : pExtDoc->SetValue(0, 0, 0, val);
3413 : // leave cell B1 empty.
3414 1 : val = 2;
3415 1 : pExtDoc->SetValue(0, 1, 0, val);
3416 1 : pExtDoc->SetValue(1, 1, 0, val);
3417 1 : val = 3;
3418 1 : pExtDoc->SetValue(0, 2, 0, val);
3419 1 : pExtDoc->SetValue(1, 2, 0, val);
3420 1 : val = 4;
3421 1 : pExtDoc->SetValue(0, 3, 0, val);
3422 1 : pExtDoc->SetValue(1, 3, 0, val);
3423 :
3424 1 : m_pDoc->InsertTab(0, OUString("Test"));
3425 :
3426 : struct {
3427 : const char* pFormula; double fResult;
3428 : } aChecks[] = {
3429 : { "=SUM('file:///extdata.fake'#Data.A1:A4)", 10 },
3430 : { "=SUM('file:///extdata.fake'#Data.B1:B4)", 9 },
3431 : { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
3432 : { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
3433 : { "=COUNT('file:///extdata.fake'#Data.A1:A4)", 4 },
3434 : { "=COUNT('file:///extdata.fake'#Data.B1:B4)", 3 }
3435 1 : };
3436 :
3437 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3438 : {
3439 6 : m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
3440 6 : m_pDoc->GetValue(0, 0, 0, val);
3441 6 : CPPUNIT_ASSERT_MESSAGE("unexpected result involving external ranges.", val == aChecks[i].fResult);
3442 : }
3443 :
3444 1 : pRefMgr->clearCache(nFileId);
3445 1 : testExtRefFuncT(m_pDoc, pExtDoc);
3446 1 : testExtRefFuncOFFSET(m_pDoc, pExtDoc);
3447 1 : testExtRefFuncVLOOKUP(m_pDoc, pExtDoc);
3448 :
3449 : // Unload the external document shell.
3450 1 : xExtDocSh->DoClose();
3451 2 : CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
3452 1 : findLoadedDocShellByName(aExtDocName) == NULL);
3453 :
3454 2 : m_pDoc->DeleteTab(0);
3455 4 : }
3456 :
3457 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|