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 <test/bootstrapfixture.hxx>
11 #include "helper/debughelper.hxx"
12 #include "helper/qahelper.hxx"
13 #include <markdata.hxx>
14 #include <calcconfig.hxx>
15 #include <clipparam.hxx>
16 #include <compiler.hxx>
17 #include <tokenarray.hxx>
18 #include <refdata.hxx>
19 #include <scopetools.hxx>
20 #include <formulacell.hxx>
21 #include <docsh.hxx>
22 #include <docfunc.hxx>
23 #include <inputopt.hxx>
24 #include <paramisc.hxx>
25 #include <tokenstringcontext.hxx>
26 #include <refupdatecontext.hxx>
27 #include <dbdata.hxx>
28 #include <scmatrix.hxx>
29 #include <validat.hxx>
30 #include <scitems.hxx>
31 #include <patattr.hxx>
32 #include <docpool.hxx>
33 #include <docoptio.hxx>
34 #include <formulaopt.hxx>
35 #include <externalrefmgr.hxx>
36 #include <scdll.hxx>
37 #include <scmod.hxx>
38 #include <svl/itemset.hxx>
39
40 #include <formula/vectortoken.hxx>
41 #include <svl/broadcast.hxx>
42 #include <svl/intitem.hxx>
43 #include <sfx2/docfile.hxx>
44
45 #include <memory>
46 #include <functional>
47 #include <set>
48 #include <algorithm>
49
50 using namespace formula;
51
52 namespace {
53
getCachedRange(const ScExternalRefCache::TableTypeRef & pCacheTab)54 ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
55 {
56 ScRange aRange;
57
58 vector<SCROW> aRows;
59 pCacheTab->getAllRows(aRows);
60 bool bFirst = true;
61 for (const SCROW nRow : aRows)
62 {
63 vector<SCCOL> aCols;
64 pCacheTab->getAllCols(nRow, aCols);
65 for (const SCCOL nCol : aCols)
66 {
67 if (bFirst)
68 {
69 aRange.aStart = ScAddress(nCol, nRow, 0);
70 aRange.aEnd = aRange.aStart;
71 bFirst = false;
72 }
73 else
74 {
75 if (nCol < aRange.aStart.Col())
76 aRange.aStart.SetCol(nCol);
77 else if (aRange.aEnd.Col() < nCol)
78 aRange.aEnd.SetCol(nCol);
79
80 if (nRow < aRange.aStart.Row())
81 aRange.aStart.SetRow(nRow);
82 else if (aRange.aEnd.Row() < nRow)
83 aRange.aEnd.SetRow(nRow);
84 }
85 }
86 }
87 return aRange;
88 }
89
setExpandRefs(bool bExpand)90 void setExpandRefs(bool bExpand)
91 {
92 ScModule* pMod = SC_MOD();
93 ScInputOptions aOpt = pMod->GetInputOptions();
94 aOpt.SetExpandRefs(bExpand);
95 pMod->SetInputOptions(aOpt);
96 }
97
testFormulaRefUpdateNameCopySheetCheckTab(ScDocument * pDoc,SCTAB nTab,bool bCheckNames)98 void testFormulaRefUpdateNameCopySheetCheckTab( ScDocument* pDoc, SCTAB nTab, bool bCheckNames )
99 {
100 if (bCheckNames)
101 {
102 const ScRangeData* pName;
103 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL");
104 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL should exist", pName);
105 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL");
106 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL should exist", pName);
107 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_GLOBAL");
108 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_GLOBAL should exist", pName);
109 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_LOCAL");
110 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_LOCAL should exist", pName);
111 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED");
112 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED should exist", pName);
113 pName = pDoc->GetRangeName(nTab)->findByUpperName("GLOBAL_UNUSED_NOREF");
114 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED_NOREF should not exist", !pName);
115 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_GLOBAL");
116 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_GLOBAL should exist", pName);
117 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_LOCAL");
118 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_LOCAL should exist", pName);
119 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED");
120 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED should exist", pName);
121 pName = pDoc->GetRangeName(nTab)->findByUpperName("LOCAL_UNUSED_NOREF");
122 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED_NOREF should exist", pName);
123 }
124
125 ScAddress aPos(0,0,0);
126 aPos.SetRow(0);
127 aPos.SetTab(nTab);
128 int nSheet = nTab + 1;
129 CPPUNIT_ASSERT_EQUAL( 1.0 * nSheet, pDoc->GetValue(aPos));
130 aPos.IncRow();
131 CPPUNIT_ASSERT_EQUAL( 11.0 * nSheet, pDoc->GetValue(aPos));
132 aPos.IncRow();
133 CPPUNIT_ASSERT_EQUAL( 100.0 * nSheet, pDoc->GetValue(aPos));
134 aPos.IncRow();
135 CPPUNIT_ASSERT_EQUAL( 11000.0 * nSheet, pDoc->GetValue(aPos));
136 aPos.IncRow();
137 CPPUNIT_ASSERT_EQUAL( 10000.0 * nSheet, pDoc->GetValue(aPos));
138 aPos.IncRow();
139 CPPUNIT_ASSERT_EQUAL( 1100000.0 * nSheet, pDoc->GetValue(aPos));
140 }
141
142 class ColumnTest
143 {
144 ScDocument * m_pDoc;
145
146 const SCROW m_nTotalRows;
147 const SCROW m_nStart1;
148 const SCROW m_nEnd1;
149 const SCROW m_nStart2;
150 const SCROW m_nEnd2;
151
152 public:
ColumnTest(ScDocument * pDoc,SCROW nTotalRows,SCROW nStart1,SCROW nEnd1,SCROW nStart2,SCROW nEnd2)153 ColumnTest( ScDocument * pDoc, SCROW nTotalRows,
154 SCROW nStart1, SCROW nEnd1, SCROW nStart2, SCROW nEnd2 )
155 : m_pDoc(pDoc), m_nTotalRows(nTotalRows)
156 , m_nStart1(nStart1), m_nEnd1(nEnd1)
157 , m_nStart2(nStart2), m_nEnd2(nEnd2)
158 {}
159
operator ()(SCCOL nColumn,const OUString & rFormula,std::function<double (SCROW)> const & lExpected) const160 void operator() ( SCCOL nColumn, const OUString& rFormula,
161 std::function<double(SCROW )> const & lExpected ) const
162 {
163 ScDocument aClipDoc(SCDOCMODE_CLIP);
164 ScMarkData aMark(m_pDoc->GetSheetLimits());
165
166 ScAddress aPos(nColumn, m_nStart1, 0);
167 m_pDoc->SetString(aPos, rFormula);
168 ASSERT_DOUBLES_EQUAL( lExpected(m_nStart1), m_pDoc->GetValue(aPos) );
169
170 // Copy formula cell to clipboard.
171 ScClipParam aClipParam(aPos, false);
172 aMark.SetMarkArea(aPos);
173 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
174
175 // Paste it to first range.
176 InsertDeleteFlags nFlags = InsertDeleteFlags::CONTENTS;
177 ScRange aDestRange(nColumn, m_nStart1, 0, nColumn, m_nEnd1, 0);
178 aMark.SetMarkArea(aDestRange);
179 m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
180
181 // Paste it second range.
182 aDestRange = ScRange(nColumn, m_nStart2, 0, nColumn, m_nEnd2, 0);
183 aMark.SetMarkArea(aDestRange);
184 m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
185
186 // Check the formula results for passed column.
187 for( SCROW i = 0; i < m_nTotalRows; ++i )
188 {
189 if( !((m_nStart1 <= i && i <= m_nEnd1) || (m_nStart2 <= i && i <= m_nEnd2)) )
190 continue;
191 double fExpected = lExpected(i);
192 ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(nColumn,i,0)));
193 }
194 }
195 };
196
197 }
198
199 class TestFormula : public test::BootstrapFixture
200 {
201 public:
202 TestFormula();
203
204 virtual void setUp() override;
205 virtual void tearDown() override;
206
207 void testFormulaCreateStringFromTokens();
208 void testFormulaParseReference();
209 void testFetchVectorRefArray();
210 void testGroupConverter3D();
211 void testFormulaTokenEquality();
212 void testFormulaRefData();
213 void testFormulaCompiler();
214 void testFormulaCompilerJumpReordering();
215 void testFormulaCompilerImplicitIntersection2Param();
216 void testFormulaCompilerImplicitIntersection1ParamNoChange();
217 void testFormulaCompilerImplicitIntersection1ParamWithChange();
218 void testFormulaCompilerImplicitIntersection1NoGroup();
219 void testFormulaCompilerImplicitIntersectionOperators();
220 void testFormulaAnnotateTrimOnDoubleRefs();
221 void testFormulaRefUpdate();
222 void testFormulaRefUpdateRange();
223 void testFormulaRefUpdateSheets();
224 void testFormulaRefUpdateSheetsDelete();
225 void testFormulaRefUpdateInsertRows();
226 void testFormulaRefUpdateInsertColumns();
227 void testFormulaRefUpdateMove();
228 void testFormulaRefUpdateMoveUndo();
229 void testFormulaRefUpdateMoveUndo2();
230 void testFormulaRefUpdateMoveUndo3NonShared();
231 void testFormulaRefUpdateMoveUndo3Shared();
232 void testFormulaRefUpdateMoveUndoDependents();
233 void testFormulaRefUpdateMoveUndo4();
234 void testFormulaRefUpdateMoveToSheet();
235 void testFormulaRefUpdateDeleteContent();
236 void testFormulaRefUpdateDeleteAndShiftLeft();
237 void testFormulaRefUpdateDeleteAndShiftLeft2();
238 void testFormulaRefUpdateDeleteAndShiftUp();
239 void testFormulaRefUpdateName();
240 void testFormulaRefUpdateNameMove();
241 void testFormulaRefUpdateNameExpandRef();
242 void testFormulaRefUpdateNameExpandRef2();
243 void testFormulaRefUpdateNameDeleteRow();
244 void testFormulaRefUpdateNameCopySheet();
245 void testFormulaRefUpdateSheetLocalMove();
246 void testFormulaRefUpdateNameDelete();
247 void testFormulaRefUpdateValidity();
248 void testTokenArrayRefUpdateMove();
249 void testSingleCellCopyColumnLabel();
250 void testIntersectionOpExcel();
251 void testTdf97369();
252 void testTdf97587();
253 void testTdf93415();
254 void testTdf100818();
255 void testMatConcat();
256 void testMatConcatReplication();
257 void testExternalRef();
258 void testFormulaDepTracking();
259 void testFormulaDepTracking2();
260 void testFormulaDepTracking3();
261 void testFormulaDepTrackingDeleteRow();
262 void testFormulaDepTrackingDeleteCol();
263 void testFormulaMatrixResultUpdate();
264 void testExternalRefFunctions();
265 void testExternalRangeName();
266 void testExternalRefUnresolved();
267 void testRefR1C1WholeCol();
268 void testRefR1C1WholeRow();
269 void testIterations();
270 void testInsertColCellStoreEventSwap();
271 void testFormulaAfterDeleteRows();
272 void testMultipleOperations();
273 void testFuncCOLUMN();
274 void testFuncCOUNT();
275 void testFuncCOUNTBLANK();
276 void testFuncROW();
277 void testFuncSUM();
278 void testFuncPRODUCT();
279 void testFuncSUMPRODUCT();
280 void testFuncSUMXMY2();
281 void testFuncMIN();
282 void testFuncN();
283 void testFuncCOUNTIF();
284 void testFuncNUMBERVALUE();
285 void testFuncLEN();
286 void testFuncLOOKUP();
287 void testFuncLOOKUParrayWithError();
288 void testTdf141146();
289 void testFuncVLOOKUP();
290 void testFuncMATCH();
291 void testFuncCELL();
292 void testFuncDATEDIF();
293 void testFuncINDIRECT();
294 void testFuncINDIRECT2();
295 void testFunc_MATCH_INDIRECT();
296 void testFuncIF();
297 void testFuncCHOOSE();
298 void testFuncIFERROR();
299 void testFuncSHEET();
300 void testFuncNOW();
301 void testMatrixOp();
302 void testFuncRangeOp();
303 void testFuncFORMULA();
304 void testFuncTableRef();
305 void testFuncFTEST();
306 void testFuncFTESTBug();
307 void testFuncCHITEST();
308 void testFuncTTEST();
309 void testFuncSUMX2PY2();
310 void testFuncSUMX2MY2();
311 void testFuncGCD();
312 void testFuncLCM();
313 void testFuncSUMSQ();
314 void testFuncMDETERM();
315 void testFormulaErrorPropagation();
316 void testFuncRowsHidden();
317 void testFuncSUMIFS();
318 void testFuncRefListArraySUBTOTAL();
319 void testFuncJumpMatrixArrayIF();
320 void testFuncJumpMatrixArrayOFFSET();
321
322 CPPUNIT_TEST_SUITE(TestFormula);
323
324 CPPUNIT_TEST(testFormulaCreateStringFromTokens);
325 CPPUNIT_TEST(testFormulaParseReference);
326 CPPUNIT_TEST(testFetchVectorRefArray);
327 CPPUNIT_TEST(testGroupConverter3D);
328 CPPUNIT_TEST(testFormulaTokenEquality);
329 CPPUNIT_TEST(testFormulaRefData);
330 CPPUNIT_TEST(testFormulaCompiler);
331 CPPUNIT_TEST(testFormulaCompilerJumpReordering);
332 CPPUNIT_TEST(testFormulaCompilerImplicitIntersection2Param);
333 CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1ParamNoChange);
334 CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1ParamWithChange);
335 CPPUNIT_TEST(testFormulaCompilerImplicitIntersection1NoGroup);
336 CPPUNIT_TEST(testFormulaCompilerImplicitIntersectionOperators);
337 CPPUNIT_TEST(testFormulaAnnotateTrimOnDoubleRefs);
338 CPPUNIT_TEST(testFormulaRefUpdate);
339 CPPUNIT_TEST(testFormulaRefUpdateRange);
340 CPPUNIT_TEST(testFormulaRefUpdateSheets);
341 CPPUNIT_TEST(testFormulaRefUpdateSheetsDelete);
342 CPPUNIT_TEST(testFormulaRefUpdateInsertRows);
343 CPPUNIT_TEST(testFormulaRefUpdateInsertColumns);
344 CPPUNIT_TEST(testFormulaRefUpdateMove);
345 CPPUNIT_TEST(testFormulaRefUpdateMoveUndo);
346 CPPUNIT_TEST(testFormulaRefUpdateMoveUndo2);
347 CPPUNIT_TEST(testFormulaRefUpdateMoveUndo3NonShared);
348 CPPUNIT_TEST(testFormulaRefUpdateMoveUndo3Shared);
349 CPPUNIT_TEST(testFormulaRefUpdateMoveUndoDependents);
350 CPPUNIT_TEST(testFormulaRefUpdateMoveUndo4);
351 CPPUNIT_TEST(testFormulaRefUpdateMoveToSheet);
352 CPPUNIT_TEST(testFormulaRefUpdateDeleteContent);
353 CPPUNIT_TEST(testFormulaRefUpdateDeleteAndShiftLeft);
354 CPPUNIT_TEST(testFormulaRefUpdateDeleteAndShiftLeft2);
355 CPPUNIT_TEST(testFormulaRefUpdateDeleteAndShiftUp);
356 CPPUNIT_TEST(testFormulaRefUpdateName);
357 CPPUNIT_TEST(testFormulaRefUpdateNameMove);
358 CPPUNIT_TEST(testFormulaRefUpdateNameExpandRef);
359 CPPUNIT_TEST(testFormulaRefUpdateNameExpandRef2);
360 CPPUNIT_TEST(testFormulaRefUpdateNameDeleteRow);
361 CPPUNIT_TEST(testFormulaRefUpdateNameCopySheet);
362 CPPUNIT_TEST(testFormulaRefUpdateSheetLocalMove);
363 CPPUNIT_TEST(testFormulaRefUpdateNameDelete);
364 CPPUNIT_TEST(testFormulaRefUpdateValidity);
365 CPPUNIT_TEST(testTokenArrayRefUpdateMove);
366 CPPUNIT_TEST(testSingleCellCopyColumnLabel);
367 CPPUNIT_TEST(testIntersectionOpExcel);
368 CPPUNIT_TEST(testTdf97369);
369 CPPUNIT_TEST(testTdf97587);
370 CPPUNIT_TEST(testTdf93415);
371 CPPUNIT_TEST(testTdf100818);
372 CPPUNIT_TEST(testMatConcat);
373 CPPUNIT_TEST(testMatConcatReplication);
374 CPPUNIT_TEST(testExternalRef);
375 CPPUNIT_TEST(testFormulaDepTracking);
376 CPPUNIT_TEST(testFormulaDepTracking2);
377 CPPUNIT_TEST(testFormulaDepTracking3);
378 CPPUNIT_TEST(testFormulaDepTrackingDeleteRow);
379 CPPUNIT_TEST(testFormulaDepTrackingDeleteCol);
380 CPPUNIT_TEST(testFormulaMatrixResultUpdate);
381 CPPUNIT_TEST(testExternalRefFunctions);
382 CPPUNIT_TEST(testExternalRangeName);
383 CPPUNIT_TEST(testExternalRefUnresolved);
384 CPPUNIT_TEST(testRefR1C1WholeCol);
385 CPPUNIT_TEST(testRefR1C1WholeRow);
386 CPPUNIT_TEST(testIterations);
387 CPPUNIT_TEST(testInsertColCellStoreEventSwap);
388 CPPUNIT_TEST(testFormulaAfterDeleteRows);
389 CPPUNIT_TEST(testMultipleOperations);
390 CPPUNIT_TEST(testFuncCOLUMN);
391 CPPUNIT_TEST(testFuncCOUNT);
392 CPPUNIT_TEST(testFuncCOUNTBLANK);
393 CPPUNIT_TEST(testFuncROW);
394 CPPUNIT_TEST(testFuncSUM);
395 CPPUNIT_TEST(testFuncPRODUCT);
396 CPPUNIT_TEST(testFuncSUMPRODUCT);
397 CPPUNIT_TEST(testFuncSUMXMY2);
398 CPPUNIT_TEST(testFuncMIN);
399 CPPUNIT_TEST(testFuncN);
400 CPPUNIT_TEST(testFuncCOUNTIF);
401 CPPUNIT_TEST(testFuncNUMBERVALUE);
402 CPPUNIT_TEST(testFuncLEN);
403 CPPUNIT_TEST(testFuncLOOKUP);
404 CPPUNIT_TEST(testFuncLOOKUParrayWithError);
405 CPPUNIT_TEST(testTdf141146);
406 CPPUNIT_TEST(testFuncVLOOKUP);
407 CPPUNIT_TEST(testFuncMATCH);
408 CPPUNIT_TEST(testFuncCELL);
409 CPPUNIT_TEST(testFuncDATEDIF);
410 CPPUNIT_TEST(testFuncINDIRECT);
411 CPPUNIT_TEST(testFuncINDIRECT2);
412 CPPUNIT_TEST(testFunc_MATCH_INDIRECT);
413 CPPUNIT_TEST(testFuncIF);
414 CPPUNIT_TEST(testFuncCHOOSE);
415 CPPUNIT_TEST(testFuncIFERROR);
416 CPPUNIT_TEST(testFuncSHEET);
417 CPPUNIT_TEST(testFuncNOW);
418 CPPUNIT_TEST(testMatrixOp);
419 CPPUNIT_TEST(testFuncRangeOp);
420 CPPUNIT_TEST(testFuncFORMULA);
421 CPPUNIT_TEST(testFuncTableRef);
422 CPPUNIT_TEST(testFuncFTEST);
423 CPPUNIT_TEST(testFuncFTESTBug);
424 CPPUNIT_TEST(testFuncCHITEST);
425 CPPUNIT_TEST(testFuncTTEST);
426 CPPUNIT_TEST(testFuncSUMX2PY2);
427 CPPUNIT_TEST(testFuncSUMX2MY2);
428 CPPUNIT_TEST(testFuncGCD);
429 CPPUNIT_TEST(testFuncLCM);
430 CPPUNIT_TEST(testFuncSUMSQ);
431 CPPUNIT_TEST(testFuncMDETERM);
432 CPPUNIT_TEST(testFormulaErrorPropagation);
433 CPPUNIT_TEST(testFuncRowsHidden);
434 CPPUNIT_TEST(testFuncSUMIFS);
435 CPPUNIT_TEST(testFuncRefListArraySUBTOTAL);
436 CPPUNIT_TEST(testFuncJumpMatrixArrayIF);
437 CPPUNIT_TEST(testFuncJumpMatrixArrayOFFSET);
438
439 CPPUNIT_TEST_SUITE_END();
440
441 private:
442 ScDocShellRef m_xDocShell;
443 ScDocument* m_pDoc;
444 };
445
TestFormula()446 TestFormula::TestFormula()
447 {
448 }
449
setUp()450 void TestFormula::setUp()
451 {
452 BootstrapFixture::setUp();
453
454 ScDLL::Init();
455
456 m_xDocShell = new ScDocShell(
457 SfxModelFlags::EMBEDDED_OBJECT |
458 SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
459 SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
460 m_xDocShell->SetIsInUcalc();
461 m_xDocShell->DoInitUnitTest();
462
463 m_pDoc = &m_xDocShell->GetDocument();
464 }
465
tearDown()466 void TestFormula::tearDown()
467 {
468 m_xDocShell->DoClose();
469 m_xDocShell.clear();
470
471 test::BootstrapFixture::tearDown();
472 }
473
testFormulaCreateStringFromTokens()474 void TestFormula::testFormulaCreateStringFromTokens()
475 {
476 // Insert sheets.
477 m_pDoc->InsertTab(0, "Test");
478 m_pDoc->InsertTab(1, "Kevin's Data");
479 m_pDoc->InsertTab(2, "Past Data");
480 m_pDoc->InsertTab(3, "2013");
481
482 // Insert named ranges.
483 static const struct {
484 bool bGlobal;
485 const char* pName;
486 const char* pExpr;
487 } aNames[] = {
488 { true, "x", "Test.H1" },
489 { true, "y", "Test.H2" },
490 { true, "z", "Test.H3" },
491
492 { false, "sheetx", "Test.J1" }
493 };
494
495 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
496 ScRangeName* pSheetNames = m_pDoc->GetRangeName(0);
497 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
498 CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames);
499
500 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
501 {
502 ScRangeData* pName = new ScRangeData(
503 *m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
504 ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
505
506 if (aNames[i].bGlobal)
507 {
508 bool bInserted = pGlobalNames->insert(pName);
509 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
510 }
511 else
512 {
513 bool bInserted = pSheetNames->insert(pName);
514 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
515 }
516 }
517
518 // Insert DB ranges.
519 static const struct {
520 const char* pName;
521 SCTAB nTab;
522 SCCOL nCol1;
523 SCROW nRow1;
524 SCCOL nCol2;
525 SCROW nRow2;
526 } aDBs[] = {
527 { "Table1", 0, 0, 0, 10, 10 },
528 { "Table2", 1, 0, 0, 10, 10 },
529 { "Table3", 2, 0, 0, 10, 10 }
530 };
531
532 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
533 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
534
535 for (size_t i = 0; i < SAL_N_ELEMENTS(aDBs); ++i)
536 {
537 std::unique_ptr<ScDBData> pData( new ScDBData(
538 OUString::createFromAscii(
539 aDBs[i].pName), aDBs[i].nTab, aDBs[i].nCol1, aDBs[i].nRow1, aDBs[i].nCol2,aDBs[i].nRow2) );
540 bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
541 CPPUNIT_ASSERT_MESSAGE(
542 OString(
543 OString::Concat("Failed to insert \"") + aDBs[i].pName + "\"").getStr(),
544 bInserted);
545 }
546
547 const char* aTests[] = {
548 "1+2",
549 "SUM(A1:A10;B1:B10;C5;D6)",
550 "IF(Test.B10<>10;\"Good\";\"Bad\")",
551 "AVERAGE('2013'.B10:C20)",
552 "'Kevin''s Data'.B10",
553 "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
554 "x+y*z", // named ranges
555 "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
556 "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
557 "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
558 "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
559 };
560 (void) aTests;
561
562 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
563
564 // Artificially add external reference data after the context object is
565 // initialized.
566 aCxt.maExternalFileNames.emplace_back("file:///path/to/fake.file");
567 std::vector<OUString> aExtTabNames;
568 aExtTabNames.emplace_back("Sheet");
569 aCxt.maExternalCachedTabNames.emplace(0, aExtTabNames);
570
571 ScAddress aPos(0,0,0);
572
573 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
574 {
575 #if 0
576 OUString aFormula = OUString::createFromAscii(aTests[i]);
577 #endif
578 ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH);
579 #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
580 pArray.reset(aComp.CompileString(aFormula));
581 CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray.get());
582
583 OUString aCheck = pArray->CreateString(aCxt, aPos);
584 CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
585 #endif
586 }
587
588 m_pDoc->DeleteTab(3);
589 m_pDoc->DeleteTab(2);
590 m_pDoc->DeleteTab(1);
591 m_pDoc->DeleteTab(0);
592 }
593
594 namespace {
595
isEmpty(const formula::VectorRefArray & rArray,size_t nPos)596 bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
597 {
598 if (rArray.mpStringArray)
599 {
600 if (rArray.mpStringArray[nPos])
601 return false;
602 }
603
604 if (rArray.mpNumericArray)
605 return std::isnan(rArray.mpNumericArray[nPos]);
606 else
607 return true;
608 }
609
equals(const formula::VectorRefArray & rArray,size_t nPos,double fVal)610 bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
611 {
612 if (rArray.mpStringArray && rArray.mpStringArray[nPos])
613 // This is a string cell.
614 return false;
615
616 return rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal;
617 }
618
equals(const formula::VectorRefArray & rArray,size_t nPos,const OUString & rVal)619 bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
620 {
621 if (!rArray.mpStringArray)
622 return false;
623
624 bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
625 if (!bEquals)
626 {
627 cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
628 cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
629 }
630 return bEquals;
631 }
632
633 }
634
testFormulaParseReference()635 void TestFormula::testFormulaParseReference()
636 {
637 OUString aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
638 m_pDoc->InsertTab(0, "Dummy"); // just to shift the sheet indices...
639 m_pDoc->InsertTab(1, aTab1); // name with a single quote.
640 m_pDoc->InsertTab(2, aTab2); // name with 2 single quotes.
641 m_pDoc->InsertTab(3, aTab3); // name without single quotes.
642 m_pDoc->InsertTab(4, aTab4); // name that doesn't require to be quoted.
643
644 OUString aTabName;
645 m_pDoc->GetName(1, aTabName);
646 CPPUNIT_ASSERT_EQUAL(aTab1, aTabName);
647 m_pDoc->GetName(2, aTabName);
648 CPPUNIT_ASSERT_EQUAL(aTab2, aTabName);
649 m_pDoc->GetName(3, aTabName);
650 CPPUNIT_ASSERT_EQUAL(aTab3, aTabName);
651 m_pDoc->GetName(4, aTabName);
652 CPPUNIT_ASSERT_EQUAL(aTab4, aTabName);
653
654 // Make sure the formula input and output match.
655 {
656 const char* aChecks[] = {
657 "'90''s Music'.B12",
658 "'90''s and 70''s'.$AB$100",
659 "'All Others'.Z$100",
660 "NoQuote.$C111"
661 };
662
663 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
664 {
665 // Use the 'Dummy' sheet for this.
666 OUString aInput = "=" + OUString::createFromAscii(aChecks[i]);
667 m_pDoc->SetString(ScAddress(0,0,0), aInput);
668 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), aChecks[i], "Wrong formula");
669 }
670 }
671
672 ScAddress aPos;
673 ScAddress::ExternalInfo aExtInfo;
674 ScRefFlags nRes = aPos.Parse("'90''s Music'.D10", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
675 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
676 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(1), aPos.Tab());
677 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), aPos.Col());
678 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), aPos.Row());
679 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
680
681 nRes = aPos.Parse("'90''s and 70''s'.C100", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
682 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
683 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(2), aPos.Tab());
684 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aPos.Col());
685 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(99), aPos.Row());
686 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
687
688 nRes = aPos.Parse("'All Others'.B3", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
689 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
690 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(3), aPos.Tab());
691 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aPos.Col());
692 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), aPos.Row());
693 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
694
695 nRes = aPos.Parse("NoQuote.E13", *m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
696 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
697 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aPos.Tab());
698 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(4), aPos.Col());
699 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), aPos.Row());
700 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
701
702 ScRange aRange;
703
704 aRange.aStart.SetTab(0);
705 nRes = aRange.Parse(":B", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
706 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
707
708 aRange.aStart.SetTab(0);
709 nRes = aRange.Parse("B:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
710 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
711
712 aRange.aStart.SetTab(0);
713 nRes = aRange.Parse(":B2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
714 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
715
716 aRange.aStart.SetTab(0);
717 nRes = aRange.Parse("B2:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
718 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
719
720 aRange.aStart.SetTab(0);
721 nRes = aRange.Parse(":2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
722 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
723
724 aRange.aStart.SetTab(0);
725 nRes = aRange.Parse("2:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
726 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
727
728 aRange.aStart.SetTab(0);
729 nRes = aRange.Parse(":2B", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
730 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
731
732 aRange.aStart.SetTab(0);
733 nRes = aRange.Parse("2B:", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
734 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
735
736 aRange.aStart.SetTab(0);
737 nRes = aRange.Parse("abc_foo:abc_bar", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
738 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
739
740 aRange.aStart.SetTab(0);
741 nRes = aRange.Parse("B1:B2~C1", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
742 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes & ScRefFlags::VALID));
743
744 aRange.aStart.SetTab(0);
745 nRes = aRange.Parse("B:B", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
746 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
747 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
748 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
749 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
750 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
751 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
752 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
753 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
754 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
755 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
756 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
757 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
758 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
759 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
760 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
761
762 aRange.aStart.SetTab(0);
763 nRes = aRange.Parse("2:2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
764 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
765 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
766 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
767 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
768 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
769 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
770 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
771 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
772 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
773 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
774 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
775 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
776 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
777 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
778 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
779
780 nRes = aRange.Parse("NoQuote.B:C", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
781 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
782 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aRange.aStart.Tab());
783 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
784 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
785 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aRange.aEnd.Tab());
786 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aRange.aEnd.Col());
787 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
788 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
789 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
790 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
791 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
792 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
793 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
794 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
795 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
796
797 // Both rows at sheet bounds and relative => convert to absolute => entire column reference.
798 aRange.aStart.SetTab(0);
799 nRes = aRange.Parse("B1:B1048576", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
800 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
801 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
802 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aStart.Col());
803 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), aRange.aStart.Row());
804 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
805 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aRange.aEnd.Col());
806 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxRow(), aRange.aEnd.Row());
807 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
808 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
809 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
810 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
811 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
812 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
813 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS),
814 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
815
816 // Both columns at sheet bounds and relative => convert to absolute => entire row reference.
817 aRange.aStart.SetTab(0);
818 nRes = aRange.Parse("A2:AMJ2", *m_pDoc, formula::FormulaGrammar::CONV_OOO);
819 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & ScRefFlags::VALID));
820 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aStart.Tab());
821 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), aRange.aStart.Col());
822 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aStart.Row());
823 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(0), aRange.aEnd.Tab());
824 CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), aRange.aEnd.Col());
825 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), aRange.aEnd.Row());
826 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
827 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID),
828 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
829 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID)));
830 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::ZERO),
831 static_cast<sal_uInt16>(nRes & (ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS)));
832 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS),
833 static_cast<sal_uInt16>(nRes & (ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS)));
834
835 // Check for reference input conversion to and display string of entire column/row.
836 {
837 const char* aChecks[][2] = {
838 { "=B:B", "B:B" },
839 { "=B1:B1048576", "B:B" },
840 { "=B1:B$1048576", "B1:B$1048576" },
841 { "=B$1:B1048576", "B$1:B1048576" },
842 { "=B$1:B$1048576", "B:B" },
843 { "=2:2", "2:2" },
844 { "=A2:AMJ2", "2:2" },
845 { "=A2:$AMJ2", "A2:$AMJ2" },
846 { "=$A2:AMJ2", "$A2:AMJ2" },
847 { "=$A2:$AMJ2", "2:2" }
848 };
849
850 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
851 {
852 // Use the 'Dummy' sheet for this.
853 m_pDoc->SetString(ScAddress(0,0,0), OUString::createFromAscii(aChecks[i][0]));
854 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), aChecks[i][1], "Wrong formula");
855 }
856 }
857
858 m_pDoc->DeleteTab(4);
859 m_pDoc->DeleteTab(3);
860 m_pDoc->DeleteTab(2);
861 m_pDoc->DeleteTab(1);
862 m_pDoc->DeleteTab(0);
863 }
864
testFetchVectorRefArray()865 void TestFormula::testFetchVectorRefArray()
866 {
867 m_pDoc->InsertTab(0, "Test");
868
869 // All numeric cells in Column A.
870 m_pDoc->SetValue(ScAddress(0,0,0), 1);
871 m_pDoc->SetValue(ScAddress(0,1,0), 2);
872 m_pDoc->SetValue(ScAddress(0,2,0), 3);
873 m_pDoc->SetValue(ScAddress(0,3,0), 4);
874
875 formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 4);
876 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
877 CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
878 CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
879 CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
880 CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
881 CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
882
883 aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 5);
884 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
885 CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
886 CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
887 CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
888 CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
889 CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
890 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
891
892 // All string cells in Column B. Note that the fetched string arrays are
893 // only to be compared case-insensitively. Right now, we use upper cased
894 // strings to achieve case-insensitive-ness, but that may change. So,
895 // don't count on that.
896 m_pDoc->SetString(ScAddress(1,0,0), "Andy");
897 m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
898 m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
899 m_pDoc->SetString(ScAddress(1,3,0), "David");
900 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 5);
901 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
902 CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
903 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Andy"));
904 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Bruce"));
905 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "Charlie"));
906 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "David"));
907 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
908
909 // Mixture of numeric, string, and empty cells in Column C.
910 m_pDoc->SetString(ScAddress(2,0,0), "Header");
911 m_pDoc->SetValue(ScAddress(2,1,0), 11);
912 m_pDoc->SetValue(ScAddress(2,2,0), 12);
913 m_pDoc->SetValue(ScAddress(2,3,0), 13);
914 m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
915 m_pDoc->CalcAll();
916
917 aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,0,0), 7);
918 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
919 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
920 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
921 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Header"));
922 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 11));
923 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 12));
924 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 13));
925 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
926 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 36));
927 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
928
929 // Mixed type again in Column D, but it starts with a numeric cell.
930 m_pDoc->SetValue(ScAddress(3,0,0), 10);
931 m_pDoc->SetString(ScAddress(3,1,0), "Below 10");
932 // Leave 2 empty cells.
933 m_pDoc->SetValue(ScAddress(3,4,0), 11);
934 m_pDoc->SetString(ScAddress(3,5,0), "=12");
935 m_pDoc->SetString(ScAddress(3,6,0), "=13");
936 m_pDoc->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
937 m_pDoc->CalcAll();
938
939 aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,0,0), 8);
940 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
941 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
942 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
943 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 10));
944 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Below 10"));
945 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 2));
946 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
947 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 11));
948 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 12));
949 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13));
950 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC"));
951
952 // Column E consists of formula cells whose results are all numeric.
953 for (SCROW i = 0; i <= 6; ++i)
954 m_pDoc->SetString(ScAddress(4,i,0), "=ROW()");
955 m_pDoc->CalcAll();
956
957 // Leave row 7 empty.
958 m_pDoc->SetString(ScAddress(4,8,0), "Andy");
959 m_pDoc->SetValue(ScAddress(4,9,0), 123);
960
961 // This array fits within a single formula block.
962 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 5);
963 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
964 CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray);
965 CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", !aArray.mpStringArray);
966 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
967 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
968 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
969 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
970 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
971
972 // This array spans over multiple blocks.
973 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 11);
974 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
975 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray);
976 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpStringArray);
977 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
978 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
979 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
980 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
981 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
982 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6));
983 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7));
984 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
985 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy"));
986 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123));
987 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10));
988
989 // Hit the cache but at a different start row.
990 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 3);
991 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
992 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
993 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3));
994 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4));
995 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5));
996
997 // Column F begins with empty rows at the top.
998 m_pDoc->SetValue(ScAddress(5,2,0), 1.1);
999 m_pDoc->SetValue(ScAddress(5,3,0), 1.2);
1000 m_pDoc->SetString(ScAddress(5,4,0), "=2*8");
1001 m_pDoc->CalcAll();
1002
1003 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 4);
1004 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1005 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
1006 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1));
1007 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2));
1008 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16));
1009 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
1010
1011 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 3);
1012 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1013 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
1014 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
1015 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
1016 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
1017
1018 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 10);
1019 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1020 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
1021 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
1022 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
1023 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
1024 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2));
1025 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16));
1026 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5));
1027 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
1028 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
1029 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8));
1030 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9));
1031
1032 // Get the array for F3:F4. This array should only consist of numeric array.
1033 aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 3);
1034 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1035 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1036 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1037
1038 // Column G consists only of strings.
1039 m_pDoc->SetString(ScAddress(6,0,0), "Title");
1040 m_pDoc->SetString(ScAddress(6,1,0), "foo");
1041 m_pDoc->SetString(ScAddress(6,2,0), "bar");
1042 m_pDoc->SetString(ScAddress(6,3,0), "foo");
1043 m_pDoc->SetString(ScAddress(6,4,0), "baz");
1044 m_pDoc->SetString(ScAddress(6,5,0), "quack");
1045 m_pDoc->SetString(ScAddress(6,6,0), "beep");
1046 m_pDoc->SetString(ScAddress(6,7,0), "kerker");
1047
1048 aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
1049 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1050 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
1051 CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
1052 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "foo"));
1053 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "bar"));
1054 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "foo"));
1055 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "baz"));
1056
1057 aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
1058 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1059 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
1060 CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
1061 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "bar"));
1062 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "foo"));
1063 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "baz"));
1064 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "quack"));
1065
1066 // Column H starts with formula cells.
1067 for (SCROW i = 0; i < 10; ++i)
1068 m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
1069
1070 m_pDoc->CalcAll();
1071 aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
1072 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1073 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1074 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1075 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 4.0));
1076 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, 5.0));
1077 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, 6.0));
1078
1079 aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
1080 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
1081 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1082 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1083 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 5.0));
1084
1085 // Clear everything and start over.
1086 clearRange(m_pDoc, ScRange(0,0,0,m_pDoc->MaxCol(),m_pDoc->MaxRow(),0));
1087 m_pDoc->PrepareFormulaCalc();
1088
1089 // Totally empty range in a totally empty column (Column A).
1090 aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
1091 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1092 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1093 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1094 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1095 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1096
1097 // Totally empty range in a non-empty column (Column B).
1098 m_pDoc->SetString(ScAddress(1,10,0), "Some text"); // B11
1099 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
1100 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1101 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1102 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1103 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1104 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1105
1106 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
1107 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1108 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1109 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1110 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1111 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1112
1113 // These values come from a cache because of the call above.
1114 aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
1115 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1116 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1117 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[0]));
1118 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[1]));
1119 CPPUNIT_ASSERT(std::isnan(aArray.mpNumericArray[2]));
1120
1121 // The column begins with a string header at row 1 (Column C).
1122 m_pDoc->SetString(ScAddress(2,0,0), "MyHeader");
1123 for (SCROW i = 1; i <= 9; ++i) // rows 2-10 are numeric.
1124 m_pDoc->SetValue(ScAddress(2,i,0), i);
1125
1126 aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,1,0), 9); // C2:C10
1127 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1128 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1129 for (size_t i = 0; i < 9; ++i)
1130 CPPUNIT_ASSERT_EQUAL(double(i+1), aArray.mpNumericArray[i]);
1131
1132 // The column begins with a number, followed by a string then followed by
1133 // a block of numbers (Column D).
1134 m_pDoc->SetValue(ScAddress(3,0,0), 0.0);
1135 m_pDoc->SetString(ScAddress(3,1,0), "Some string");
1136 for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
1137 m_pDoc->SetValue(ScAddress(3,i,0), i);
1138
1139 aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,2,0), 8); // D3:D10
1140 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1141 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1142 for (size_t i = 0; i < 8; ++i)
1143 CPPUNIT_ASSERT_EQUAL(double(i+2), aArray.mpNumericArray[i]);
1144
1145 // The column begins with a formula, followed by a string then followed by
1146 // a block of numbers (Column E).
1147 m_pDoc->SetString(ScAddress(4,0,0), "=1*2");
1148 m_pDoc->SetString(ScAddress(4,1,0), "Some string");
1149 for (SCROW i = 2; i <= 9; ++i) // rows 3-10 are numeric.
1150 m_pDoc->SetValue(ScAddress(4,i,0), i*2);
1151
1152 aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 8); // E3:E10
1153 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
1154 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
1155 for (size_t i = 0; i < 8; ++i)
1156 CPPUNIT_ASSERT_EQUAL(double((i+2)*2), aArray.mpNumericArray[i]);
1157
1158 m_pDoc->DeleteTab(0);
1159 }
1160
testGroupConverter3D()1161 void TestFormula::testGroupConverter3D()
1162 {
1163 m_pDoc->InsertTab(0, "Test");
1164 m_pDoc->InsertTab(1, "Test2");
1165
1166 m_pDoc->SetValue(1, 0, 0, 1.0);
1167 m_pDoc->SetValue(1, 0, 1, 2.0);
1168
1169 for (SCROW nRow = 0; nRow < 200; ++nRow)
1170 {
1171 OUString aFormula = "=SUM(Test.B" + OUString::number(nRow+1) + ":Test2.B" + OUString::number(nRow+1) + ")";
1172 m_pDoc->SetString(0, nRow, 0, aFormula);
1173 }
1174
1175 double nVal = m_pDoc->GetValue(0, 0, 0);
1176 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
1177
1178 m_pDoc->DeleteTab(1);
1179 m_pDoc->DeleteTab(0);
1180 }
1181
testFormulaTokenEquality()1182 void TestFormula::testFormulaTokenEquality()
1183 {
1184 struct FormulaTokenEqualityTest
1185 {
1186 const char* mpFormula1;
1187 const char* mpFormula2;
1188 bool mbEqual;
1189 };
1190
1191 static const FormulaTokenEqualityTest aTests[] = {
1192 { "R1C2", "R1C2", true },
1193 { "R1C2", "R1C3", false },
1194 { "R1C2", "R2C2", false },
1195 { "RC2", "RC[1]", false },
1196 { "R1C2:R10C2", "R1C2:R10C2", true },
1197 { "R1C2:R10C2", "R1C2:R11C2", false },
1198 { "1", "2", false },
1199 { "RC[1]+1.2", "RC[1]+1.2", true },
1200 { "RC[1]*0.2", "RC[1]*0.5", false },
1201 { "\"Test1\"", "\"Test2\"", false },
1202 { "\"Test\"", "\"Test\"", true },
1203 { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
1204 { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
1205 };
1206
1207 formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
1208 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
1209 {
1210 ScFormulaCell aCell1(*m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
1211 ScFormulaCell aCell2(*m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
1212
1213 ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
1214 if (aTests[i].mbEqual)
1215 {
1216 if (eComp == ScFormulaCell::NotEqual)
1217 {
1218 std::ostringstream os;
1219 os << "These two formulas should be evaluated equal: '"
1220 << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
1221 CPPUNIT_FAIL(os.str());
1222 }
1223 }
1224 else
1225 {
1226 if (eComp != ScFormulaCell::NotEqual)
1227 {
1228 std::ostringstream os;
1229 os << "These two formulas should be evaluated non-equal: '"
1230 << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
1231 CPPUNIT_FAIL(os.str());
1232 }
1233 }
1234 }
1235 }
1236
testFormulaRefData()1237 void TestFormula::testFormulaRefData()
1238 {
1239 std::unique_ptr<ScDocument> pDoc = std::make_unique<ScDocument>();
1240
1241 ScAddress aAddr(4,5,3), aPos(2,2,2);
1242 ScSingleRefData aRef;
1243 aRef.InitAddress(aAddr);
1244 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsRowRel());
1245 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsColRel());
1246 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsTabRel());
1247 CPPUNIT_ASSERT_EQUAL(SCCOL(4), aRef.Col());
1248 CPPUNIT_ASSERT_EQUAL(SCROW(5), aRef.Row());
1249 CPPUNIT_ASSERT_EQUAL(SCTAB(3), aRef.Tab());
1250
1251 aRef.SetRowRel(true);
1252 aRef.SetColRel(true);
1253 aRef.SetTabRel(true);
1254 aRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos);
1255 CPPUNIT_ASSERT_EQUAL(SCCOL(2), aRef.Col());
1256 CPPUNIT_ASSERT_EQUAL(SCROW(3), aRef.Row());
1257 CPPUNIT_ASSERT_EQUAL(SCTAB(1), aRef.Tab());
1258
1259 // Test extension of range reference.
1260
1261 ScComplexRefData aDoubleRef;
1262 aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
1263
1264 aRef.InitAddress(ScAddress(6,5,0));
1265
1266 aDoubleRef.Extend(pDoc->GetSheetLimits(), aRef, ScAddress());
1267 ScRange aTest = aDoubleRef.toAbs(*pDoc, ScAddress());
1268 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(2,2,0), aTest.aStart);
1269 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(6,5,0), aTest.aEnd);
1270
1271 ScComplexRefData aDoubleRef2;
1272 aDoubleRef2.InitRangeRel(*pDoc, ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
1273 aDoubleRef.Extend(pDoc->GetSheetLimits(), aDoubleRef2, ScAddress(5,5,0));
1274 aTest = aDoubleRef.toAbs(*pDoc, ScAddress(5,5,0));
1275
1276 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(1,2,0), aTest.aStart);
1277 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(8,6,0), aTest.aEnd);
1278 }
1279
testFormulaCompiler()1280 void TestFormula::testFormulaCompiler()
1281 {
1282 static const struct {
1283 const char* pInput; FormulaGrammar::Grammar eInputGram;
1284 const char* pOutput; FormulaGrammar::Grammar eOutputGram;
1285 } aTests[] = {
1286 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF },
1287 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE },
1288 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1 },
1289 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1 },
1290 };
1291
1292 for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
1293 {
1294 std::unique_ptr<ScTokenArray> pArray = compileFormula(m_pDoc, OUString::createFromAscii(aTests[i].pInput), aTests[i].eInputGram);
1295 CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray);
1296
1297 OUString aFormula = toString(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
1298 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
1299 }
1300 }
1301
testFormulaCompilerJumpReordering()1302 void TestFormula::testFormulaCompilerJumpReordering()
1303 {
1304 struct TokenCheck
1305 {
1306 OpCode meOp;
1307 StackVar meType;
1308 };
1309
1310 {
1311 // Compile formula string first.
1312 std::unique_ptr<ScTokenArray> pCode(compileFormula(m_pDoc, "=IF(B1;12;\"text\")"));
1313 CPPUNIT_ASSERT(pCode);
1314
1315 // Then generate RPN tokens.
1316 ScCompiler aCompRPN(*m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1317 aCompRPN.CompileTokenArray();
1318
1319 // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
1320 static const TokenCheck aCheckRPN[] =
1321 {
1322 { ocPush, svSingleRef },
1323 { ocIf, svUnknown }, // type is context dependent, don't test it
1324 { ocPush, svDouble },
1325 { ocSep, svSep },
1326 { ocPush, svString },
1327 { ocClose, svSep },
1328 };
1329
1330 sal_uInt16 nLen = pCode->GetCodeLen();
1331 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN)), nLen);
1332
1333 FormulaToken** ppTokens = pCode->GetCode();
1334 for (sal_uInt16 i = 0; i < nLen; ++i)
1335 {
1336 const FormulaToken* p = ppTokens[i];
1337 CPPUNIT_ASSERT_EQUAL(aCheckRPN[i].meOp, p->GetOpCode());
1338 if (aCheckRPN[i].meOp != ocIf )
1339 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN[i].meType), static_cast<int>(p->GetType()));
1340 }
1341
1342 // Generate RPN tokens again, but this time no jump command reordering.
1343 pCode->DelRPN();
1344 ScCompiler aCompRPN2(*m_pDoc, ScAddress(), *pCode, FormulaGrammar::GRAM_NATIVE);
1345 aCompRPN2.EnableJumpCommandReorder(false);
1346 aCompRPN2.CompileTokenArray();
1347
1348 static const TokenCheck aCheckRPN2[] =
1349 {
1350 { ocPush, svSingleRef },
1351 { ocPush, svDouble },
1352 { ocPush, svString },
1353 { ocIf, svUnknown }, // type is context dependent, don't test it
1354 };
1355
1356 nLen = pCode->GetCodeLen();
1357 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(SAL_N_ELEMENTS(aCheckRPN2)), nLen);
1358 ppTokens = pCode->GetCode();
1359 for (sal_uInt16 i = 0; i < nLen; ++i)
1360 {
1361 const FormulaToken* p = ppTokens[i];
1362 CPPUNIT_ASSERT_EQUAL(aCheckRPN2[i].meOp, p->GetOpCode());
1363 if (aCheckRPN[i].meOp == ocPush)
1364 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN2[i].meType), static_cast<int>(p->GetType()));
1365 }
1366 }
1367 }
1368
testFormulaCompilerImplicitIntersection2Param()1369 void TestFormula::testFormulaCompilerImplicitIntersection2Param()
1370 {
1371 struct TestCaseFormula
1372 {
1373 OUString aFormula;
1374 ScAddress aCellAddress;
1375 ScRange aSumRange;
1376 bool bStartColRel; // SumRange-StartCol
1377 bool bEndColRel; // SumRange-EndCol
1378 };
1379
1380 m_pDoc->InsertTab(0, "Formula");
1381 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1382
1383 {
1384 TestCaseFormula aTestCases[] =
1385 {
1386 // Formula, FormulaCellAddress, SumRange with Implicit Intersection
1387
1388 // Sumrange is single cell, address is abs
1389 {
1390 OUString("=SUMIF($B$2:$B$10;F2;$D$5)"),
1391 ScAddress(7, 5, 0),
1392 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1393 false,
1394 false
1395 },
1396
1397 // Sumrange is single cell, address is relative
1398 {
1399 OUString("=SUMIF($B$2:$B$10;F2;D5)"),
1400 ScAddress(7, 5, 0),
1401 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1402 true,
1403 true
1404 },
1405
1406 // Baserange(abs,abs), Sumrange(abs,abs)
1407 {
1408 OUString("=SUMIF($B$2:$B$10;F2;$D$5:$D$10)"),
1409 ScAddress(7, 5, 0),
1410 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1411 false,
1412 false
1413 },
1414
1415 // Baserange(abs,rel), Sumrange(abs,abs)
1416 {
1417 OUString("=SUMIF($B$2:B10;F2;$D$5:$D$10)"),
1418 ScAddress(7, 5, 0),
1419 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1420 false,
1421 false
1422 },
1423
1424 // Baserange(rel,abs), Sumrange(abs,abs)
1425 {
1426 OUString("=SUMIF(B2:$B$10;F2;$D$5:$D$10)"),
1427 ScAddress(7, 5, 0),
1428 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1429 false,
1430 false
1431 },
1432
1433 // Baserange(rel,rel), Sumrange(abs,abs)
1434 {
1435 OUString("=SUMIF(B2:B10;F2;$D$5:$D$10)"),
1436 ScAddress(7, 5, 0),
1437 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1438 false,
1439 false
1440 },
1441
1442 // Baserange(abs,abs), Sumrange(abs,rel)
1443 {
1444 OUString("=SUMIF($B$2:$B$10;F2;$D$5:D10)"),
1445 ScAddress(7, 5, 0),
1446 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1447 false,
1448 true
1449 },
1450
1451 // Baserange(abs,abs), Sumrange(rel,abs)
1452 {
1453 OUString("=SUMIF($B$2:$B$10;F2;D5:$D$10)"),
1454 ScAddress(7, 5, 0),
1455 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1456 true,
1457 false
1458 },
1459
1460 // Baserange(abs,abs), Sumrange(rel,rel)
1461 {
1462 OUString("=SUMIF($B$2:$B$10;F2;D5:D10)"),
1463 ScAddress(7, 5, 0),
1464 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1465 true,
1466 true
1467 }
1468 };
1469
1470 for (const auto& rCase : aTestCases)
1471 {
1472 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1473 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1474 const ScTokenArray* pCode = pCell->GetCode();
1475 CPPUNIT_ASSERT(pCode);
1476
1477 sal_uInt16 nLen = pCode->GetCodeLen();
1478 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(4), nLen);
1479
1480 FormulaToken** ppTokens = pCode->GetCode();
1481
1482 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(first argument to SUMIF)", svDoubleRef, ppTokens[0]->GetType());
1483 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(third argument to SUMIF)", svDoubleRef, ppTokens[2]->GetType());
1484
1485 ScComplexRefData aSumRangeData = *ppTokens[2]->GetDoubleRef();
1486 ScRange aSumRange = aSumRangeData.toAbs(*m_pDoc, rCase.aCellAddress);
1487 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sum-range in RPN array", rCase.aSumRange, aSumRange);
1488
1489 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for start column address in sum-range", rCase.bStartColRel, aSumRangeData.Ref1.IsColRel());
1490 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for end column address in sum-range", rCase.bEndColRel, aSumRangeData.Ref2.IsColRel());
1491 }
1492 }
1493 }
1494
testFormulaCompilerImplicitIntersection1ParamNoChange()1495 void TestFormula::testFormulaCompilerImplicitIntersection1ParamNoChange()
1496 {
1497 struct TestCaseFormulaNoChange
1498 {
1499 OUString aFormula;
1500 ScAddress aCellAddress;
1501 bool bMatrixFormula;
1502 bool bForcedArray;
1503 };
1504
1505 m_pDoc->InsertTab(0, "Formula");
1506 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1507
1508 {
1509 ScAddress aStartAddr(4, 5, 0);
1510 TestCaseFormulaNoChange aCasesNoChange[] =
1511 {
1512 {
1513 OUString("=COS(A$2:A$100)"), // No change because of abs col ref.
1514 aStartAddr,
1515 false,
1516 false
1517 },
1518 {
1519 OUString("=COS($A7:$A100)"), // No intersection
1520 aStartAddr,
1521 false,
1522 false
1523 },
1524 {
1525 OUString("=COS($A5:$C7)"), // No intersection 2-D range
1526 aStartAddr,
1527 false,
1528 false
1529 },
1530 {
1531 OUString("=SUMPRODUCT(COS(A6:A10))"), // COS() in forced array mode
1532 aStartAddr,
1533 false,
1534 true
1535 },
1536 {
1537 OUString("=COS(A6:A10)"), // Matrix formula
1538 aStartAddr,
1539 true,
1540 false
1541 }
1542 };
1543
1544 for (const auto& rCase : aCasesNoChange)
1545 {
1546 if (rCase.bMatrixFormula)
1547 {
1548 ScMarkData aMark(m_pDoc->GetSheetLimits());
1549 aMark.SelectOneTable(0);
1550 SCCOL nColStart = rCase.aCellAddress.Col();
1551 SCROW nRowStart = rCase.aCellAddress.Row();
1552 m_pDoc->InsertMatrixFormula(nColStart, nRowStart, nColStart, nRowStart + 4,
1553 aMark, rCase.aFormula);
1554 }
1555 else
1556 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1557
1558 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1559 const ScTokenArray* pCode = pCell->GetCode();
1560 CPPUNIT_ASSERT(pCode);
1561
1562 sal_uInt16 nRPNLen = pCode->GetCodeLen();
1563 sal_uInt16 nRawLen = pCode->GetLen();
1564 sal_uInt16 nRawArgPos;
1565 if (rCase.bForcedArray)
1566 {
1567 nRawArgPos = 4;
1568 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(7), nRawLen);
1569 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(3), nRPNLen);
1570 }
1571 else
1572 {
1573 nRawArgPos = 2;
1574 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16>(4), nRawLen);
1575 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1576 }
1577
1578 FormulaToken** ppRawTokens = pCode->GetArray();
1579 FormulaToken** ppRPNTokens = pCode->GetCode();
1580
1581 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of raw token(argument to COS)", svDoubleRef, ppRawTokens[nRawArgPos]->GetType());
1582 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svDoubleRef, ppRPNTokens[0]->GetType());
1583
1584 ScComplexRefData aArgRangeRaw = *ppRawTokens[nRawArgPos]->GetDoubleRef();
1585 ScComplexRefData aArgRangeRPN = *ppRPNTokens[0]->GetDoubleRef();
1586 bool bRawMatchRPNToken(aArgRangeRaw == aArgRangeRPN);
1587 CPPUNIT_ASSERT_MESSAGE("raw arg token and RPN arg token contents do not match", bRawMatchRPNToken);
1588 }
1589 }
1590 }
1591
testFormulaCompilerImplicitIntersection1ParamWithChange()1592 void TestFormula::testFormulaCompilerImplicitIntersection1ParamWithChange()
1593 {
1594 struct TestCaseFormula
1595 {
1596 OUString aFormula;
1597 ScAddress aCellAddress;
1598 ScAddress aArgAddr;
1599 };
1600
1601 m_pDoc->InsertTab(0, "Formula");
1602 m_pDoc->InsertTab(1, "Formula1");
1603 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1604
1605 {
1606 ScAddress aStartAddr(10, 5, 0);
1607 TestCaseFormula aCasesWithChange[] =
1608 {
1609 {
1610 OUString("=COS($A6:$A100)"), // Corner case with intersection
1611 aStartAddr,
1612 ScAddress(0, 5, 0)
1613 },
1614 {
1615 OUString("=COS($A2:$A6)"), // Corner case with intersection
1616 aStartAddr,
1617 ScAddress(0, 5, 0)
1618 },
1619 {
1620 OUString("=COS($A2:$A100)"), // Typical 1D case
1621 aStartAddr,
1622 ScAddress(0, 5, 0)
1623 },
1624 {
1625 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1626 ScAddress(0, 0, 1), // Formula in sheet 1
1627 ScAddress(0, 0, 0)
1628 },
1629 {
1630 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1631 ScAddress(0, 2, 1), // Formula in sheet 1
1632 ScAddress(0, 2, 0)
1633 },
1634 {
1635 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1636 ScAddress(2, 0, 1), // Formula in sheet 1
1637 ScAddress(2, 0, 0)
1638 },
1639 {
1640 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1641 ScAddress(2, 2, 1), // Formula in sheet 1
1642 ScAddress(2, 2, 0)
1643 },
1644 {
1645 OUString("=COS($Formula.$A1:$C3)"), // Typical 2D case
1646 ScAddress(1, 1, 1), // Formula in sheet 1
1647 ScAddress(1, 1, 0)
1648 }
1649 };
1650
1651 for (const auto& rCase : aCasesWithChange)
1652 {
1653 m_pDoc->SetString(rCase.aCellAddress, rCase.aFormula);
1654
1655 const ScFormulaCell* pCell = m_pDoc->GetFormulaCell(rCase.aCellAddress);
1656 const ScTokenArray* pCode = pCell->GetCode();
1657 CPPUNIT_ASSERT(pCode);
1658
1659 sal_uInt16 nRPNLen = pCode->GetCodeLen();
1660 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16>(2), nRPNLen);
1661
1662 FormulaToken** ppRPNTokens = pCode->GetCode();
1663
1664 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef, ppRPNTokens[0]->GetType());
1665
1666 ScSingleRefData aArgAddrRPN = *ppRPNTokens[0]->GetSingleRef();
1667 ScAddress aArgAddrActual = aArgAddrRPN.toAbs(*m_pDoc, rCase.aCellAddress);
1668 CPPUNIT_ASSERT_EQUAL_MESSAGE("Computed implicit intersection singleref is wrong", rCase.aArgAddr, aArgAddrActual);
1669 }
1670 }
1671 }
1672
testFormulaCompilerImplicitIntersection1NoGroup()1673 void TestFormula::testFormulaCompilerImplicitIntersection1NoGroup()
1674 {
1675 m_pDoc->InsertTab(0, "Formula");
1676 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1677
1678 m_pDoc->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
1679 m_pDoc->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
1680
1681 // Implicit intersection optimization in ScCompiler::HandleIIOpCode() internally changes
1682 // these to "=COS(A3)" and "=COS(A4)", but these shouldn't be merged into a formula group,
1683 // otherwise B4's formula would then be "=COS(A2:A6)".
1684 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "COS(A1:A5)", "Formula in B3 has changed.");
1685 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,3,0), "COS(A1:A5)", "Formula in B4 has changed.");
1686
1687 m_pDoc->DeleteTab(0);
1688 }
1689
testFormulaCompilerImplicitIntersectionOperators()1690 void TestFormula::testFormulaCompilerImplicitIntersectionOperators()
1691 {
1692 struct TestCase
1693 {
1694 OUString formula[3];
1695 double result[3];
1696 };
1697
1698 m_pDoc->InsertTab(0, "Test");
1699 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1700
1701 m_pDoc->SetValue(2, 0, 0, 5); // C1
1702 m_pDoc->SetValue(2, 1, 0, 4); // C2
1703 m_pDoc->SetValue(2, 2, 0, 3); // C3
1704 m_pDoc->SetValue(3, 0, 0, 1); // D1
1705 m_pDoc->SetValue(3, 1, 0, 2); // D2
1706 m_pDoc->SetValue(3, 2, 0, 3); // D3
1707
1708 TestCase tests[] =
1709 {
1710 { OUString("=C:C/D:D"), OUString("=C:C/D:D"), OUString("=C:C/D:D"), 5, 2, 1 },
1711 { OUString("=C1:C2/D1:D2"), OUString("=C2:C3/D2:D3"), OUString("=C3:C4/D3:D4"), 5, 2, 1 }
1712 };
1713
1714 for (const TestCase& test : tests)
1715 {
1716 for(int i = 0; i < 2; ++i )
1717 m_pDoc->SetString(ScAddress(4,i,0), test.formula[i]); // E1-3
1718 for(int i = 0; i < 2; ++i )
1719 CPPUNIT_ASSERT_EQUAL_MESSAGE(OUString( test.formula[i] + " result incorrect in row " + OUString::number(i+1)).toUtf8().getStr(),
1720 test.result[i], m_pDoc->GetValue(ScAddress(4,i,0)));
1721 }
1722
1723 m_pDoc->DeleteTab(0);
1724 }
1725
testFormulaAnnotateTrimOnDoubleRefs()1726 void TestFormula::testFormulaAnnotateTrimOnDoubleRefs()
1727 {
1728 m_pDoc->InsertTab(0, "Test");
1729 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1730
1731 constexpr sal_Int32 nCols = 2;
1732 constexpr sal_Int32 nRows = 5;
1733
1734 // Values in A1:B5
1735 constexpr sal_Int32 aMat[nRows][nCols] = {
1736 {4, 50},
1737 {5, 30},
1738 {4, 40},
1739 {0, 70},
1740 {5, 90}
1741 };
1742
1743 for (sal_Int32 nCol = 0; nCol < nCols; ++nCol)
1744 {
1745 for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
1746 m_pDoc->SetValue(nCol, nRow, 0, aMat[nRow][nCol]);
1747 }
1748
1749 m_pDoc->SetValue(2, 0, 0, 4); // C1 = 4
1750 m_pDoc->SetValue(3, 0, 0, 5); // D1 = 5
1751
1752 ScMarkData aMark(m_pDoc->GetSheetLimits());
1753 aMark.SelectOneTable(0);
1754
1755 struct TestCase
1756 {
1757 OUString aFormula;
1758 ScRange aTrimmableRange;
1759 double fResult;
1760 bool bMatrixFormula;
1761 };
1762
1763 constexpr sal_Int32 nTestCases = 5;
1764 TestCase aTestCases[nTestCases] = {
1765 {
1766 "=SUM(IF($C$1=A:A;B:B)/10*D1)",
1767 ScRange(0, 0, 0, 0, 1048575, 0),
1768 45.0,
1769 true
1770 },
1771
1772 {
1773 "=SUM(IF(A:A=5;B:B)/10*D1)",
1774 ScRange(0, 0, 0, 0, 1048575, 0),
1775 60.0,
1776 true
1777 },
1778
1779 {
1780 "=SUM(IF($C$1=A:A;B:B;B:B)/10*D1)", // IF has else clause
1781 ScRange(-1, -1, -1, -1, -1, -1), // Has no trimmable double-ref.
1782 140.0,
1783 true
1784 },
1785
1786 {
1787 "=SUM(IF($C$1=A:A;B:B)/10*D1)",
1788 ScRange(-1, -1, -1, -1, -1, -1), // Has no trimmable double-ref.
1789 25,
1790 false // Not in matrix mode.
1791 },
1792
1793 {
1794 "=SUMPRODUCT(A:A=$C$1; 1-(A:A=$C$1))",
1795 ScRange(-1, -1, -1, -1, -1, -1), // Has no trimmable double-ref.
1796 0.0,
1797 false // Not in matrix mode.
1798 },
1799 };
1800
1801 for (sal_Int32 nTestIdx = 0; nTestIdx < nTestCases; ++nTestIdx)
1802 {
1803 TestCase& rTestCase = aTestCases[nTestIdx];
1804 if (rTestCase.bMatrixFormula)
1805 m_pDoc->InsertMatrixFormula(4, 0, 4, 0, aMark, rTestCase.aFormula); // Formula in E1
1806 else
1807 m_pDoc->SetString(ScAddress(4, 0, 0), rTestCase.aFormula); // Formula in E1
1808
1809 std::string aMsgStart = "TestCase#" + std::to_string(nTestIdx + 1) + " : ";
1810 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsgStart + "Incorrect formula result", rTestCase.fResult, m_pDoc->GetValue(ScAddress(4, 0, 0)));
1811
1812 ScTokenArray* pCode = getTokens(*m_pDoc, ScAddress(4, 0, 0));
1813 sal_Int32 nLen = pCode->GetCodeLen();
1814 FormulaToken** pRPNArray = pCode->GetCode();
1815
1816 for (sal_Int32 nIdx = 0; nIdx < nLen; ++nIdx)
1817 {
1818 FormulaToken* pTok = pRPNArray[nIdx];
1819 if (pTok && pTok->GetType() == svDoubleRef)
1820 {
1821 ScRange aRange = pTok->GetDoubleRef()->toAbs(*m_pDoc, ScAddress(4, 0, 0));
1822 if (aRange == rTestCase.aTrimmableRange)
1823 CPPUNIT_ASSERT_MESSAGE(aMsgStart + "Double ref is incorrectly flagged as not trimmable to data",
1824 pTok->GetDoubleRef()->IsTrimToData());
1825 else
1826 CPPUNIT_ASSERT_MESSAGE(aMsgStart + "Double ref is incorrectly flagged as trimmable to data",
1827 !pTok->GetDoubleRef()->IsTrimToData());
1828 }
1829 }
1830 }
1831
1832 m_pDoc->DeleteTab(0);
1833 }
1834
testFormulaRefUpdate()1835 void TestFormula::testFormulaRefUpdate()
1836 {
1837 m_pDoc->InsertTab(0, "Formula");
1838
1839 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1840
1841 m_pDoc->SetValue(ScAddress(0,0,0), 2.0); // A1
1842 m_pDoc->SetString(ScAddress(2,2,0), "=A1"); // C3
1843 m_pDoc->SetString(ScAddress(2,3,0), "=$A$1"); // C4
1844
1845 ScAddress aPos(2,2,0);
1846 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1847
1848 aPos = ScAddress(2,3,0);
1849 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1850
1851 // Delete row 2 to push formula cells up (to C2:C3).
1852 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1853
1854 aPos = ScAddress(2,1,0);
1855 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C2.");
1856
1857 aPos = ScAddress(2,2,0);
1858 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C3.");
1859
1860 // Insert one row at row 2 to move them back.
1861 m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
1862
1863 aPos = ScAddress(2,2,0);
1864 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1865
1866 aPos = ScAddress(2,3,0);
1867 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1868
1869 // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
1870 m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1871
1872 aPos = ScAddress(2,4,0);
1873 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A3", "Wrong formula in C5.");
1874
1875 aPos = ScAddress(2,5,0);
1876 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$3", "Wrong formula in C6.");
1877
1878 // Delete 2 rows at row 1 to shift them back.
1879 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
1880
1881 aPos = ScAddress(2,2,0);
1882 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1883
1884 aPos = ScAddress(2,3,0);
1885 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1886
1887 // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
1888 m_pDoc->InsertCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1889
1890 aPos = ScAddress(5,2,0);
1891 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in F3.");
1892
1893 aPos = ScAddress(5,3,0);
1894 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in F4.");
1895
1896 // Delete columns B:D to shift them back.
1897 m_pDoc->DeleteCol(ScRange(1,0,0,3,m_pDoc->MaxRow(),0));
1898
1899 aPos = ScAddress(2,2,0);
1900 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1901
1902 aPos = ScAddress(2,3,0);
1903 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1904
1905 // Insert cells over A1:A3 to only shift A1 down to A4.
1906 m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
1907
1908 aPos = ScAddress(2,2,0);
1909 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A4", "Wrong formula in C3.");
1910
1911 aPos = ScAddress(2,3,0);
1912 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$4", "Wrong formula in C4.");
1913
1914 // .. and back.
1915 m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
1916
1917 aPos = ScAddress(2,2,0);
1918 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "A1", "Wrong formula in C3.");
1919
1920 aPos = ScAddress(2,3,0);
1921 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$A$1", "Wrong formula in C4.");
1922
1923 // Delete row 1 which will delete the value cell (A1).
1924 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1925
1926 aPos = ScAddress(2,1,0);
1927 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
1928 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1929 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1930 aPos = ScAddress(2,2,0);
1931 pFC = m_pDoc->GetFormulaCell(aPos);
1932 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
1933 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
1934
1935 // Clear all and start over.
1936 clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
1937
1938 // Test range updates
1939
1940 // Fill B2:C3 with values.
1941 m_pDoc->SetValue(ScAddress(1,1,0), 1);
1942 m_pDoc->SetValue(ScAddress(1,2,0), 2);
1943 m_pDoc->SetValue(ScAddress(2,1,0), 3);
1944 m_pDoc->SetValue(ScAddress(2,2,0), 4);
1945
1946 m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
1947 m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
1948
1949 aPos = ScAddress(0,5,0);
1950 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1951
1952 aPos = ScAddress(0,6,0);
1953 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1954
1955 // Insert a row at row 1.
1956 m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1957
1958 aPos = ScAddress(0,6,0);
1959 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B3:C4)", "Wrong formula in A7.");
1960
1961 aPos = ScAddress(0,7,0);
1962 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$3:$C$4)", "Wrong formula in A8.");
1963
1964 // ... and back.
1965 m_pDoc->DeleteRow(ScRange(0,0,0,m_pDoc->MaxCol(),0,0));
1966
1967 aPos = ScAddress(0,5,0);
1968 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1969
1970 aPos = ScAddress(0,6,0);
1971 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1972
1973 // Insert columns B:C to shift only the value range.
1974 m_pDoc->InsertCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1975
1976 aPos = ScAddress(0,5,0);
1977 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(D2:E3)", "Wrong formula in A6.");
1978
1979 aPos = ScAddress(0,6,0);
1980 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($D$2:$E$3)", "Wrong formula in A7.");
1981
1982 // ... and back.
1983 m_pDoc->DeleteCol(ScRange(1,0,0,2,m_pDoc->MaxRow(),0));
1984
1985 aPos = ScAddress(0,5,0);
1986 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
1987
1988 aPos = ScAddress(0,6,0);
1989 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1990
1991 // Insert rows 5:6 to shift the formula cells only.
1992 m_pDoc->InsertRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
1993
1994 aPos = ScAddress(0,7,0);
1995 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A8.");
1996
1997 aPos = ScAddress(0,8,0);
1998 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A9.");
1999
2000 // ... and back.
2001 m_pDoc->DeleteRow(ScRange(0,4,0,m_pDoc->MaxCol(),5,0));
2002
2003 aPos = ScAddress(0,5,0);
2004 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B2:C3)", "Wrong formula in A6.");
2005
2006 aPos = ScAddress(0,6,0);
2007 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM($B$2:$C$3)", "Wrong formula in A7.");
2008
2009 // Check the values of the formula cells in A6:A7.
2010 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2011 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2012
2013 // Insert cells over B1:B2 to partially shift value range.
2014 m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
2015
2016 // Check the values of the formula cells in A6:A7 again.
2017 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2018 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2019
2020 // ... and shift them back.
2021 m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
2022
2023 // The formula cell results should be back too.
2024 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2025 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2026
2027 // Delete rows 2:3 to completely remove the referenced range.
2028 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),2,0));
2029
2030 // Both A4 and A5 should show #REF! errors.
2031 pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
2032 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
2033 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
2034
2035 pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
2036 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
2037 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), static_cast<int>(pFC->GetErrCode()));
2038
2039 m_pDoc->DeleteTab(0);
2040 }
2041
testFormulaRefUpdateRange()2042 void TestFormula::testFormulaRefUpdateRange()
2043 {
2044 m_pDoc->InsertTab(0, "Formula");
2045
2046 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2047
2048 setExpandRefs(false);
2049
2050 // Set values to B2:C5.
2051 m_pDoc->SetValue(ScAddress(1,1,0), 1);
2052 m_pDoc->SetValue(ScAddress(1,2,0), 2);
2053 m_pDoc->SetValue(ScAddress(1,3,0), 3);
2054 m_pDoc->SetValue(ScAddress(1,4,0), 4);
2055 m_pDoc->SetValue(ScAddress(2,1,0), 5);
2056 m_pDoc->SetValue(ScAddress(2,2,0), 6);
2057 m_pDoc->SetValue(ScAddress(2,3,0), 7);
2058 m_pDoc->SetValue(ScAddress(2,4,0), 8);
2059
2060 // Set formula cells to A7 and A8.
2061 m_pDoc->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
2062 m_pDoc->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
2063
2064 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(B2:C5)", "Wrong formula in A7.");
2065
2066 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($B$2:$C$5)", "Wrong formula in A8.");
2067
2068 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2069 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2070
2071 // Delete row 3. This should shrink the range references by one row.
2072 m_pDoc->DeleteRow(ScRange(0,2,0,m_pDoc->MaxCol(),2,0));
2073
2074 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(B2:C4)", "Wrong formula in A6.");
2075
2076 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($B$2:$C$4)", "Wrong formula in A7.");
2077
2078 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2079 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2080
2081 // Delete row 4 - bottom of range
2082 m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
2083
2084 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(B2:C3)", "Wrong formula in A5.");
2085
2086 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($B$2:$C$3)", "Wrong formula in A6.");
2087
2088 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,4,0)));
2089 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2090
2091 // Delete row 2 - top of range
2092 m_pDoc->DeleteRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
2093
2094 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,3,0), "SUM(B2:C2)", "Wrong formula in A4.");
2095
2096 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM($B$2:$C$2)", "Wrong formula in A5.");
2097
2098 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,3,0)));
2099 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,4,0)));
2100
2101 // Clear the range and start over.
2102 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
2103
2104 // Fill C2:F3 with values.
2105 m_pDoc->SetValue(ScAddress(2,1,0), 1);
2106 m_pDoc->SetValue(ScAddress(3,1,0), 2);
2107 m_pDoc->SetValue(ScAddress(4,1,0), 3);
2108 m_pDoc->SetValue(ScAddress(5,1,0), 4);
2109 m_pDoc->SetValue(ScAddress(2,2,0), 5);
2110 m_pDoc->SetValue(ScAddress(3,2,0), 6);
2111 m_pDoc->SetValue(ScAddress(4,2,0), 7);
2112 m_pDoc->SetValue(ScAddress(5,2,0), 8);
2113
2114 // Set formulas to A2 and A3.
2115 m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
2116 m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
2117
2118 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:F3)", "Wrong formula in A2.");
2119
2120 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$F$3)", "Wrong formula in A3.");
2121
2122 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2123 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2124
2125 // Delete column D.
2126 m_pDoc->DeleteCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
2127
2128 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:E3)", "Wrong formula in A2.");
2129
2130 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$E$3)", "Wrong formula in A3.");
2131
2132 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2133 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2134
2135 // Delete column E - the right edge of reference range.
2136 m_pDoc->DeleteCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
2137
2138 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:D3)", "Wrong formula in A2.");
2139
2140 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$D$3)", "Wrong formula in A3.");
2141
2142 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2143 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2144
2145 // Delete column C - the left edge of reference range.
2146 m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2147
2148 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C2:C3)", "Wrong formula in A2.");
2149
2150 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$C$3)", "Wrong formula in A3.");
2151
2152 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
2153 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2154
2155 // Clear the range and start over.
2156 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
2157
2158 // Disable expansion of range reference on insertion in adjacent areas.
2159 setExpandRefs(false);
2160
2161 // Fill C2:D3 with values.
2162 m_pDoc->SetValue(ScAddress(2,1,0), 1);
2163 m_pDoc->SetValue(ScAddress(3,1,0), 2);
2164 m_pDoc->SetValue(ScAddress(2,2,0), 3);
2165 m_pDoc->SetValue(ScAddress(3,2,0), 4);
2166
2167 // Set formulas at A5 and A6.
2168 m_pDoc->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
2169 m_pDoc->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
2170
2171 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
2172
2173 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
2174
2175 // Insert a column at column C. This should simply shift the reference without expansion.
2176 m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2177
2178 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(D2:E3)", "Wrong formula in A5.");
2179
2180 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($D$2:$E$3)", "Wrong formula in A6.");
2181
2182 // Shift it back.
2183 m_pDoc->DeleteCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2184
2185 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
2186
2187 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
2188
2189 // Insert at column D. This should expand the reference by one column length.
2190 m_pDoc->InsertCol(ScRange(3,0,0,3,m_pDoc->MaxRow(),0));
2191
2192 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
2193
2194 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
2195
2196 // Insert at column F. No expansion should occur since the edge expansion is turned off.
2197 m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
2198
2199 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
2200
2201 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
2202
2203 // Insert at row 2. No expansion should occur with edge expansion turned off.
2204 m_pDoc->InsertRow(ScRange(0,1,0,m_pDoc->MaxCol(),1,0));
2205
2206 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "SUM(C3:E4)", "Wrong formula in A6.");
2207
2208 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM($C$3:$E$4)", "Wrong formula in A7.");
2209
2210 // Insert at row 4 to expand the reference range.
2211 m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),3,0));
2212
2213 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "SUM(C3:E5)", "Wrong formula in A7.");
2214
2215 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM($C$3:$E$5)", "Wrong formula in A8.");
2216
2217 // Insert at row 6. No expansion with edge expansion turned off.
2218 m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
2219
2220 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "SUM(C3:E5)", "Wrong formula in A8.");
2221
2222 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM($C$3:$E$5)", "Wrong formula in A9.");
2223
2224 // Clear the range and start over.
2225 clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
2226
2227 // Turn edge expansion on.
2228 setExpandRefs(true);
2229
2230 // Fill C6:D7 with values.
2231 m_pDoc->SetValue(ScAddress(2,5,0), 1);
2232 m_pDoc->SetValue(ScAddress(2,6,0), 2);
2233 m_pDoc->SetValue(ScAddress(3,5,0), 3);
2234 m_pDoc->SetValue(ScAddress(3,6,0), 4);
2235
2236 // Set formulas at A2 and A3.
2237 m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
2238 m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
2239
2240 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:D7)", "Wrong formula in A2.");
2241
2242 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$D$7)", "Wrong formula in A3.");
2243
2244 // Insert at column E. This should expand the reference range by one column.
2245 m_pDoc->InsertCol(ScRange(4,0,0,4,m_pDoc->MaxRow(),0));
2246
2247 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:E7)", "Wrong formula in A2.");
2248
2249 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$E$7)", "Wrong formula in A3.");
2250
2251 // Insert at column C to edge-expand the reference range.
2252 m_pDoc->InsertCol(ScRange(2,0,0,2,m_pDoc->MaxRow(),0));
2253
2254 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F7)", "Wrong formula in A2.");
2255
2256 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$7)", "Wrong formula in A3.");
2257
2258 // Insert at row 8 to edge-expand.
2259 m_pDoc->InsertRow(ScRange(0,7,0,m_pDoc->MaxCol(),7,0));
2260
2261 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F8)", "Wrong formula in A2.");
2262
2263 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$8)", "Wrong formula in A3.");
2264
2265 // Insert at row 6 to edge-expand.
2266 m_pDoc->InsertRow(ScRange(0,5,0,m_pDoc->MaxCol(),5,0));
2267
2268 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F9)", "Wrong formula in A2.");
2269
2270 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$9)", "Wrong formula in A3.");
2271
2272 m_pDoc->InsertTab(1, "StickyRange");
2273
2274 // A3:A18 all possible combinations of relative and absolute addressing,
2275 // leaving one row above and below unreferenced.
2276 ScAddress aPos(0,2,1);
2277 m_pDoc->SetString( aPos, "=B2:B1048575");
2278 aPos.IncRow();
2279 m_pDoc->SetString( aPos, "=B2:B$1048575");
2280 aPos.IncRow();
2281 m_pDoc->SetString( aPos, "=B2:$B1048575");
2282 aPos.IncRow();
2283 m_pDoc->SetString( aPos, "=B2:$B$1048575");
2284 aPos.IncRow();
2285 m_pDoc->SetString( aPos, "=B$2:B1048575");
2286 aPos.IncRow();
2287 m_pDoc->SetString( aPos, "=B$2:B$1048575");
2288 aPos.IncRow();
2289 m_pDoc->SetString( aPos, "=B$2:$B1048575");
2290 aPos.IncRow();
2291 m_pDoc->SetString( aPos, "=B$2:$B$1048575");
2292 aPos.IncRow();
2293 m_pDoc->SetString( aPos, "=$B2:B1048575");
2294 aPos.IncRow();
2295 m_pDoc->SetString( aPos, "=$B2:B$1048575");
2296 aPos.IncRow();
2297 m_pDoc->SetString( aPos, "=$B2:$B1048575");
2298 aPos.IncRow();
2299 m_pDoc->SetString( aPos, "=$B2:$B$1048575");
2300 aPos.IncRow();
2301 m_pDoc->SetString( aPos, "=$B$2:B1048575");
2302 aPos.IncRow();
2303 m_pDoc->SetString( aPos, "=$B$2:B$1048575");
2304 aPos.IncRow();
2305 m_pDoc->SetString( aPos, "=$B$2:$B1048575");
2306 aPos.IncRow();
2307 m_pDoc->SetString( aPos, "=$B$2:$B$1048575");
2308 aPos.IncRow();
2309 // A19 reference to two cells on one row.
2310 m_pDoc->SetString( aPos, "=B1048575:C1048575");
2311 aPos.IncRow();
2312
2313 // Insert 2 rows in the middle to shift bottom reference down and make it
2314 // sticky.
2315 m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
2316
2317 // A3:A18 must not result in #REF! anywhere.
2318 aPos.Set(0,2,1);
2319 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:B1048576", "Wrong reference in A3 after insertion.");
2320 aPos.IncRow();
2321 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:B$1048576", "Wrong reference in A4 after insertion.");
2322 aPos.IncRow();
2323 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:$B1048576", "Wrong reference in A5 after insertion.");
2324 aPos.IncRow();
2325 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B2:$B$1048576", "Wrong reference in A6 after insertion.");
2326 aPos.IncRow();
2327 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:B1048576", "Wrong reference in A7 after insertion.");
2328 aPos.IncRow();
2329 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:B$1048576", "Wrong reference in A8 after insertion.");
2330 aPos.IncRow();
2331 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:$B1048576", "Wrong reference in A9 after insertion.");
2332 aPos.IncRow();
2333 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$2:$B$1048576", "Wrong reference in A10 after insertion.");
2334 aPos.IncRow();
2335 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:B1048576", "Wrong reference in A11 after insertion.");
2336 aPos.IncRow();
2337 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:B$1048576", "Wrong reference in A12 after insertion.");
2338 aPos.IncRow();
2339 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:$B1048576", "Wrong reference in A13 after insertion.");
2340 aPos.IncRow();
2341 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B2:$B$1048576", "Wrong reference in A14 after insertion.");
2342 aPos.IncRow();
2343 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:B1048576", "Wrong reference in A15 after insertion.");
2344 aPos.IncRow();
2345 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:B$1048576", "Wrong reference in A16 after insertion.");
2346 aPos.IncRow();
2347 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:$B1048576", "Wrong reference in A17 after insertion.");
2348 aPos.IncRow();
2349 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$2:$B$1048576", "Wrong reference in A18 after insertion.");
2350 aPos.IncRow();
2351
2352 // A19 reference to one row shifted out should be #REF!
2353 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B#REF!:C#REF!", "Wrong reference in A19 after insertion.");
2354 // A19 enter reference to last row.
2355 m_pDoc->SetString( aPos, "=B1048576:C1048576");
2356 aPos.IncRow();
2357
2358 // Delete row 1 to shift top reference up, bottom reference stays sticky.
2359 m_pDoc->DeleteRow(ScRange(0,0,1,m_pDoc->MaxCol(),0,1));
2360
2361 // Check sticky bottom references and display of entire column references,
2362 // now in A2:A17.
2363 aPos.Set(0,1,1);
2364 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A2 after deletion.");
2365 aPos.IncRow();
2366 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:B$1048576", "Wrong reference in A3 after deletion.");
2367 aPos.IncRow();
2368 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A4 after deletion.");
2369 aPos.IncRow();
2370 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
2371 aPos.IncRow();
2372 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:B1048576", "Wrong reference in A6 after deletion.");
2373 aPos.IncRow();
2374 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A7 after deletion.");
2375 aPos.IncRow();
2376 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
2377 aPos.IncRow();
2378 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A9 after deletion.");
2379 aPos.IncRow();
2380 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A10 after deletion.");
2381 aPos.IncRow();
2382 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
2383 aPos.IncRow();
2384 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A12 after deletion.");
2385 aPos.IncRow();
2386 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
2387 aPos.IncRow();
2388 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
2389 aPos.IncRow();
2390 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A15 after deletion.");
2391 aPos.IncRow();
2392 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2393 aPos.IncRow();
2394 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A17 after deletion.");
2395 aPos.IncRow();
2396
2397 // A18 reference to one last row should be shifted up.
2398 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1048575:C1048575", "Wrong reference in A18 after deletion.");
2399 aPos.IncRow();
2400
2401 // Insert 4 rows in the middle.
2402 m_pDoc->InsertRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+3, 1));
2403 // Delete 2 rows in the middle.
2404 m_pDoc->DeleteRow( ScRange( 0, aPos.Row(), 1, m_pDoc->MaxCol(), aPos.Row()+1, 1));
2405
2406 // References in A2:A17 must still be the same.
2407 aPos.Set(0,1,1);
2408 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A2 after deletion.");
2409 aPos.IncRow();
2410 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:B$1048576", "Wrong reference in A3 after deletion.");
2411 aPos.IncRow();
2412 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A4 after deletion.");
2413 aPos.IncRow();
2414 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
2415 aPos.IncRow();
2416 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:B1048576", "Wrong reference in A6 after deletion.");
2417 aPos.IncRow();
2418 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:B", "Wrong reference in A7 after deletion.");
2419 aPos.IncRow();
2420 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
2421 aPos.IncRow();
2422 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "B:$B", "Wrong reference in A9 after deletion.");
2423 aPos.IncRow();
2424 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A10 after deletion.");
2425 aPos.IncRow();
2426 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
2427 aPos.IncRow();
2428 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A12 after deletion.");
2429 aPos.IncRow();
2430 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
2431 aPos.IncRow();
2432 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
2433 aPos.IncRow();
2434 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:B", "Wrong reference in A15 after deletion.");
2435 aPos.IncRow();
2436 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2437 aPos.IncRow();
2438 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "$B:$B", "Wrong reference in A17 after deletion.");
2439 aPos.IncRow();
2440
2441 // Enter values in B1 and B1048576 (last row).
2442 m_pDoc->SetValue( 1,0,1, 1.0);
2443 m_pDoc->SetValue( 1,m_pDoc->MaxRow(),1, 2.0);
2444 // Sticky reference including last row.
2445 m_pDoc->SetString( 2,0,1, "=SUM(B:B)");
2446 // Reference to last row.
2447 CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxRow() changed, adapt unit test.", 1048575, int(m_pDoc->MaxRow()));
2448 m_pDoc->SetString( 2,1,1, "=SUM(B1048576:C1048576)");
2449 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 3.0, m_pDoc->GetValue(2,0,1));
2450 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C2.", 2.0, m_pDoc->GetValue(2,1,1));
2451 // Delete last row.
2452 m_pDoc->DeleteRow( ScRange( 0, m_pDoc->MaxRow(), 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2453 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 1.0, m_pDoc->GetValue(2,0,1));
2454 CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C2 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,1,1));
2455
2456 // Enter values in A23 and AMJ23 (last column).
2457 m_pDoc->SetValue( 0,22,1, 1.0);
2458 m_pDoc->SetValue( m_pDoc->MaxCol(),22,1, 2.0);
2459 // C3 with sticky reference including last column.
2460 m_pDoc->SetString( 2,2,1, "=SUM(23:23)");
2461 // C4 with reference to last column.
2462 CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxCol() changed, adapt unit test.", 1023, int(m_pDoc->MaxCol()));
2463 m_pDoc->SetString( 2,3,1, "=SUM(AMJ22:AMJ23)");
2464 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 3.0, m_pDoc->GetValue(2,2,1));
2465 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C4.", 2.0, m_pDoc->GetValue(2,3,1));
2466 // Delete last column.
2467 m_pDoc->DeleteCol( ScRange( m_pDoc->MaxCol(), 0, 1, m_pDoc->MaxCol(), m_pDoc->MaxRow(), 1));
2468 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 1.0, m_pDoc->GetValue(2,2,1));
2469 CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C4 not invalidated.", OUString("#REF!"), m_pDoc->GetString(2,3,1));
2470
2471 m_pDoc->DeleteTab(1);
2472
2473 m_pDoc->DeleteTab(0);
2474 }
2475
testFormulaRefUpdateSheets()2476 void TestFormula::testFormulaRefUpdateSheets()
2477 {
2478 m_pDoc->InsertTab(0, "Sheet1");
2479 m_pDoc->InsertTab(1, "Sheet2");
2480
2481 OUString aName;
2482 m_pDoc->GetName(0, aName);
2483 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2484 m_pDoc->GetName(1, aName);
2485 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2486
2487 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2488
2489 // Set values to B2:C3 on sheet Sheet1.
2490 m_pDoc->SetValue(ScAddress(1,1,0), 1);
2491 m_pDoc->SetValue(ScAddress(1,2,0), 2);
2492 m_pDoc->SetValue(ScAddress(2,1,0), 3);
2493 m_pDoc->SetValue(ScAddress(2,2,0), 4);
2494
2495 // Set formulas to B2 and B3 on sheet Sheet2.
2496 m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
2497 m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
2498
2499 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2500
2501 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2502
2503 // Swap the sheets.
2504 m_pDoc->MoveTab(0, 1);
2505 m_pDoc->GetName(0, aName);
2506 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2507 m_pDoc->GetName(1, aName);
2508 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2509
2510 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2511
2512 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2513
2514 // Swap back.
2515 m_pDoc->MoveTab(0, 1);
2516 m_pDoc->GetName(0, aName);
2517 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2518 m_pDoc->GetName(1, aName);
2519 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2520
2521 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2522
2523 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2524
2525 // Insert a new sheet between the two.
2526 m_pDoc->InsertTab(1, "Temp");
2527
2528 m_pDoc->GetName(1, aName);
2529 CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName);
2530 m_pDoc->GetName(2, aName);
2531 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2532
2533 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2534
2535 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2536
2537 // Move the last sheet (Sheet2) to the first position.
2538 m_pDoc->MoveTab(2, 0);
2539
2540 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2541
2542 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2543
2544 // Move back.
2545 m_pDoc->MoveTab(0, 2);
2546
2547 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2548
2549 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2550
2551 // Move the "Temp" sheet to the last position.
2552 m_pDoc->MoveTab(1, 2);
2553
2554 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2555
2556 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2557
2558 // Move back.
2559 m_pDoc->MoveTab(2, 1);
2560
2561 // Delete the temporary sheet.
2562 m_pDoc->DeleteTab(1);
2563
2564 m_pDoc->GetName(1, aName);
2565 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2566
2567 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2568
2569 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2570
2571 // Insert a new sheet before the first one.
2572 m_pDoc->InsertTab(0, "Temp");
2573
2574 m_pDoc->GetName(1, aName);
2575 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
2576 m_pDoc->GetName(2, aName);
2577 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2578
2579 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2580
2581 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2582
2583 // Delete the temporary sheet.
2584 m_pDoc->DeleteTab(0);
2585
2586 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2587
2588 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2589
2590 // Append a bunch of sheets.
2591 m_pDoc->InsertTab(2, "Temp1");
2592 m_pDoc->InsertTab(3, "Temp2");
2593 m_pDoc->InsertTab(4, "Temp3");
2594
2595 // Move these tabs around. This shouldn't affects the first 2 sheets.
2596 m_pDoc->MoveTab(2, 4);
2597 m_pDoc->MoveTab(3, 2);
2598
2599 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2600
2601 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2602
2603 // Delete the temp sheets.
2604 m_pDoc->DeleteTab(4);
2605 m_pDoc->DeleteTab(3);
2606 m_pDoc->DeleteTab(2);
2607
2608 // Delete Sheet1.
2609 m_pDoc->DeleteTab(0);
2610 m_pDoc->GetName(0, aName);
2611 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
2612
2613 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(#REF!.B2:C3)", "Wrong formula in Sheet2.B2.");
2614
2615 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2616
2617 m_pDoc->DeleteTab(0);
2618 }
2619
testFormulaRefUpdateInsertRows()2620 void TestFormula::testFormulaRefUpdateInsertRows()
2621 {
2622 setExpandRefs(false);
2623
2624 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2625 m_pDoc->InsertTab(0, "Formula");
2626
2627 // Insert raw values in B2:B4.
2628 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
2629 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
2630 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
2631
2632 // Insert a formula in B5 to sum up B2:B4.
2633 m_pDoc->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
2634
2635 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2636
2637 // Insert rows over rows 1:2.
2638 ScMarkData aMark(m_pDoc->GetSheetLimits());
2639 aMark.SelectOneTable(0);
2640 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2641 rFunc.InsertCells(ScRange(0,0,0,m_pDoc->MaxCol(),1,0), &aMark, INS_INSROWS_BEFORE, false, true);
2642
2643 // The raw data should have shifted to B4:B6.
2644 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2645 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,4,0)));
2646 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,5,0)));
2647
2648 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,6,0), "SUM(B4:B6)", "Wrong formula!");
2649
2650 // Clear and start over.
2651 clearSheet(m_pDoc, 0);
2652
2653 // Set raw values in A4:A6.
2654 m_pDoc->SetValue(ScAddress(0,3,0), 1.0);
2655 m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
2656 m_pDoc->SetValue(ScAddress(0,5,0), 3.0);
2657
2658 // Set formula in A3 to reference A4:A6.
2659 m_pDoc->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
2660
2661 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
2662
2663 // Insert 3 rows over 2:4. This should push A3:A6 to A6:A9.
2664 rFunc.InsertCells(ScRange(0,1,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
2665 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
2666 CPPUNIT_ASSERT(pFC);
2667 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula cell should not be an error.", 0, static_cast<int>(pFC->GetErrCode()));
2668 ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2669
2670 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "MAX(A7:A9)", "Wrong formula!");
2671
2672 m_pDoc->DeleteTab(0);
2673 }
2674
testFormulaRefUpdateSheetsDelete()2675 void TestFormula::testFormulaRefUpdateSheetsDelete()
2676 {
2677 m_pDoc->InsertTab(0, "Sheet1");
2678 m_pDoc->InsertTab(1, "Sheet2");
2679 m_pDoc->InsertTab(2, "Sheet3");
2680 m_pDoc->InsertTab(3, "Sheet4");
2681
2682 m_pDoc->SetString(ScAddress(4,1,0), "=SUM(Sheet2.A4:Sheet4.A4)");
2683 m_pDoc->SetString(ScAddress(4,2,0), "=SUM($Sheet2.A4:$Sheet4.A4)");
2684 m_pDoc->DeleteTab(1);
2685
2686 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,1,0), "SUM(Sheet3.A4:Sheet4.A4)", "Wrong Formula");
2687 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,2,0), "SUM($Sheet3.A4:$Sheet4.A4)", "Wrong Formula");
2688
2689 m_pDoc->InsertTab(1, "Sheet2");
2690
2691 m_pDoc->SetString(ScAddress(5,1,3), "=SUM(Sheet1.A5:Sheet3.A5)");
2692 m_pDoc->SetString(ScAddress(5,2,3), "=SUM($Sheet1.A5:$Sheet3.A5)");
2693 m_pDoc->DeleteTab(2);
2694
2695 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(5,1,2), "SUM(Sheet1.A5:Sheet2.A5)", "Wrong Formula");
2696 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(5,2,2), "SUM($Sheet1.A5:$Sheet2.A5)", "Wrong Formula");
2697
2698 m_pDoc->InsertTab(2, "Sheet3");
2699
2700 m_pDoc->SetString(ScAddress(6,1,3), "=SUM(Sheet1.A6:Sheet3.A6)");
2701 m_pDoc->SetString(ScAddress(6,2,3), "=SUM($Sheet1.A6:$Sheet3.A6)");
2702 m_pDoc->DeleteTabs(0,3);
2703
2704 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(6,1,0), "SUM(#REF!.A6:#REF!.A6)", "Wrong Formula");
2705 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(6,2,0), "SUM($#REF!.A6:$#REF!.A6)", "Wrong Formula");
2706
2707 m_pDoc->InsertTab(0, "Sheet1");
2708 m_pDoc->InsertTab(1, "Sheet2");
2709 m_pDoc->InsertTab(2, "Sheet3");
2710
2711 m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.A2:Sheet3.A2");
2712 m_pDoc->SetString(ScAddress(2,1,1), "=SUM(Sheet1.A1:Sheet2.A1");
2713 m_pDoc->SetString(ScAddress(3,1,1), "=SUM(Sheet2.A3:Sheet4.A3");
2714
2715 m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.A2:$Sheet3.A2");
2716 m_pDoc->SetString(ScAddress(2,2,1), "=SUM($Sheet1.A1:$Sheet2.A1");
2717 m_pDoc->SetString(ScAddress(3,2,1), "=SUM($Sheet2.A3:$Sheet4.A3");
2718
2719 m_pDoc->DeleteTab(2);
2720
2721 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.A2:Sheet2.A2)", "Wrong Formula");
2722
2723 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,1), "SUM(Sheet1.A1:Sheet2.A1)", "Wrong Formula");
2724
2725 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,1), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2726
2727 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.A2:$Sheet2.A2)", "Wrong Formula");
2728
2729 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,1), "SUM($Sheet1.A1:$Sheet2.A1)", "Wrong Formula");
2730
2731 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,1), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2732
2733 m_pDoc->DeleteTab(0);
2734
2735 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet2.A2:Sheet2.A2)", "Wrong Formula");
2736
2737 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "SUM(Sheet2.A1:Sheet2.A1)", "Wrong Formula");
2738
2739 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2740
2741 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet2.A2:$Sheet2.A2)", "Wrong Formula");
2742
2743 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,2,0), "SUM($Sheet2.A1:$Sheet2.A1)", "Wrong Formula");
2744
2745 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,2,0), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2746
2747 m_pDoc->DeleteTab(0);
2748 m_pDoc->DeleteTab(0);
2749 }
2750
testFormulaRefUpdateInsertColumns()2751 void TestFormula::testFormulaRefUpdateInsertColumns()
2752 {
2753 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2754 setExpandRefs(false);
2755
2756 m_pDoc->InsertTab(0, "Formula");
2757
2758 // Set named range for B2 with absolute column and relative same row.
2759 const ScAddress aNamePos(0,1,0);
2760 bool bInserted = m_pDoc->InsertNewRangeName("RowRelativeRange", aNamePos, "$Formula.$B2");
2761 CPPUNIT_ASSERT(bInserted);
2762
2763 // Set named range for entire absolute column B.
2764 bInserted = m_pDoc->InsertNewRangeName("EntireColumn", aNamePos, "$B:$B");
2765 CPPUNIT_ASSERT(bInserted);
2766
2767 // Set named range for entire absolute row 2.
2768 bInserted = m_pDoc->InsertNewRangeName("EntireRow", aNamePos, "$2:$2");
2769 CPPUNIT_ASSERT(bInserted);
2770
2771 // Set values in B1:B3.
2772 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
2773 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
2774 m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
2775
2776 // Reference them in B4.
2777 m_pDoc->SetString(ScAddress(1,3,0), "=SUM(B1:B3)");
2778 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
2779
2780 // Use named range in C2 to reference B2.
2781 m_pDoc->SetString(ScAddress(2,1,0), "=RowRelativeRange");
2782 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
2783
2784 // Use named range in C3 to reference column B, values in B1,B2,B3,B4
2785 m_pDoc->SetString(ScAddress(2,2,0), "=SUM(EntireColumn)");
2786 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2,2,0)));
2787
2788 // Use named range in C4 to reference row 2, values in B2 and C2.
2789 m_pDoc->SetString(ScAddress(2,3,0), "=SUM(EntireRow)");
2790 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,3,0)));
2791
2792 // Insert columns over A:B.
2793 ScMarkData aMark(m_pDoc->GetSheetLimits());
2794 aMark.SelectOneTable(0);
2795 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2796 rFunc.InsertCells(ScRange(0,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
2797
2798 // Now, the original column B has moved to column D.
2799 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,3,0), "SUM(D1:D3)", "Wrong formula in D4 after column insertion.");
2800
2801 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
2802
2803 // Check that the named reference points to the moved cell, now D2.
2804 ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("ROWRELATIVERANGE");
2805 CPPUNIT_ASSERT(pName);
2806 OUString aSymbol;
2807 pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2808 CPPUNIT_ASSERT_EQUAL(OUString("$Formula.$D2"), aSymbol);
2809
2810 // Check that the formula using the name, now in E2, still has the same result.
2811 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,1,0), "RowRelativeRange", "Wrong formula in E2 after column insertion.");
2812
2813 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,1,0)));
2814
2815 // Check that the named column reference points to the moved column, now D.
2816 pName = m_pDoc->GetRangeName()->findByUpperName("ENTIRECOLUMN");
2817 CPPUNIT_ASSERT(pName);
2818 pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2819 CPPUNIT_ASSERT_EQUAL(OUString("$D:$D"), aSymbol);
2820
2821 // Check that the formula using the name, now in E3, still has the same result.
2822 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,2,0), "SUM(EntireColumn)", "Wrong formula in E3 after column insertion.");
2823
2824 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(4,2,0)));
2825
2826 // Check that the named row reference still points to the same entire row
2827 // and does not have a #REF! error due to inserted columns.
2828 pName = m_pDoc->GetRangeName()->findByUpperName("ENTIREROW");
2829 CPPUNIT_ASSERT(pName);
2830 pName->GetSymbol(aSymbol, aNamePos, formula::FormulaGrammar::GRAM_ENGLISH);
2831 CPPUNIT_ASSERT_EQUAL(OUString("$2:$2"), aSymbol);
2832
2833 // Check that the formula using the name, now in E4, still has the same result.
2834 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(4,3,0), "SUM(EntireRow)", "Wrong formula in E4 after column insertion.");
2835
2836 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(4,3,0)));
2837
2838 m_pDoc->DeleteTab(0);
2839 }
2840
testFormulaRefUpdateMove()2841 void TestFormula::testFormulaRefUpdateMove()
2842 {
2843 m_pDoc->InsertTab(0, "Sheet1");
2844
2845 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2846
2847 // Set value to B4:B6.
2848 m_pDoc->SetValue(ScAddress(1,3,0), 1);
2849 m_pDoc->SetValue(ScAddress(1,4,0), 2);
2850 m_pDoc->SetValue(ScAddress(1,5,0), 3);
2851
2852 // Set formulas to A9:A12 that references B4:B6.
2853 m_pDoc->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
2854 m_pDoc->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
2855 m_pDoc->SetString(ScAddress(0,10,0), "=B5");
2856 m_pDoc->SetString(ScAddress(0,11,0), "=$B$6");
2857
2858 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2859 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2860 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2861 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2862
2863 // Move B4:B6 to D4 (two columns to the right).
2864 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2865 bool bMoved = rFunc.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
2866 CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved);
2867
2868 // The results of the formula cells that reference the moved range should remain the same.
2869 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
2870 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
2871 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
2872 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
2873
2874 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "SUM(D4:D6)", "Wrong formula.");
2875 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM($D$4:$D$6)", "Wrong formula.");
2876 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "D5", "Wrong formula.");
2877 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "$D$6", "Wrong formula.");
2878
2879 // Move A9:A12 to B10:B13.
2880 bMoved = rFunc.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
2881 CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved);
2882
2883 // The results of these formula cells should still stay the same.
2884 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,9,0));
2885 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,10,0));
2886 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2887 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2888
2889 // Displayed formulas should stay the same since the referenced range hasn't moved.
2890 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2891 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2892 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,11,0), "D5", "Wrong formula.");
2893 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2894
2895 // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
2896 // where it is.
2897 bMoved = rFunc.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
2898 CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved);
2899
2900 // Only the values of B10 and B11 should be updated.
2901 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,9,0));
2902 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,10,0));
2903 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
2904 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
2905
2906 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2907 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2908 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,11,0), "E5", "Wrong formula.");
2909 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2910
2911 m_pDoc->DeleteTab(0);
2912 }
2913
testFormulaRefUpdateMoveUndo()2914 void TestFormula::testFormulaRefUpdateMoveUndo()
2915 {
2916 m_pDoc->InsertTab(0, "Test");
2917
2918 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
2919
2920 // Set values in A1:A4.
2921 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
2922 m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
2923 m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
2924 m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
2925
2926 // Set formulas with single cell references in A6:A8.
2927 m_pDoc->SetString(ScAddress(0,5,0), "=A1");
2928 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2929 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "A1", "Wrong formula.");
2930
2931 m_pDoc->SetString(ScAddress(0,6,0), "=A1+A2+A3");
2932 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2933 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2934
2935 m_pDoc->SetString(ScAddress(0,7,0), "=A1+A3+A4");
2936 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2937 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2938
2939 // Set formulas with range references in A10:A12.
2940 m_pDoc->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
2941 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2942 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2943
2944 m_pDoc->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
2945 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2946 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2947
2948 m_pDoc->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
2949 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2950 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2951
2952 // Move A1:A3 to C1:C3. Note that A4 remains.
2953 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
2954 bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
2955 CPPUNIT_ASSERT(bMoved);
2956
2957 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2958 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "C1", "Wrong formula.");
2959
2960 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2961 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "C1+C2+C3", "Wrong formula.");
2962
2963 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2964 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "C1+C3+A4", "Wrong formula.");
2965
2966 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2967 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(C1:C2)", "Wrong formula.");
2968
2969 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2970 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(C1:C3)", "Wrong formula.");
2971
2972 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2973 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2974
2975 // Undo the move.
2976 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
2977 CPPUNIT_ASSERT(pUndoMgr);
2978 pUndoMgr->Undo();
2979
2980 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
2981 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "A1", "Wrong formula.");
2982
2983 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
2984 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2985
2986 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
2987 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2988
2989 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
2990 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2991
2992 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
2993 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2994
2995 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
2996 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)","Wrong formula." );
2997
2998 // Make sure the broadcasters are still valid by changing the value of A1.
2999 m_pDoc->SetValue(ScAddress(0,0,0), 20);
3000
3001 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(0,5,0)));
3002 CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3003 CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc->GetValue(ScAddress(0,7,0)));
3004
3005 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3006 CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,10,0)));
3007 CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc->GetValue(ScAddress(0,11,0)));
3008
3009 m_pDoc->DeleteTab(0);
3010 }
3011
testFormulaRefUpdateMoveUndo2()3012 void TestFormula::testFormulaRefUpdateMoveUndo2()
3013 {
3014 m_pDoc->InsertTab(0, "Test");
3015
3016 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3017
3018 std::vector<std::vector<const char*>> aData = {
3019 { "1", "2", "=A2*10", "=SUM(A1:B1)" },
3020 { "3", "4", "=SUM(A2:B2)", "=SUM(A2:B2)" },
3021 { "=SUM(A1:B1)" },
3022 };
3023
3024 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3025
3026 std::vector<std::vector<const char*>> aCheckInitial = {
3027 { "1", "2", "30", "3" },
3028 { "3", "4", "7", "7" },
3029 { "3", nullptr, nullptr, nullptr },
3030 };
3031
3032 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3033 CPPUNIT_ASSERT(bGood);
3034
3035 // D1:D2 should be grouped.
3036 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
3037 CPPUNIT_ASSERT(pFC);
3038 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3039
3040 // Drag A1:B1 into A2:B2 thereby overwriting the old A2:B2 content.
3041 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3042 bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,1,0,0), ScAddress(0,1,0), true, true, false, true);
3043 CPPUNIT_ASSERT(bMoved);
3044
3045 std::vector<std::vector<const char*>> aCheckAfter = {
3046 { nullptr, nullptr, "10", "3" },
3047 { "1", "2", "3", "3" },
3048 { "3", nullptr, nullptr, nullptr },
3049 };
3050
3051 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:B1 moved to A2:B2");
3052 CPPUNIT_ASSERT(bGood);
3053
3054 // Undo the move.
3055 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3056 CPPUNIT_ASSERT(pUndoMgr);
3057 pUndoMgr->Undo();
3058
3059 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3060 CPPUNIT_ASSERT(bGood);
3061
3062 // D1:D2 should be grouped.
3063 pFC = m_pDoc->GetFormulaCell(ScAddress(3,0,0));
3064 CPPUNIT_ASSERT(pFC);
3065 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3066
3067 // Redo and check.
3068 pUndoMgr->Redo();
3069
3070 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3071 CPPUNIT_ASSERT(bGood);
3072
3073 m_pDoc->DeleteTab(0);
3074 }
3075
testFormulaRefUpdateMoveUndo3NonShared()3076 void TestFormula::testFormulaRefUpdateMoveUndo3NonShared()
3077 {
3078 m_pDoc->InsertTab(0, "Test");
3079
3080 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3081
3082 std::vector<std::vector<const char*>> aData = {
3083 { "10", nullptr, nullptr },
3084 { "=A1", nullptr, nullptr },
3085 { "=A2+A1", nullptr, nullptr },
3086 };
3087
3088 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3089
3090 std::vector<std::vector<const char*>> aCheckInitial = {
3091 { "10", nullptr, nullptr },
3092 { "10", nullptr, nullptr },
3093 { "20", nullptr, nullptr },
3094 };
3095
3096 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3097 CPPUNIT_ASSERT(bGood);
3098
3099 // Drag A2:A3 into C2:C3.
3100 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3101 bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,2,0), ScAddress(2,1,0), true, true, false, true);
3102 CPPUNIT_ASSERT(bMoved);
3103
3104 std::vector<std::vector<const char*>> aCheckAfter = {
3105 { "10", nullptr, nullptr},
3106 { nullptr, nullptr, "10" },
3107 { nullptr, nullptr, "20" },
3108 };
3109
3110 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A3 moved to C2:C3");
3111 CPPUNIT_ASSERT(bGood);
3112
3113 // Undo the move.
3114 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3115 CPPUNIT_ASSERT(pUndoMgr);
3116 pUndoMgr->Undo();
3117
3118 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3119 CPPUNIT_ASSERT(bGood);
3120
3121 // Redo and check.
3122 pUndoMgr->Redo();
3123
3124 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3125 CPPUNIT_ASSERT(bGood);
3126
3127 m_pDoc->DeleteTab(0);
3128 }
3129
testFormulaRefUpdateMoveUndo3Shared()3130 void TestFormula::testFormulaRefUpdateMoveUndo3Shared()
3131 {
3132 m_pDoc->InsertTab(0, "Test");
3133
3134 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3135
3136 std::vector<std::vector<const char*>> aData = {
3137 { "10", nullptr, nullptr },
3138 { "=A1", nullptr, nullptr },
3139 { "=A2+$A$1", nullptr, nullptr },
3140 { "=A3+$A$1", nullptr, nullptr },
3141 };
3142
3143 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3144
3145 std::vector<std::vector<const char*>> aCheckInitial = {
3146 { "10", nullptr, nullptr },
3147 { "10", nullptr, nullptr },
3148 { "20", nullptr, nullptr },
3149 { "30", nullptr, nullptr },
3150 };
3151
3152 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3153 CPPUNIT_ASSERT(bGood);
3154
3155 // A3:A4 should be grouped.
3156 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
3157 CPPUNIT_ASSERT(pFC);
3158 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3159
3160 // Drag A2:A4 into C2:C4.
3161 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3162 bool bMoved = rFunc.MoveBlock(ScRange(0,1,0,0,3,0), ScAddress(2,1,0), true, true, false, true);
3163 CPPUNIT_ASSERT(bMoved);
3164
3165 std::vector<std::vector<const char*>> aCheckAfter = {
3166 { "10", nullptr, nullptr},
3167 { nullptr, nullptr, "10" },
3168 { nullptr, nullptr, "20" },
3169 { nullptr, nullptr, "30" },
3170 };
3171
3172 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A2:A4 moved to C2:C4");
3173 CPPUNIT_ASSERT(bGood);
3174
3175 // C3:C4 should be grouped.
3176 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
3177 CPPUNIT_ASSERT(pFC);
3178 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3179
3180 // Undo the move.
3181 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3182 CPPUNIT_ASSERT(pUndoMgr);
3183 pUndoMgr->Undo();
3184
3185 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3186 CPPUNIT_ASSERT(bGood);
3187
3188 // A3:A4 should be grouped.
3189 pFC = m_pDoc->GetFormulaCell(ScAddress(0,2,0));
3190 CPPUNIT_ASSERT(pFC);
3191 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC->GetSharedLength());
3192
3193 // Redo and check.
3194 pUndoMgr->Redo();
3195
3196 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3197 CPPUNIT_ASSERT(bGood);
3198
3199 m_pDoc->DeleteTab(0);
3200 }
3201
testFormulaRefUpdateMoveUndoDependents()3202 void TestFormula::testFormulaRefUpdateMoveUndoDependents()
3203 {
3204 m_pDoc->InsertTab(0, "Test");
3205
3206 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3207 std::vector<std::vector<const char*>> aData = {
3208 { "1" },
3209 { "22" },
3210 { "3" },
3211 { "4" },
3212 { "5" },
3213 { "=SUM(C1:C5)" },
3214 { "=C6" },
3215 };
3216
3217 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData);
3218
3219 std::vector<std::vector<const char*>> aCheckInitial = {
3220 { "1" },
3221 { "22" },
3222 { "3" },
3223 { "4" },
3224 { "5" },
3225 { "35" },
3226 { "35" },
3227 };
3228
3229 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3230 CPPUNIT_ASSERT(bGood);
3231
3232 // Drag C2 into D2.
3233 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3234 bool bMoved = rFunc.MoveBlock(ScRange(2, 1, 0, 2, 1, 0), ScAddress(3, 1, 0), true, true, false, true);
3235 CPPUNIT_ASSERT(bMoved);
3236
3237 std::vector<std::vector<const char*>> aCheckAfter = {
3238 { "1" },
3239 { nullptr },
3240 { "3" },
3241 { "4" },
3242 { "5" },
3243 { "13" },
3244 { "13" },
3245 };
3246
3247 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "C2 moved to D2");
3248 CPPUNIT_ASSERT(bGood);
3249
3250 // Undo the move.
3251 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3252 CPPUNIT_ASSERT(pUndoMgr);
3253 pUndoMgr->Undo();
3254
3255 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3256 CPPUNIT_ASSERT(bGood);
3257
3258 // Redo and check.
3259 pUndoMgr->Redo();
3260
3261 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3262 CPPUNIT_ASSERT(bGood);
3263
3264 m_pDoc->DeleteTab(0);
3265 }
3266
testFormulaRefUpdateMoveUndo4()3267 void TestFormula::testFormulaRefUpdateMoveUndo4()
3268 {
3269 m_pDoc->InsertTab(0, "Test");
3270
3271 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3272 std::vector<std::vector<const char*>> aData = {
3273 { "1", nullptr, "=B1", "=A1" },
3274 { "2", nullptr, "=B2", "=A2" },
3275 };
3276
3277 ScRange aOutRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
3278
3279 std::vector<std::vector<const char*>> aCheckInitial = {
3280 { "1", nullptr, "0", "1" },
3281 { "2", nullptr, "0", "2" },
3282 };
3283
3284 bool bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "initial data");
3285 CPPUNIT_ASSERT(bGood);
3286
3287 // Drag A1:A2 into B1:B2.
3288 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3289 bool bMoved = rFunc.MoveBlock(ScRange(0, 0, 0, 0, 1, 0), ScAddress(1, 0, 0), true, true, false, true);
3290 CPPUNIT_ASSERT(bMoved);
3291
3292 std::vector<std::vector<const char*>> aCheckAfter = {
3293 { nullptr, "1", "1", "1" },
3294 { nullptr, "2", "2", "2" },
3295 };
3296
3297 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "A1:A2 moved to B1:B2");
3298 CPPUNIT_ASSERT(bGood);
3299
3300 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
3301 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
3302 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
3303 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
3304
3305 // Undo the move.
3306 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3307 CPPUNIT_ASSERT(pUndoMgr);
3308 pUndoMgr->Undo();
3309
3310 bGood = checkOutput(m_pDoc, aOutRange, aCheckInitial, "after undo");
3311 CPPUNIT_ASSERT(bGood);
3312
3313 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
3314 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
3315 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "A1", "Wrong formula"); // D1
3316 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "A2", "Wrong formula"); // D2
3317
3318 // Redo and check.
3319 pUndoMgr->Redo();
3320
3321 bGood = checkOutput(m_pDoc, aOutRange, aCheckAfter, "after redo");
3322 CPPUNIT_ASSERT(bGood);
3323
3324 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
3325 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
3326 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
3327 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
3328
3329 m_pDoc->DeleteTab(0);
3330 }
3331
testFormulaRefUpdateMoveToSheet()3332 void TestFormula::testFormulaRefUpdateMoveToSheet()
3333 {
3334 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3335
3336 m_pDoc->InsertTab(0, "Sheet1");
3337 m_pDoc->InsertTab(1, "Sheet2");
3338
3339 // Set values to A1:A2 on Sheet1, and B1:B2 to reference them.
3340 m_pDoc->SetValue(ScAddress(0,0,0), 11);
3341 m_pDoc->SetValue(ScAddress(0,1,0), 12);
3342 m_pDoc->SetString(ScAddress(1,0,0), "=A1");
3343 m_pDoc->SetString(ScAddress(1,1,0), "=A2");
3344
3345 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A1", "Wrong formula");
3346 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A2", "Wrong formula");
3347
3348 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
3349 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(1,1,0)));
3350
3351 // Move A1:A2 on Sheet1 to B3:B4 on Sheet2.
3352 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3353 bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(1,2,1), true, true, false, true);
3354 CPPUNIT_ASSERT(bMoved);
3355
3356 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
3357 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
3358
3359 // Undo and check again.
3360 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3361 pUndoMgr->Undo();
3362
3363 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A1", "Wrong formula");
3364 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A2", "Wrong formula");
3365
3366 // Redo and check.
3367 pUndoMgr->Redo();
3368
3369 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
3370 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
3371
3372 m_pDoc->DeleteTab(1);
3373 m_pDoc->DeleteTab(0);
3374 }
3375
testFormulaRefUpdateDeleteContent()3376 void TestFormula::testFormulaRefUpdateDeleteContent()
3377 {
3378 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3379
3380 m_pDoc->InsertTab(0, "Test");
3381
3382 // Set value in B2.
3383 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3384 // Set formula in C2 to reference B2.
3385 m_pDoc->SetString(ScAddress(2,1,0), "=B2");
3386
3387 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3388
3389 // Delete B2.
3390 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3391 ScMarkData aMark(m_pDoc->GetSheetLimits());
3392 aMark.SetMarkArea(ScAddress(1,1,0));
3393 rFunc.DeleteContents(aMark, InsertDeleteFlags::CONTENTS, true, true);
3394
3395 CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3396 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3397
3398 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3399 CPPUNIT_ASSERT(pUndoMgr);
3400
3401 // Undo and check the result of C2.
3402 pUndoMgr->Undo();
3403 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3404 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3405
3406 // Redo and check.
3407 pUndoMgr->Redo();
3408 CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(1,1,0)));
3409 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
3410
3411 m_pDoc->DeleteTab(0);
3412 }
3413
testFormulaRefUpdateDeleteAndShiftLeft()3414 void TestFormula::testFormulaRefUpdateDeleteAndShiftLeft()
3415 {
3416 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3417
3418 m_pDoc->InsertTab(0, "Test");
3419
3420 // Insert 1,2,3,4,5 in C1:G1.
3421 for (SCCOL i = 0; i <= 4; ++i)
3422 m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3423
3424 // Insert formula in H1.
3425 ScAddress aPos(7,0,0);
3426 m_pDoc->SetString(aPos, "=SUM(C1:G1)");
3427
3428 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3429
3430 // Delete columns D:E (middle of the reference).
3431 ScMarkData aMark(m_pDoc->GetSheetLimits());
3432 aMark.SelectOneTable(0);
3433 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3434 bool bDeleted = rFunc.DeleteCells(ScRange(3,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3435 CPPUNIT_ASSERT(bDeleted);
3436
3437 aPos.IncCol(-2);
3438 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3439 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3440
3441 // Undo and check.
3442 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3443 CPPUNIT_ASSERT(pUndo);
3444
3445 pUndo->Undo();
3446 aPos.IncCol(2);
3447 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3448 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3449
3450 // Delete columns C:D (left end of the reference).
3451 bDeleted = rFunc.DeleteCells(ScRange(2,0,0,3,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3452 CPPUNIT_ASSERT(bDeleted);
3453
3454 aPos.IncCol(-2);
3455 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3456 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3457
3458 // Undo and check again.
3459 pUndo->Undo();
3460 aPos.IncCol(2);
3461 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3462 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3463
3464 // Delete columns B:E (overlaps on the left).
3465 bDeleted = rFunc.DeleteCells(ScRange(1,0,0,4,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3466 CPPUNIT_ASSERT(bDeleted);
3467
3468 aPos.IncCol(-4);
3469 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3470 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:C1)", "Wrong formula!");
3471
3472 // Undo and check again.
3473 pUndo->Undo();
3474 aPos.IncCol(4);
3475 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3476 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:G1)", "Wrong formula!");
3477
3478 // Start over with a new scenario.
3479 clearSheet(m_pDoc, 0);
3480
3481 // Insert 1,2,3,4,5,6 into C1:H1.
3482 for (SCCOL i = 0; i <= 5; ++i)
3483 m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
3484
3485 // Set formula in B1.
3486 aPos = ScAddress(1,0,0);
3487 m_pDoc->SetString(aPos, "=SUM(C1:H1)");
3488 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3489
3490 // Delete columns F:H (right end of the reference).
3491 bDeleted = rFunc.DeleteCells(ScRange(5,0,0,7,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3492 CPPUNIT_ASSERT(bDeleted);
3493
3494 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3495 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:E1)", "Wrong formula!");
3496
3497 // Undo and check.
3498 pUndo->Undo();
3499 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3500 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:H1)", "Wrong formula!");
3501
3502 // Delete columns G:I (overlaps on the right).
3503 bDeleted = rFunc.DeleteCells(ScRange(6,0,0,8,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3504 CPPUNIT_ASSERT(bDeleted);
3505
3506 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3507 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:F1)", "Wrong formula!");
3508
3509 // Undo and check again.
3510 pUndo->Undo();
3511 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3512 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(C1:H1)", "Wrong formula!");
3513
3514 m_pDoc->DeleteTab(0);
3515 }
3516
testFormulaRefUpdateDeleteAndShiftLeft2()3517 void TestFormula::testFormulaRefUpdateDeleteAndShiftLeft2()
3518 {
3519 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3520
3521 m_pDoc->InsertTab(0, "Test");
3522
3523 std::vector<std::vector<const char*>> aData = {
3524 { "1", "=COUNT($A$1:$A$4)", "=COUNT(A1)" },
3525 { "2", "=COUNT($A$1:$A$4)", "=COUNT(A2)" },
3526 { "3", "=COUNT($A$1:$A$4)", "=COUNT(A3)" },
3527 { "4", "=COUNT($A$1:$A$4)", "=COUNT(A4)" },
3528 };
3529
3530 insertRangeData(m_pDoc, ScAddress(), aData);
3531
3532 auto funcCheckOriginal = [&]()
3533 {
3534 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0))); // A1
3535 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0))); // A2
3536 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0))); // A3
3537 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,3,0))); // A4
3538
3539 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,0,0))); // B1
3540 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
3541 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0))); // B3
3542 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0))); // B4
3543
3544 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,0,0))); // C1
3545 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
3546 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,2,0))); // C3
3547 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,3,0))); // C4
3548 };
3549
3550 auto funcCheckDeleted = [&]()
3551 {
3552 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,0,0))); // A1
3553 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,1,0))); // A2
3554 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,2,0))); // A3
3555 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(0,3,0))); // A4
3556
3557 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,0,0))); // B1
3558 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,1,0))); // B2
3559 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,2,0))); // B3
3560 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,3,0))); // B4
3561 };
3562
3563 funcCheckOriginal();
3564
3565 // Delete Column A.
3566 ScMarkData aMark(m_pDoc->GetSheetLimits());
3567 aMark.SelectOneTable(0);
3568 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3569 bool bDeleted = rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
3570 CPPUNIT_ASSERT(bDeleted);
3571
3572 funcCheckDeleted();
3573
3574 // Undo and check.
3575 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3576 CPPUNIT_ASSERT(pUndo);
3577
3578 pUndo->Undo();
3579 funcCheckOriginal();
3580
3581 // Redo and check.
3582 pUndo->Redo();
3583 funcCheckDeleted();
3584
3585 m_pDoc->DeleteTab(0);
3586 }
3587
testFormulaRefUpdateDeleteAndShiftUp()3588 void TestFormula::testFormulaRefUpdateDeleteAndShiftUp()
3589 {
3590 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3591
3592 m_pDoc->InsertTab(0, "Test");
3593
3594 // Insert 1,2,3,4,5 in A3:A7.
3595 for (SCROW i = 0; i <= 4; ++i)
3596 m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3597
3598 // Insert formula in A8.
3599 ScAddress aPos(0,7,0);
3600 m_pDoc->SetString(aPos, "=SUM(A3:A7)");
3601
3602 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3603
3604 // Delete rows 4:5 (middle of the reference).
3605 ScMarkData aMark(m_pDoc->GetSheetLimits());
3606 aMark.SelectOneTable(0);
3607 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3608 bool bDeleted = rFunc.DeleteCells(ScRange(0,3,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3609 CPPUNIT_ASSERT(bDeleted);
3610
3611 aPos.IncRow(-2);
3612 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3613 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3614
3615 // Undo and check.
3616 SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
3617 CPPUNIT_ASSERT(pUndo);
3618
3619 pUndo->Undo();
3620 aPos.IncRow(2);
3621 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3622 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3623
3624 // Delete rows 3:4 (top end of the reference).
3625 bDeleted = rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),3,0), &aMark, DelCellCmd::CellsUp, true);
3626 CPPUNIT_ASSERT(bDeleted);
3627
3628 aPos.IncRow(-2);
3629 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
3630 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3631
3632 // Undo and check again.
3633 pUndo->Undo();
3634 aPos.IncRow(2);
3635 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3636 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3637
3638 // Delete rows 2:5 (overlaps on the top).
3639 bDeleted = rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),4,0), &aMark, DelCellCmd::CellsUp, true);
3640 CPPUNIT_ASSERT(bDeleted);
3641
3642 aPos.IncRow(-4);
3643 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
3644 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A2:A3)", "Wrong formula!");
3645
3646 // Undo and check again.
3647 pUndo->Undo();
3648 aPos.IncRow(4);
3649 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
3650 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A7)", "Wrong formula!");
3651
3652 // Start over with a new scenario.
3653 clearSheet(m_pDoc, 0);
3654
3655 // Insert 1,2,3,4,5,6 into A3:A8.
3656 for (SCROW i = 0; i <= 5; ++i)
3657 m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
3658
3659 // Set formula in B1.
3660 aPos = ScAddress(0,1,0);
3661 m_pDoc->SetString(aPos, "=SUM(A3:A8)");
3662 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3663
3664 // Delete rows 6:8 (bottom end of the reference).
3665 bDeleted = rFunc.DeleteCells(ScRange(0,5,0,m_pDoc->MaxCol(),7,0), &aMark, DelCellCmd::CellsUp, true);
3666 CPPUNIT_ASSERT(bDeleted);
3667
3668 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
3669 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A5)", "Wrong formula!");
3670
3671 // Undo and check.
3672 pUndo->Undo();
3673 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3674 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A8)", "Wrong formula!");
3675
3676 // Delete rows 7:9 (overlaps on the bottom).
3677 bDeleted = rFunc.DeleteCells(ScRange(0,6,0,m_pDoc->MaxCol(),8,0), &aMark, DelCellCmd::CellsUp, true);
3678 CPPUNIT_ASSERT(bDeleted);
3679
3680 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
3681 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A6)", "Wrong formula!");
3682
3683 // Undo and check again.
3684 pUndo->Undo();
3685 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
3686 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A3:A8)", "Wrong formula!");
3687
3688 m_pDoc->DeleteTab(0);
3689 }
3690
testFormulaRefUpdateName()3691 void TestFormula::testFormulaRefUpdateName()
3692 {
3693 m_pDoc->InsertTab(0, "Formula");
3694
3695 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3696
3697 // Fill C2:C5 with values.
3698 m_pDoc->SetValue(ScAddress(2,1,0), 1);
3699 m_pDoc->SetValue(ScAddress(2,2,0), 2);
3700 m_pDoc->SetValue(ScAddress(2,3,0), 3);
3701 m_pDoc->SetValue(ScAddress(2,4,0), 4);
3702
3703 // Add a named expression that references the immediate left cell.
3704 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
3705 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
3706 ScRangeData* pName = new ScRangeData(
3707 *m_pDoc, "ToLeft", "RC[-1]", ScAddress(2,1,0),
3708 ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
3709
3710 bool bInserted = pGlobalNames->insert(pName);
3711 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3712
3713 // Insert formulas in D2:D5 using the named expression.
3714 m_pDoc->SetString(ScAddress(3,1,0), "=ToLeft");
3715 m_pDoc->SetString(ScAddress(3,2,0), "=ToLeft");
3716 m_pDoc->SetString(ScAddress(3,3,0), "=ToLeft");
3717 m_pDoc->SetString(ScAddress(3,4,0), "=ToLeft");
3718
3719 // Make sure the results are correct.
3720 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3721 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3722 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3723 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3724
3725 // Push cells in column C down by one cell.
3726 m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
3727
3728 // Make sure the results change accordingly.
3729 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(3,1,0));
3730 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,2,0));
3731 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,3,0));
3732 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,4,0));
3733
3734 // Move cells back.
3735 m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
3736
3737 // Make sure the results are back as well.
3738 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
3739 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
3740 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
3741 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
3742
3743 // Fill B10:B12 with values.
3744 m_pDoc->SetValue(ScAddress(1,9,0), 10);
3745 m_pDoc->SetValue(ScAddress(1,10,0), 11);
3746 m_pDoc->SetValue(ScAddress(1,11,0), 12);
3747
3748 // Insert a new named expression that references these values as absolute range.
3749 pName = new ScRangeData(
3750 *m_pDoc, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3751 bInserted = pGlobalNames->insert(pName);
3752 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3753
3754 // Set formula at C8 that references this named expression.
3755 m_pDoc->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
3756 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3757
3758 // Shift B10:B12 to right by 2 columns.
3759 m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
3760
3761 // This should shift the absolute range B10:B12 that MyRange references.
3762 pName = pGlobalNames->findByUpperName("MYRANGE");
3763 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3764 OUString aExpr;
3765 pName->GetSymbol(aExpr);
3766 CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr);
3767
3768 // This move shouldn't affect the value of C8.
3769 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,7,0));
3770 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
3771 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3772
3773 // Update the value of D10 and make sure C8 gets updated.
3774 m_pDoc->SetValue(ScAddress(3,9,0), 20);
3775 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3776
3777 // Insert a new sheet before the current.
3778 m_pDoc->InsertTab(0, "New");
3779 OUString aName;
3780 m_pDoc->GetName(1, aName);
3781 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3782
3783 pName = pGlobalNames->findByUpperName("MYRANGE");
3784 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3785
3786 m_pDoc->SetValue(ScAddress(3,9,1), 10);
3787 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
3788
3789 // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
3790 m_pDoc->DeleteTab(0);
3791
3792 aName.clear();
3793 m_pDoc->GetName(0, aName);
3794 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
3795
3796 pName = pGlobalNames->findByUpperName("MYRANGE");
3797 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
3798
3799 m_pDoc->SetValue(ScAddress(3,9,0), 11);
3800 CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
3801
3802 // Clear all and start over.
3803 clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
3804 pGlobalNames->clear();
3805
3806 pName = new ScRangeData(
3807 *m_pDoc, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
3808 bInserted = pGlobalNames->insert(pName);
3809 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
3810 pName->GetSymbol(aExpr);
3811 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3812
3813 // Insert range of cells to shift right. The range partially overlaps the named range.
3814 m_pDoc->InsertCol(ScRange(2,4,0,3,8,0));
3815
3816 // This should not alter the range.
3817 pName->GetSymbol(aExpr);
3818 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
3819
3820 m_pDoc->DeleteTab(0);
3821 }
3822
testFormulaRefUpdateNameMove()3823 void TestFormula::testFormulaRefUpdateNameMove()
3824 {
3825 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3826
3827 m_pDoc->InsertTab(0, "Test");
3828
3829 // Set values to B2:B4.
3830 m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
3831 m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
3832 m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
3833
3834 // Set named range for B2:B4.
3835 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
3836 CPPUNIT_ASSERT(bInserted);
3837
3838 // Set formula in A10.
3839 m_pDoc->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
3840 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3841
3842 ScRangeData* pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3843 CPPUNIT_ASSERT(pData);
3844 OUString aSymbol;
3845 pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3846 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3847
3848 // Move B2:B4 to D3.
3849 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3850 bool bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3851 CPPUNIT_ASSERT(bMoved);
3852
3853 // The named range should have moved as well.
3854 pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3855 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3856
3857 // The value of A10 should remain unchanged.
3858 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3859
3860 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
3861 CPPUNIT_ASSERT(pUndoMgr);
3862
3863 // Undo and check.
3864 pUndoMgr->Undo();
3865
3866 pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3867 CPPUNIT_ASSERT(pData);
3868 pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3869 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
3870 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3871
3872 // Redo and check.
3873 pUndoMgr->Redo();
3874
3875 pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3876 CPPUNIT_ASSERT(pData);
3877 pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3878 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
3879 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3880
3881 // Undo again to bring it back to the initial condition, and clear the undo buffer.
3882 pUndoMgr->Undo();
3883 pUndoMgr->Clear();
3884
3885 // Add an identical formula to A11 and make a formula group over A10:A11.
3886 m_pDoc->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
3887 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,0));
3888 CPPUNIT_ASSERT(pFC);
3889 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
3890 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
3891
3892 // Move B2:B4 to D3 again.
3893 bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3894 CPPUNIT_ASSERT(bMoved);
3895
3896 // Values of A10 and A11 should remain the same.
3897 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
3898 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
3899
3900 // Clear and start over.
3901 clearSheet(m_pDoc, 0);
3902 m_pDoc->GetRangeName()->clear();
3903
3904 // Set value to B2.
3905 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
3906
3907 // Define B2 as 'MyCell'.
3908 bInserted = m_pDoc->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
3909 CPPUNIT_ASSERT(bInserted);
3910
3911 // Set formula to B3 that references B2 via MyCell.
3912 m_pDoc->SetString(ScAddress(1,2,0), "=MyCell*2");
3913 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3914
3915 // Move B2 to D2.
3916 bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,1,0), ScAddress(3,1,0), true, true, false, true);
3917 CPPUNIT_ASSERT(bMoved);
3918
3919 // Value in B3 should remain unchanged.
3920 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
3921
3922 m_pDoc->DeleteTab(0);
3923 }
3924
testFormulaRefUpdateNameExpandRef()3925 void TestFormula::testFormulaRefUpdateNameExpandRef()
3926 {
3927 setExpandRefs(true);
3928
3929 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
3930
3931 m_pDoc->InsertTab(0, "Test");
3932
3933 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
3934 CPPUNIT_ASSERT(bInserted);
3935
3936 // Set values to A1:A3.
3937 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
3938 m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
3939 m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
3940
3941 m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
3942 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
3943
3944 // Insert a new row at row 4, which should expand the named range to A1:A4.
3945 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
3946 ScMarkData aMark(m_pDoc->GetSheetLimits());
3947 aMark.SelectOneTable(0);
3948 rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
3949 ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3950 CPPUNIT_ASSERT(pName);
3951 OUString aSymbol;
3952 pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3953 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3954
3955 // Make sure the listening area has been expanded as well. Note the
3956 // formula cell has been pushed downward by one cell.
3957 m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
3958 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3959
3960 // Insert a new column at column 2, which should not expand the named
3961 // range as it is only one column wide.
3962 rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
3963 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3964 CPPUNIT_ASSERT(pName);
3965 pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3966 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
3967
3968 // Make sure the referenced area has not changed.
3969 m_pDoc->SetValue(ScAddress(0,3,0), 2.0);
3970 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3971 m_pDoc->SetValue(ScAddress(1,3,0), 2.0);
3972 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,6,0)));
3973
3974 // Clear the document and start over.
3975 m_pDoc->GetRangeName()->clear();
3976 clearSheet(m_pDoc, 0);
3977
3978 // Set values to B4:B6.
3979 m_pDoc->SetValue(ScAddress(1,3,0), 1.0);
3980 m_pDoc->SetValue(ScAddress(1,4,0), 2.0);
3981 m_pDoc->SetValue(ScAddress(1,5,0), 3.0);
3982
3983 bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
3984 CPPUNIT_ASSERT(bInserted);
3985
3986 // Set formula to A1.
3987 m_pDoc->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
3988 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,0,0));
3989
3990 // Insert rows over 3:5 which should expand the range by 3 rows.
3991 rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),4,0), &aMark, INS_INSROWS_BEFORE, false, true);
3992
3993 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
3994 CPPUNIT_ASSERT(pName);
3995
3996 pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
3997 CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
3998
3999 // Clear the document and start over.
4000 m_pDoc->GetRangeName()->clear();
4001 clearSheet(m_pDoc, 0);
4002
4003 // Set values to A1:A3.
4004 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4005 m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
4006 m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
4007
4008 // Name A1:A3 'MyData'.
4009 bInserted = m_pDoc->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
4010 CPPUNIT_ASSERT(bInserted);
4011
4012 // Set formulas to C1:C2 and E1.
4013 m_pDoc->SetString(ScAddress(2,0,0), "=SUM(MyData)");
4014 m_pDoc->SetString(ScAddress(2,1,0), "=SUM(MyData)");
4015 m_pDoc->SetString(ScAddress(4,0,0), "=SUM(MyData)");
4016
4017 // C1:C2 should be shared.
4018 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
4019 CPPUNIT_ASSERT(pFC);
4020 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
4021 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
4022
4023 // E1 should not be shared.
4024 pFC = m_pDoc->GetFormulaCell(ScAddress(4,0,0));
4025 CPPUNIT_ASSERT(pFC);
4026 CPPUNIT_ASSERT(!pFC->IsShared());
4027
4028 // Check the results.
4029 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4030 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4031 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(4,0,0)));
4032
4033 // Insert a new row at row 3. This should expand MyData to A1:A4.
4034 rFunc.InsertCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, INS_INSROWS_BEFORE, false, true);
4035
4036 // Set new value to A3.
4037 m_pDoc->SetValue(ScAddress(0,2,0), 4.0);
4038
4039 // Check the results again.
4040 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4041 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4042 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(4,0,0)));
4043
4044 m_pDoc->DeleteTab(0);
4045 }
4046
testFormulaRefUpdateNameExpandRef2()4047 void TestFormula::testFormulaRefUpdateNameExpandRef2()
4048 {
4049 setExpandRefs(true);
4050
4051 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4052
4053 m_pDoc->InsertTab(0, "Test");
4054
4055 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$B$3");
4056 CPPUNIT_ASSERT(bInserted);
4057
4058 // Insert a new row at row 4, which should expand the named range to A1:A4.
4059 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4060 ScMarkData aMark(m_pDoc->GetSheetLimits());
4061 aMark.SelectOneTable(0);
4062
4063 // Insert a new column at column 3, which should expand the named
4064 rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
4065 ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4066 CPPUNIT_ASSERT(pName);
4067 OUString aSymbol;
4068 pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
4069 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$C$3"), aSymbol);
4070
4071 m_pDoc->DeleteTab(0);
4072 }
4073
testFormulaRefUpdateNameDeleteRow()4074 void TestFormula::testFormulaRefUpdateNameDeleteRow()
4075 {
4076 m_pDoc->InsertTab(0, "Test");
4077
4078 // Insert a new name 'MyRange' to reference B2:B4.
4079 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$2:$B$4");
4080 CPPUNIT_ASSERT(bInserted);
4081
4082 const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4083 CPPUNIT_ASSERT(pName);
4084
4085 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4086 const ScTokenArray* pCode = pName->GetCode();
4087 OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4088 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4089
4090 // Insert a new name 'MyAddress' to reference $B$3. Note absolute row.
4091 bInserted = m_pDoc->InsertNewRangeName("MyAddress", ScAddress(0,0,0), "$B$3");
4092 CPPUNIT_ASSERT(bInserted);
4093
4094 const ScRangeData* pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
4095 CPPUNIT_ASSERT(pName2);
4096
4097 sc::TokenStringContext aCxt2(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4098 const ScTokenArray* pCode2 = pName2->GetCode();
4099 OUString aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4100 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
4101
4102 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4103
4104 // Delete row 3.
4105 ScMarkData aMark(m_pDoc->GetSheetLimits());
4106 aMark.SelectOneTable(0);
4107 rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4108
4109 // The reference in the 'MyRange' name should get updated to B2:B3.
4110 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4111 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
4112
4113 // The reference in the 'MyAddress' name should get updated to $B$#REF!.
4114 aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4115 CPPUNIT_ASSERT_EQUAL(OUString("$B$#REF!"), aExpr2);
4116
4117 // Delete row 3 again.
4118 rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4119 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4120 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
4121
4122 // Undo and check.
4123 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4124 CPPUNIT_ASSERT(pUndoMgr);
4125
4126 pUndoMgr->Undo();
4127
4128 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4129 CPPUNIT_ASSERT(pName);
4130 pCode = pName->GetCode();
4131
4132 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4133 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
4134
4135 // Undo again and check.
4136 pUndoMgr->Undo();
4137
4138 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4139 CPPUNIT_ASSERT(pName);
4140 pCode = pName->GetCode();
4141
4142 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4143 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4144
4145 // Delete row 2-3.
4146 rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),2,0), &aMark, DelCellCmd::CellsUp, true);
4147
4148 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4149 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
4150
4151 // Undo and check.
4152 pUndoMgr->Undo();
4153
4154 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4155 CPPUNIT_ASSERT(pName);
4156 pCode = pName->GetCode();
4157
4158 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4159 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4160
4161 pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
4162 CPPUNIT_ASSERT(pName2);
4163 pCode2 = pName2->GetCode();
4164
4165 aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4166 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
4167
4168 m_pDoc->InsertTab(1, "test2");
4169
4170 ScMarkData aMark2(m_pDoc->GetSheetLimits());
4171 aMark2.SelectOneTable(1);
4172 rFunc.DeleteCells(ScRange(0,2,1,m_pDoc->MaxCol(),2,1), &aMark2, DelCellCmd::CellsUp, true);
4173
4174 pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4175 CPPUNIT_ASSERT(pName);
4176 pCode = pName->GetCode();
4177
4178 aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4179 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
4180
4181 pName2 = m_pDoc->GetRangeName()->findByUpperName("MYADDRESS");
4182 CPPUNIT_ASSERT(pName2);
4183 pCode2 = pName2->GetCode();
4184
4185 // Deleting a range the 'MyAddress' name points into due to its implicit
4186 // relative sheet reference to the sheet where used does not invalidate
4187 // the named expression because when updating the sheet reference is
4188 // relative to its base position on sheet 0 (same for the 'MyRange' range,
4189 // which is the reason why it is not updated either).
4190 // This is a tad confusing...
4191 aExpr2 = pCode2->CreateString(aCxt2, ScAddress(0,0,0));
4192 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2);
4193
4194 m_pDoc->DeleteTab(1);
4195 m_pDoc->DeleteTab(0);
4196 }
4197
testFormulaRefUpdateNameCopySheet()4198 void TestFormula::testFormulaRefUpdateNameCopySheet()
4199 {
4200 m_pDoc->InsertTab(0, "Test");
4201 m_pDoc->InsertTab(1, "Test2");
4202
4203 bool bInserted = m_pDoc->InsertNewRangeName("RED", ScAddress(0,0,0), "$Test.$B$2");
4204 CPPUNIT_ASSERT(bInserted);
4205 bInserted = m_pDoc->InsertNewRangeName("BLUE", ScAddress(0,0,0), "$Test.$B$3");
4206 CPPUNIT_ASSERT(bInserted);
4207 m_pDoc->SetValue(1, 1, 0, 1);
4208 m_pDoc->SetValue(1, 2, 0, 2);
4209
4210 // insert formula into Test2 that is =RED+BLUE
4211 m_pDoc->SetString(ScAddress(2,2,1), "=RED+BLUE");
4212
4213 double nVal = m_pDoc->GetValue(2, 2, 1);
4214 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
4215 m_pDoc->CopyTab(1, 0);
4216
4217 nVal = m_pDoc->GetValue(2, 2, 2);
4218 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
4219
4220 nVal = m_pDoc->GetValue(2, 2, 0);
4221 CPPUNIT_ASSERT_EQUAL(3.0, nVal);
4222
4223 m_pDoc->SetValue(1, 1, 1, 3);
4224
4225 nVal = m_pDoc->GetValue(2, 2, 2);
4226 CPPUNIT_ASSERT_EQUAL(5.0, nVal);
4227
4228 nVal = m_pDoc->GetValue(2, 2, 0);
4229 CPPUNIT_ASSERT_EQUAL(5.0, nVal);
4230
4231 m_pDoc->DeleteTab(2);
4232 m_pDoc->DeleteTab(1);
4233 m_pDoc->DeleteTab(0);
4234
4235 m_pDoc->InsertTab(0, "Test1");
4236 // Global name referencing sheet Test1.
4237 bInserted = m_pDoc->InsertNewRangeName("sheetnumber", ScAddress(0,0,0), "$Test1.$A$1");
4238 CPPUNIT_ASSERT(bInserted);
4239 m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
4240 m_pDoc->SetString(ScAddress(1,0,0), "=sheetnumber");
4241 nVal = m_pDoc->GetValue(1,0,0);
4242 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet number should be 1", 1.0, nVal);
4243
4244 // Copy sheet after.
4245 m_pDoc->CopyTab(0, 1);
4246 nVal = m_pDoc->GetValue(1,0,1);
4247 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 2", 2.0, nVal);
4248 nVal = m_pDoc->GetValue(1,0,0);
4249 CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 1", 1.0, nVal);
4250 const ScRangeData* pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
4251 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
4252
4253 // Copy sheet before, shifting following now two sheets.
4254 m_pDoc->CopyTab(0, 0);
4255 nVal = m_pDoc->GetValue(1,0,0);
4256 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
4257 pName = m_pDoc->GetRangeName(0)->findByUpperName("SHEETNUMBER");
4258 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
4259 nVal = m_pDoc->GetValue(1,0,1);
4260 CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 2", 2.0, nVal);
4261 pName = m_pDoc->GetRangeName(1)->findByUpperName("SHEETNUMBER");
4262 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should not exist", !pName);
4263 nVal = m_pDoc->GetValue(1,0,2);
4264 CPPUNIT_ASSERT_EQUAL_MESSAGE("Old sheet number should be 3", 3.0, nVal);
4265 pName = m_pDoc->GetRangeName(2)->findByUpperName("SHEETNUMBER");
4266 CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should exist", pName);
4267
4268 m_pDoc->DeleteTab(2);
4269 m_pDoc->DeleteTab(1);
4270 m_pDoc->DeleteTab(0);
4271
4272 m_pDoc->InsertTab(0, "Test2");
4273 // Local name referencing sheet Test2.
4274 bInserted = m_pDoc->GetRangeName(0)->insert( new ScRangeData( *m_pDoc, "localname", "$Test2.$A$1"));
4275 CPPUNIT_ASSERT(bInserted);
4276 m_pDoc->SetString(ScAddress(0,0,0), "=SHEET()");
4277 m_pDoc->SetString(ScAddress(1,0,0), "=localname");
4278 nVal = m_pDoc->GetValue(1,0,0);
4279 CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 1", 1.0, nVal);
4280
4281 // Insert sheet before and shift sheet with local name.
4282 m_pDoc->InsertTab(0, "Test1");
4283 pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
4284 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
4285 nVal = m_pDoc->GetValue(1,0,1);
4286 CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 2", 2.0, nVal);
4287
4288 // Copy sheet before, shifting following now two sheets.
4289 m_pDoc->CopyTab(1, 0);
4290 pName = m_pDoc->GetRangeName(0)->findByUpperName("LOCALNAME");
4291 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName);
4292 nVal = m_pDoc->GetValue(1,0,0);
4293 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal);
4294 pName = m_pDoc->GetRangeName(1)->findByUpperName("LOCALNAME");
4295 CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should not exist", !pName);
4296 pName = m_pDoc->GetRangeName(2)->findByUpperName("LOCALNAME");
4297 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName);
4298 nVal = m_pDoc->GetValue(1,0,2);
4299 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 3", 3.0, nVal);
4300
4301 m_pDoc->DeleteTab(2);
4302 m_pDoc->DeleteTab(1);
4303 m_pDoc->DeleteTab(0);
4304 m_pDoc->SetRangeName(nullptr);
4305
4306 // Test nested names during copying sheet.
4307
4308 m_pDoc->InsertTab(0, "Test2");
4309 ScAddress aPos(0,0,0);
4310 bInserted = m_pDoc->InsertNewRangeName( "global", aPos, "$Test2.$A$1");
4311 CPPUNIT_ASSERT(bInserted);
4312 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local", aPos, "$Test2.$A$2");
4313 CPPUNIT_ASSERT(bInserted);
4314 bInserted = m_pDoc->InsertNewRangeName( "global_global", aPos, "global*100");
4315 CPPUNIT_ASSERT(bInserted);
4316 bInserted = m_pDoc->InsertNewRangeName( "global_local", aPos, "local*1000");
4317 CPPUNIT_ASSERT(bInserted);
4318 bInserted = m_pDoc->InsertNewRangeName( "global_unused", aPos, "$Test2.$A$1");
4319 CPPUNIT_ASSERT(bInserted);
4320 bInserted = m_pDoc->InsertNewRangeName( "global_unused_noref", aPos, "42");
4321 CPPUNIT_ASSERT(bInserted);
4322 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_global", aPos, "global*10000");
4323 CPPUNIT_ASSERT(bInserted);
4324 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_local", aPos, "local*100000");
4325 CPPUNIT_ASSERT(bInserted);
4326 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused", aPos, "$Test2.$A$2");
4327 CPPUNIT_ASSERT(bInserted);
4328 bInserted = m_pDoc->InsertNewRangeName( aPos.Tab(), "local_unused_noref", aPos, "23");
4329 CPPUNIT_ASSERT(bInserted);
4330
4331 m_pDoc->SetString(aPos, "=SHEET()");
4332 aPos.IncRow();
4333 m_pDoc->SetString(aPos, "=A1*10+SHEET()");
4334 aPos.IncRow();
4335 m_pDoc->SetString(aPos, "=global_global");
4336 aPos.IncRow();
4337 m_pDoc->SetString(aPos, "=global_local");
4338 aPos.IncRow();
4339 m_pDoc->SetString(aPos, "=local_global");
4340 aPos.IncRow();
4341 m_pDoc->SetString(aPos, "=local_local");
4342
4343 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, false);
4344
4345 // Copy sheet after.
4346 m_pDoc->CopyTab(0, 1);
4347 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, false);
4348 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 1, true);
4349
4350 // Copy sheet before, shifting following now two sheets.
4351 m_pDoc->CopyTab(1, 0);
4352 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 0, true);
4353 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 1, false);
4354 testFormulaRefUpdateNameCopySheetCheckTab( m_pDoc, 2, true);
4355
4356 m_pDoc->DeleteTab(2);
4357 m_pDoc->DeleteTab(1);
4358 m_pDoc->DeleteTab(0);
4359 }
4360
testFormulaRefUpdateSheetLocalMove()4361 void TestFormula::testFormulaRefUpdateSheetLocalMove()
4362 {
4363 SCTAB nSheet1 = 0;
4364 SCTAB nSheet2 = 1;
4365 m_pDoc->InsertTab( nSheet1, "Sheet1");
4366 m_pDoc->InsertTab( nSheet2, "Sheet2");
4367
4368 ScAddress aPos(0,0,nSheet1);
4369 bool bOk;
4370 bOk = m_pDoc->InsertNewRangeName( 0, "MyCell", aPos, "$Sheet1.$B$2");
4371 CPPUNIT_ASSERT(bOk);
4372 aPos.SetTab(nSheet2);
4373 bOk = m_pDoc->InsertNewRangeName( 1, "MyCell", aPos, "$Sheet2.$B$2");
4374 CPPUNIT_ASSERT(bOk);
4375
4376 aPos.SetTab(nSheet1);
4377 aPos.IncCol();
4378 m_pDoc->SetString( aPos, "x");
4379 aPos.IncRow();
4380 m_pDoc->SetString( aPos, "1.0");
4381 aPos.IncRow();
4382 m_pDoc->SetString( aPos, "=MyCell");
4383 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet1.B3", 1.0, m_pDoc->GetValue(aPos));
4384
4385 aPos.SetTab(nSheet2);
4386 aPos.SetRow(1);
4387 m_pDoc->SetString( aPos, "2.0");
4388 aPos.IncRow();
4389 m_pDoc->SetString( aPos, "=MyCell");
4390 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet2.B3", 2.0, m_pDoc->GetValue(aPos));
4391
4392 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4393 OUString aFormula;
4394
4395 // Move Sheet1.B1 ("x") to Sheet2.B1
4396 bOk = rFunc.MoveBlock( ScRange(1,0,nSheet1,1,0,nSheet1), ScAddress(1,0,nSheet2), true, false, false, false);
4397 CPPUNIT_ASSERT(bOk);
4398 // Results not changed.
4399 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4400 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet2.B3", 2.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4401 // Formulas not changed.
4402 m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4403 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet1.B3", OUString("=MyCell"), aFormula);
4404 m_pDoc->GetFormula( 1,2,nSheet2, aFormula);
4405 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move x: Sheet2.B3", OUString("=MyCell"), aFormula);
4406
4407 // Move Sheet2.B2 ("2.0") to Sheet1.C2
4408 bOk = rFunc.MoveBlock( ScRange(1,1,nSheet2,1,1,nSheet2), ScAddress(2,1,nSheet1), true, false, false, false);
4409 CPPUNIT_ASSERT(bOk);
4410 // Results not changed.
4411 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4412 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2.B3", 2.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4413 // Formulas not changed.
4414 m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4415 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet1.B3", OUString("=MyCell"), aFormula);
4416 m_pDoc->GetFormula( 1,2,nSheet2, aFormula);
4417 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2.B3", OUString("=MyCell"), aFormula);
4418
4419 ScRangeData* pName;
4420
4421 // Check that the sheet-local named reference points to the moved cell, now
4422 // Sheet1.C2
4423 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4424 CPPUNIT_ASSERT(pName);
4425 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4426 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move 2.0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4427
4428 // Move Sheet2.B3 ("=MyCell") to Sheet1.C3
4429 bOk = rFunc.MoveBlock( ScRange(1,2,nSheet2,1,2,nSheet2), ScAddress(2,2,nSheet1), true, false, false, false);
4430 CPPUNIT_ASSERT(bOk);
4431 // Results changed.
4432 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4433 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet2.B3", 0.0, m_pDoc->GetValue(ScAddress(1,2,nSheet2)));
4434 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4435 // One formula identical, one adjusted.
4436 m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4437 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.B3", OUString("=MyCell"), aFormula);
4438 m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4439 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4440
4441 // Check that the sheet-local named reference in Sheet1 still points to the
4442 // original cell Sheet1.B2
4443 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4444 CPPUNIT_ASSERT(pName);
4445 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4446 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4447
4448 // Check that the sheet-local named reference in Sheet2 still points to the
4449 // moved cell, now Sheet1.C2
4450 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4451 CPPUNIT_ASSERT(pName);
4452 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4453 CPPUNIT_ASSERT_EQUAL_MESSAGE("Move =MyCell: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4454
4455 // Insert sheet before the others.
4456 m_pDoc->InsertTab(0, "Sheet0");
4457 ++nSheet1;
4458 ++nSheet2;
4459
4460 // Nothing changed.
4461 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4462 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4463 m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4464 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.B3", OUString("=MyCell"), aFormula);
4465 m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4466 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4467 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4468 CPPUNIT_ASSERT(pName);
4469 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4470 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4471 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4472 CPPUNIT_ASSERT(pName);
4473 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4474 CPPUNIT_ASSERT_EQUAL_MESSAGE("Insert Sheet0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4475
4476 // Delete sheet before the others.
4477 m_pDoc->DeleteTab(0);
4478 --nSheet1;
4479 --nSheet2;
4480
4481 // Nothing changed.
4482 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4483 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.C3", 2.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4484 m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4485 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.B3", OUString("=MyCell"), aFormula);
4486 m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4487 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1.C3", OUString("=Sheet2.MyCell"), aFormula);
4488 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4489 CPPUNIT_ASSERT(pName);
4490 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4491 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4492 pName = m_pDoc->GetRangeName(nSheet2)->findByUpperName("MYCELL");
4493 CPPUNIT_ASSERT(pName);
4494 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4495 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet0: Sheet2 sheet-local name", OUString("$Sheet1.$C$2"), aFormula);
4496
4497 // Delete last sheet with sheet-local name.
4498 m_pDoc->DeleteTab(nSheet2);
4499
4500 // XXX we *could* analyze whether the expression points to a different
4501 // sheet and then move the name to a remaining sheet. If so, adapt this
4502 // test.
4503 // Nothing changed except the sheet-local name and its use.
4504 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.B3", 1.0, m_pDoc->GetValue(ScAddress(1,2,nSheet1)));
4505 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", 0.0, m_pDoc->GetValue(ScAddress(2,2,nSheet1)));
4506 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", OUString("#NAME?"), m_pDoc->GetString(ScAddress(2,2,nSheet1)));
4507 m_pDoc->GetFormula( 1,2,nSheet1, aFormula);
4508 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.B3", OUString("=MyCell"), aFormula);
4509 m_pDoc->GetFormula( 2,2,nSheet1, aFormula);
4510 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1.C3", OUString("=#NAME?"), aFormula);
4511 pName = m_pDoc->GetRangeName(nSheet1)->findByUpperName("MYCELL");
4512 CPPUNIT_ASSERT(pName);
4513 pName->GetSymbol( aFormula, ScAddress(), formula::FormulaGrammar::GRAM_ENGLISH);
4514 CPPUNIT_ASSERT_EQUAL_MESSAGE("Delete Sheet2: Sheet1 sheet-local name", OUString("$Sheet1.$B$2"), aFormula);
4515 CPPUNIT_ASSERT(!m_pDoc->GetRangeName(nSheet2));
4516 nSheet2 = -1;
4517
4518 m_pDoc->DeleteTab(0);
4519 }
4520
testFormulaRefUpdateNameDelete()4521 void TestFormula::testFormulaRefUpdateNameDelete()
4522 {
4523 m_pDoc->InsertTab(0, "Test");
4524
4525 // Insert a new name 'MyRange' to reference B1
4526 bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$1");
4527 CPPUNIT_ASSERT(bInserted);
4528
4529 const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
4530 CPPUNIT_ASSERT(pName);
4531
4532 m_pDoc->DeleteCol(1, 0, 3, 0, 0, 1);
4533 const ScTokenArray* pCode = pName->GetCode();
4534 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
4535 OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
4536 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$1"), aExpr);
4537
4538 m_pDoc->DeleteTab(0);
4539 }
4540
testFormulaRefUpdateValidity()4541 void TestFormula::testFormulaRefUpdateValidity()
4542 {
4543 struct {
4544
4545 bool checkList( std::vector<ScTypedStrData>& rList )
4546 {
4547 double aExpected[] = { 1.0, 2.0, 3.0 }; // must be sorted.
4548 size_t nCheckSize = SAL_N_ELEMENTS(aExpected);
4549
4550 if (rList.size() != nCheckSize)
4551 {
4552 cerr << "List size is not what is expected." << endl;
4553 return false;
4554 }
4555
4556 std::sort(rList.begin(), rList.end(), ScTypedStrData::LessCaseSensitive());
4557
4558 for (size_t i = 0; i < nCheckSize; ++i)
4559 {
4560 if (aExpected[i] != rList[i].GetValue())
4561 {
4562 cerr << "Incorrect value at position " << i
4563 << ": expected=" << aExpected[i] << ", actual=" << rList[i].GetValue() << endl;
4564 return false;
4565 }
4566 }
4567
4568 return true;
4569 }
4570
4571 } aCheck;
4572
4573 setExpandRefs(false);
4574 setCalcAsShown(m_pDoc, true);
4575
4576 m_pDoc->InsertTab(0, "Formula");
4577
4578 // Set values in C2:C4.
4579 m_pDoc->SetValue(ScAddress(2,1,0), 1.0);
4580 m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
4581 m_pDoc->SetValue(ScAddress(2,3,0), 3.0);
4582
4583 // Set validity in A2.
4584 ScValidationData aData(
4585 SC_VALID_LIST, ScConditionMode::Equal, "C2:C4", "", *m_pDoc, ScAddress(0,1,0), "", "",
4586 m_pDoc->GetGrammar(), m_pDoc->GetGrammar());
4587
4588 sal_uLong nIndex = m_pDoc->AddValidationEntry(aData);
4589 SfxUInt32Item aItem(ATTR_VALIDDATA, nIndex);
4590
4591 ScPatternAttr aNewAttrs(
4592 std::make_unique<SfxItemSet>(*m_pDoc->GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{}));
4593 aNewAttrs.GetItemSet().Put(aItem);
4594
4595 m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
4596
4597 const ScValidationData* pData = m_pDoc->GetValidationEntry(nIndex);
4598 CPPUNIT_ASSERT(pData);
4599
4600 // Make sure the list is correct.
4601 std::vector<ScTypedStrData> aList;
4602 pData->FillSelectionList(aList, ScAddress(0,1,0));
4603 bool bGood = aCheck.checkList(aList);
4604 CPPUNIT_ASSERT_MESSAGE("Initial list is incorrect.", bGood);
4605
4606 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4607 ScMarkData aMark(m_pDoc->GetSheetLimits());
4608 aMark.SelectOneTable(0);
4609
4610 // Insert a new column at Column B, to move the list from C2:C4 to D2:D4.
4611 bool bInserted = rFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, true, true);
4612 CPPUNIT_ASSERT_MESSAGE("Column insertion failed.", bInserted);
4613 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4614 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4615 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,3,0)));
4616
4617 // Check the list values again.
4618 aList.clear();
4619 pData->FillSelectionList(aList, ScAddress(0,1,0));
4620 bGood = aCheck.checkList(aList);
4621 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after column insertion.", bGood);
4622
4623 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
4624 CPPUNIT_ASSERT(pUndoMgr);
4625
4626 // Undo and check the list content again. The list moves back to C2:C4 after the undo.
4627 pUndoMgr->Undo();
4628 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4629 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4630 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4631
4632 aList.clear();
4633 pData->FillSelectionList(aList, ScAddress(0,1,0));
4634 bGood = aCheck.checkList(aList);
4635 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of column insertion.", bGood);
4636
4637 // Move C2:C4 to E5:E7.
4638 bool bMoved = rFunc.MoveBlock(ScRange(2,1,0,2,3,0), ScAddress(4,4,0), false, true, false, true);
4639 CPPUNIT_ASSERT(bMoved);
4640 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4,4,0)));
4641 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,5,0)));
4642 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(4,6,0)));
4643
4644 // Check the list again after the move.
4645 aList.clear();
4646 pData->FillSelectionList(aList, ScAddress(0,1,0));
4647 bGood = aCheck.checkList(aList);
4648 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after moving C2:C4 to E5:E7.", bGood);
4649
4650 // Undo the move and check. The list should be back to C2:C4.
4651 pUndoMgr->Undo();
4652 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
4653 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4654 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
4655
4656 aList.clear();
4657 pData->FillSelectionList(aList, ScAddress(0,1,0));
4658 bGood = aCheck.checkList(aList);
4659 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of the move.", bGood);
4660
4661 m_pDoc->DeleteTab(0);
4662 }
4663
testTokenArrayRefUpdateMove()4664 void TestFormula::testTokenArrayRefUpdateMove()
4665 {
4666 m_pDoc->InsertTab(0, "Sheet1");
4667 m_pDoc->InsertTab(1, "Sheet2");
4668
4669 ScAddress aPos(0,0,0); // A1
4670
4671 sc::TokenStringContext aCxt(*m_pDoc, m_pDoc->GetGrammar());
4672
4673 // Emulate cell movement from Sheet1.C3 to Sheet2.C3.
4674 sc::RefUpdateContext aRefCxt(*m_pDoc);
4675 aRefCxt.meMode = URM_MOVE;
4676 aRefCxt.maRange = ScAddress(2,2,1); // C3 on Sheet2.
4677 aRefCxt.mnTabDelta = -1;
4678
4679 std::vector<OUString> aTests = {
4680 "B1*C1",
4681 "SUM(B1:C1)",
4682 "$Sheet1.B1",
4683 "SUM(Sheet1.B1:Sheet2.B1)"
4684 };
4685
4686 // Since C3 is not referenced in any of the above formulas, moving C3 from
4687 // Sheet1 to Sheet2 should NOT change the displayed formula string at all.
4688
4689 for (const OUString& aTest : aTests)
4690 {
4691 ScCompiler aComp(*m_pDoc, aPos, m_pDoc->GetGrammar());
4692 std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aTest));
4693
4694 OUString aStr = pArray->CreateString(aCxt, aPos);
4695
4696 CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4697
4698 // This formula cell isn't moving its position. The displayed formula
4699 // string should not change.
4700 pArray->AdjustReferenceOnMove(aRefCxt, aPos, aPos);
4701
4702 aStr = pArray->CreateString(aCxt, aPos);
4703 CPPUNIT_ASSERT_EQUAL(aTest, aStr);
4704 }
4705
4706 m_pDoc->DeleteTab(1);
4707 m_pDoc->DeleteTab(0);
4708 }
4709
testMultipleOperations()4710 void TestFormula::testMultipleOperations()
4711 {
4712 m_pDoc->InsertTab(0, "MultiOp");
4713
4714 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4715
4716 // Insert the reference formula at top row.
4717 m_pDoc->SetValue(ScAddress(0,0,0), 1);
4718 m_pDoc->SetString(ScAddress(1,0,0), "=A1*10");
4719 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4720
4721 // Insert variable inputs in A3:A5.
4722 m_pDoc->SetValue(ScAddress(0,2,0), 2);
4723 m_pDoc->SetValue(ScAddress(0,3,0), 3);
4724 m_pDoc->SetValue(ScAddress(0,4,0), 4);
4725
4726 // Set multiple operations range.
4727 ScTabOpParam aParam;
4728 aParam.aRefFormulaCell = ScRefAddress(1,0,0);
4729 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4730 aParam.aRefColCell = ScRefAddress(0,0,0);
4731 ScMarkData aMark(m_pDoc->GetSheetLimits());
4732 aMark.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
4733 m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4734 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(1,2,0));
4735 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,3,0));
4736 CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,4,0));
4737
4738 // Clear A3:B5.
4739 clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
4740
4741 // This time, use indirect reference formula cell.
4742 m_pDoc->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
4743 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4744
4745 // Insert variable inputs in A3:A5.
4746 m_pDoc->SetValue(ScAddress(0,2,0), 3);
4747 m_pDoc->SetValue(ScAddress(0,3,0), 4);
4748 m_pDoc->SetValue(ScAddress(0,4,0), 5);
4749
4750 // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
4751 aParam.aRefFormulaCell.Set(2,0,0,false,false,false);
4752 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
4753 m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
4754 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,2,0));
4755 CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,3,0));
4756 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(1,4,0));
4757
4758 m_pDoc->DeleteTab(0);
4759 }
4760
testFuncCOLUMN()4761 void TestFormula::testFuncCOLUMN()
4762 {
4763 m_pDoc->InsertTab(0, "Formula");
4764 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4765
4766 m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
4767 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4768
4769 m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4770 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4771
4772 // Move the formula cell with COLUMN() function to change its value.
4773 m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4774 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(6,10,0)));
4775
4776 // The cell that references the moved cell should update its value as well.
4777 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4778
4779 // Move the column in the other direction.
4780 m_pDoc->DeleteCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
4781
4782 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4783
4784 // The cell that references the moved cell should update its value as well.
4785 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4786
4787 m_pDoc->DeleteTab(0);
4788 }
4789
testFuncCOUNT()4790 void TestFormula::testFuncCOUNT()
4791 {
4792 m_pDoc->InsertTab(0, "Formula");
4793 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4794
4795 m_pDoc->SetValue(ScAddress(0,0,0), 2); // A1
4796 m_pDoc->SetValue(ScAddress(0,1,0), 4); // A2
4797 m_pDoc->SetValue(ScAddress(0,2,0), 6); // A3
4798
4799 ScAddress aPos(1,0,0);
4800 m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
4801 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
4802
4803 aPos.IncRow();
4804 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
4805 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
4806
4807 aPos.IncRow();
4808 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
4809 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
4810
4811 aPos.IncRow();
4812 m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
4813 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
4814
4815 // Matrix in C1.
4816 ScMarkData aMark(m_pDoc->GetSheetLimits());
4817 aMark.SelectOneTable(0);
4818 m_pDoc->InsertMatrixFormula(2, 0, 2, 0, aMark, "=COUNT(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4819 // Check that the #VALUE! error of "a" not found in "b" is not counted.
4820 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
4821
4822 // Matrix in C3.
4823 m_pDoc->InsertMatrixFormula(2, 2, 2, 2, aMark, "=COUNTA(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4824 // Check that the #VALUE! error of "a" not found in "b" is counted.
4825 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,2,0)));
4826
4827 m_pDoc->DeleteTab(0);
4828 }
4829
testFuncCOUNTBLANK()4830 void TestFormula::testFuncCOUNTBLANK()
4831 {
4832 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4833 m_pDoc->InsertTab(0, "Formula");
4834
4835 std::vector<std::vector<const char*>> aData = {
4836 { "1", nullptr, "=B1", "=\"\"" },
4837 { "2", nullptr, "=B2", "=\"\"" },
4838 { "A", nullptr, "=B3", "=\"\"" },
4839 { "B", nullptr, "=B4", "=D3" },
4840 { nullptr, nullptr, "=B5", "=D4" },
4841 { "=COUNTBLANK(A1:A5)", "=COUNTBLANK(B1:B5)", "=COUNTBLANK(C1:C5)", "=COUNTBLANK(D1:D5)" }
4842 };
4843
4844 ScAddress aPos(0,0,0);
4845 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
4846 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4847
4848 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
4849 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,5,0)));
4850 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,5,0)));
4851 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(3,5,0)));
4852
4853 // Test single cell reference cases.
4854
4855 clearSheet(m_pDoc, 0);
4856
4857 std::vector<std::vector<const char*>> aData2 = {
4858 { "1", "=COUNTBLANK(A1)" },
4859 { "A", "=COUNTBLANK(A2)" },
4860 { nullptr, "=COUNTBLANK(A3)" },
4861 { "=\"\"", "=COUNTBLANK(A4)" },
4862 { "=A4" , "=COUNTBLANK(A5)" },
4863 };
4864
4865 aRange = insertRangeData(m_pDoc, aPos, aData2);
4866 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
4867
4868 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
4869 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4870 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4871 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
4872 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
4873
4874 m_pDoc->DeleteTab(0);
4875 }
4876
testFuncROW()4877 void TestFormula::testFuncROW()
4878 {
4879 m_pDoc->InsertTab(0, "Formula");
4880 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
4881
4882 m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
4883 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4884
4885 m_pDoc->SetString(ScAddress(0,1,0), "=F11");
4886 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4887
4888 // Insert 2 new rows at row 4.
4889 m_pDoc->InsertRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4890 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(5,12,0)));
4891
4892 // The cell that references the moved cell should update its value as well.
4893 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4894
4895 // Delete 2 rows to move it back.
4896 m_pDoc->DeleteRow(ScRange(0,3,0,m_pDoc->MaxCol(),4,0));
4897
4898 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
4899
4900 // The cell that references the moved cell should update its value as well.
4901 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4902
4903 // Clear sheet and start over.
4904 clearSheet(m_pDoc, 0);
4905
4906 m_pDoc->SetString(ScAddress(0,1,0), "=ROW(A5)");
4907 m_pDoc->SetString(ScAddress(1,1,0), "=ROW(B5)");
4908 m_pDoc->SetString(ScAddress(1,2,0), "=ROW(B6)");
4909 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4910 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4911 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4912
4913 // B2:B3 should be shared.
4914 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
4915 CPPUNIT_ASSERT(pFC);
4916 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
4917 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
4918
4919 // Insert a new row at row 4.
4920 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
4921 ScMarkData aMark(m_pDoc->GetSheetLimits());
4922 aMark.SelectOneTable(0);
4923 rFunc.InsertCells(ScRange(0,3,0,m_pDoc->MaxCol(),3,0), &aMark, INS_INSROWS_BEFORE, false, true);
4924 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "ROW(A6)", "Wrong formula!");
4925 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "ROW(B6)", "Wrong formula!");
4926 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "ROW(B7)", "Wrong formula!");
4927
4928 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
4929 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
4930 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,2,0)));
4931
4932 m_pDoc->DeleteTab(0);
4933 }
4934
testFuncSUM()4935 void TestFormula::testFuncSUM()
4936 {
4937 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4938 m_pDoc->InsertTab (0, "foo"));
4939
4940 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
4941
4942 // Single argument case.
4943 m_pDoc->SetValue(ScAddress(0,0,0), 1);
4944 m_pDoc->SetValue(ScAddress(0,1,0), 1);
4945 m_pDoc->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
4946 m_pDoc->CalcAll();
4947 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
4948
4949 // Multiple argument case.
4950 m_pDoc->SetValue(ScAddress(0,0,0), 1);
4951 m_pDoc->SetValue(ScAddress(0,1,0), 22);
4952 m_pDoc->SetValue(ScAddress(0,2,0), 4);
4953 m_pDoc->SetValue(ScAddress(0,3,0), 5);
4954 m_pDoc->SetValue(ScAddress(0,4,0), 6);
4955
4956 m_pDoc->SetValue(ScAddress(1,0,0), 3);
4957 m_pDoc->SetValue(ScAddress(1,1,0), 4);
4958 m_pDoc->SetValue(ScAddress(1,2,0), 5);
4959 m_pDoc->SetValue(ScAddress(1,3,0), 6);
4960 m_pDoc->SetValue(ScAddress(1,4,0), 7);
4961
4962 m_pDoc->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
4963 m_pDoc->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
4964 m_pDoc->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
4965 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(ScAddress(3,0,0)));
4966 CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc->GetValue(ScAddress(3,1,0)));
4967 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(3,2,0)));
4968
4969 // Clear and start over.
4970 clearRange(m_pDoc, ScRange(0,0,0,3,m_pDoc->MaxRow(),0));
4971
4972 // SUM needs to take the first error in case the range contains an error.
4973 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
4974 m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
4975 m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
4976 m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
4977 CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(ScAddress(0,3,0)));
4978
4979 // Set #DIV/0! error to A3. A4 should also inherit this error.
4980 m_pDoc->SetString(ScAddress(0,2,0), "=1/0");
4981 FormulaError nErr = m_pDoc->GetErrCode(ScAddress(0,2,0));
4982 CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell should have a division by zero error.",
4983 int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4984 nErr = m_pDoc->GetErrCode(ScAddress(0,3,0));
4985 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM should have also inherited a div-by-zero error.",
4986 int(FormulaError::DivisionByZero), static_cast<int>(nErr));
4987
4988 // Set #NA! to A2. A4 should now inherit this error.
4989 m_pDoc->SetString(ScAddress(0,1,0), "=NA()");
4990 nErr = m_pDoc->GetErrCode(ScAddress(0,1,0));
4991 CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr != FormulaError::NONE);
4992 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should have inherited the same error as A2.",
4993 static_cast<int>(nErr), static_cast<int>(m_pDoc->GetErrCode(ScAddress(0,3,0))));
4994
4995 m_pDoc->DeleteTab(0);
4996 }
4997
testFuncPRODUCT()4998 void TestFormula::testFuncPRODUCT()
4999 {
5000 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5001
5002 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab(0, "foo"));
5003
5004 ScAddress aPos(3, 0, 0);
5005 m_pDoc->SetValue(0, 0, 0, 3.0); // A1
5006 m_pDoc->SetString(aPos, "=PRODUCT(A1)");
5007 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 3.0, m_pDoc->GetValue(aPos));
5008 m_pDoc->SetValue(0, 0, 0, -3.0); // A1
5009 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -3.0, m_pDoc->GetValue(aPos));
5010 m_pDoc->SetString(aPos, "=PRODUCT(B1)");
5011 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos));
5012 m_pDoc->SetValue(1, 0, 0, 10.0); // B1
5013 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 10.0, m_pDoc->GetValue(aPos));
5014
5015 m_pDoc->SetString(aPos, "=PRODUCT(A1:C3)");
5016 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -30.0, m_pDoc->GetValue(aPos));
5017 m_pDoc->SetValue(1, 1, 0, -1.0); // B2
5018 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 30.0, m_pDoc->GetValue(aPos));
5019 m_pDoc->SetValue(2, 0, 0, 4.0); // C1
5020 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 120.0, m_pDoc->GetValue(aPos));
5021 m_pDoc->SetValue(0, 1, 0, -2.0); // A2
5022 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -240.0, m_pDoc->GetValue(aPos));
5023 m_pDoc->SetValue(2, 1, 0, 8.0); // C2
5024 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -1920.0, m_pDoc->GetValue(aPos));
5025 m_pDoc->SetValue(0, 2, 0, 0.2); // A3
5026 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -384.0, m_pDoc->GetValue(aPos), 10e-4);
5027 m_pDoc->SetValue(1, 2, 0, -0.25); // B3
5028 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 96.0, m_pDoc->GetValue(aPos), 10e-4);
5029 m_pDoc->SetValue(2, 2, 0, -0.125); // C3
5030 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -12.0, m_pDoc->GetValue(aPos), 10e-4);
5031 m_pDoc->SetValue(2, 2, 0, 0.0); // C3
5032 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc->GetValue(aPos), 10e-4);
5033
5034 m_pDoc->SetString(aPos, "=PRODUCT({2;3;4})");
5035 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 24.0, m_pDoc->GetValue(aPos));
5036 m_pDoc->SetString(aPos, "=PRODUCT({2;-2;2})");
5037 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -8.0, m_pDoc->GetValue(aPos));
5038 m_pDoc->SetString(aPos, "=PRODUCT({8;0.125;-1})");
5039 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -1.0, m_pDoc->GetValue(aPos));
5040
5041 m_pDoc->SetString(aPos, "=PRODUCT({2;3};{4;5})");
5042 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 120.0, m_pDoc->GetValue(aPos));
5043 m_pDoc->SetString(aPos, "=PRODUCT({10;-8};{3;-1};{15;30};{7})");
5044 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 756000.0, m_pDoc->GetValue(aPos));
5045 m_pDoc->SetString(aPos, "=PRODUCT({10;-0.1;8};{0.125;4;0.25;2};{0.5};{1};{-1})");
5046 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 1.0, m_pDoc->GetValue(aPos));
5047
5048 m_pDoc->DeleteTab(0);
5049 }
5050
testFuncSUMPRODUCT()5051 void TestFormula::testFuncSUMPRODUCT()
5052 {
5053 m_pDoc->InsertTab(0, "Test");
5054
5055 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5056
5057 ScAddress aPos(0,0,0);
5058 m_pDoc->SetString(aPos, "=SUMPRODUCT(B1:B3;C1:C3)");
5059 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
5060 m_pDoc->SetValue(ScAddress(2,0,0), 1.0); // C1
5061 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
5062 m_pDoc->SetValue(ScAddress(1,0,0), 1.0); // B1
5063 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
5064 m_pDoc->SetValue(ScAddress(1,1,0), 2.0); // B2
5065 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
5066 m_pDoc->SetValue(ScAddress(2,1,0), 3.0); // C2
5067 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(aPos));
5068 m_pDoc->SetValue(ScAddress(2,2,0), -2.0); // C3
5069 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(aPos));
5070 m_pDoc->SetValue(ScAddress(1,2,0), 5.0); // B3
5071 CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc->GetValue(aPos));
5072
5073 // Force an error in C2 and test ForcedArray matrix error propagation.
5074 m_pDoc->SetString( 2, 1, 0, "=1/0");
5075 FormulaError nError = m_pDoc->GetErrCode(aPos);
5076 CPPUNIT_ASSERT_MESSAGE("Formula result should be a propagated error", nError != FormulaError::NONE);
5077
5078 // Test ForceArray propagation of SUMPRODUCT parameters to ABS and + operator.
5079 // => ABS({-3,4})*({-3,4}+{-3,4}) => {3,4}*{-6,8} => {-18,32} => 14
5080 m_pDoc->SetValue(ScAddress(4,0,0), -3.0); // E1
5081 m_pDoc->SetValue(ScAddress(4,1,0), 4.0); // E2
5082 // Non-intersecting formula in F3.
5083 m_pDoc->SetString(ScAddress(5,2,0), "=SUMPRODUCT(ABS(E1:E2);E1:E2+E1:E2)");
5084 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(ScAddress(5,2,0)));
5085
5086 m_pDoc->DeleteTab(0);
5087 }
5088
testFuncSUMXMY2()5089 void TestFormula::testFuncSUMXMY2()
5090 {
5091 m_pDoc->InsertTab(0, "Test SumXMY2");
5092
5093 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5094
5095 ScAddress aPos(0,0,0);
5096 m_pDoc->SetString(aPos, "=SUMXMY2(B1:B3;C1:C3)");
5097 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(aPos));
5098 m_pDoc->SetValue(ScAddress(1,0,0), 1.0); // B1
5099 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(aPos));
5100 m_pDoc->SetValue(ScAddress(1,1,0), 2.0); // B2
5101 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
5102 m_pDoc->SetValue(ScAddress(1,2,0), 3.0); // B3
5103 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(aPos));
5104 m_pDoc->SetValue(ScAddress(2,0,0), -1.0); // C1
5105 CPPUNIT_ASSERT_EQUAL(17.0, m_pDoc->GetValue(aPos));
5106 m_pDoc->SetValue(ScAddress(2,1,0), 3.0); // C2
5107 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(aPos));
5108 m_pDoc->SetValue(ScAddress(2,2,0), 1.0); // C3
5109 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
5110
5111 double result = 0.0;
5112 m_pDoc->SetString(0, 4, 0, "=SUMXMY2({2;3;4};{4;3;2})");
5113 m_pDoc->GetValue(0, 4, 0, result);
5114 ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of SUMXMY2 with inline arrays failed", 8.0, result);
5115
5116 m_pDoc->DeleteTab(0);
5117 }
5118
testFuncMIN()5119 void TestFormula::testFuncMIN()
5120 {
5121 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
5122 m_pDoc->InsertTab(0, "Formula");
5123
5124 // A1:A2
5125 m_pDoc->SetString(ScAddress(0,0,0), "a");
5126 m_pDoc->SetString(ScAddress(0,1,0), "b");
5127
5128 // B1:B2
5129 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
5130 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
5131
5132 // Matrix in C1:C2.
5133 ScMarkData aMark(m_pDoc->GetSheetLimits());
5134 aMark.SelectOneTable(0);
5135 m_pDoc->InsertMatrixFormula(2, 0, 2, 1, aMark, "=MIN(IF(A1:A2=\"c\";B1:B2))");
5136
5137 // Formula cell in C1:C2 should be a 1x2 matrix array.
5138 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
5139 CPPUNIT_ASSERT(pFC);
5140 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be an array.", ScMatrixMode::Formula, pFC->GetMatrixFlag());
5141
5142 SCCOL nCols;
5143 SCROW nRows;
5144 pFC->GetMatColsRows(nCols, nRows);
5145 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
5146 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRows);
5147
5148 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C1 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,0,0))));
5149 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C2 is invalid.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(2,1,0))));
5150
5151 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
5152 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
5153
5154 // Inline array input (A4).
5155 m_pDoc->SetString(ScAddress(0,3,0), "=MIN({-2;4;3})");
5156 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
5157
5158 // Add more values to B3:B4.
5159 m_pDoc->SetValue(ScAddress(1,2,0), 20.0);
5160 m_pDoc->SetValue(ScAddress(1,3,0), -20.0);
5161
5162 // Get the MIN of B1:B4.
5163 m_pDoc->SetString(ScAddress(2,4,0), "=MIN(B1:B4)");
5164 CPPUNIT_ASSERT_EQUAL(-20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
5165
5166 m_pDoc->DeleteTab(0);
5167 }
5168
testFuncN()5169 void TestFormula::testFuncN()
5170 {
5171 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5172 m_pDoc->InsertTab (0, "foo"));
5173
5174 double result;
5175
5176 // Clear the area first.
5177 clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
5178
5179 // Put values to reference.
5180 double val = 0;
5181 m_pDoc->SetValue(0, 0, 0, val);
5182 m_pDoc->SetString(0, 2, 0, "Text");
5183 val = 1;
5184 m_pDoc->SetValue(0, 3, 0, val);
5185 val = -1;
5186 m_pDoc->SetValue(0, 4, 0, val);
5187 val = 12.3;
5188 m_pDoc->SetValue(0, 5, 0, val);
5189 m_pDoc->SetString(0, 6, 0, "'12.3");
5190
5191 // Cell references
5192 m_pDoc->SetString(1, 0, 0, "=N(A1)");
5193 m_pDoc->SetString(1, 1, 0, "=N(A2)");
5194 m_pDoc->SetString(1, 2, 0, "=N(A3)");
5195 m_pDoc->SetString(1, 3, 0, "=N(A4)");
5196 m_pDoc->SetString(1, 4, 0, "=N(A5)");
5197 m_pDoc->SetString(1, 5, 0, "=N(A6)");
5198 m_pDoc->SetString(1, 6, 0, "=N(A9)");
5199
5200 // In-line values
5201 m_pDoc->SetString(1, 7, 0, "=N(0)");
5202 m_pDoc->SetString(1, 8, 0, "=N(1)");
5203 m_pDoc->SetString(1, 9, 0, "=N(-1)");
5204 m_pDoc->SetString(1, 10, 0, "=N(123)");
5205 m_pDoc->SetString(1, 11, 0, "=N(\"\")");
5206 m_pDoc->SetString(1, 12, 0, "=N(\"12\")");
5207 m_pDoc->SetString(1, 13, 0, "=N(\"foo\")");
5208
5209 // Range references
5210 m_pDoc->SetString(2, 2, 0, "=N(A1:A8)");
5211 m_pDoc->SetString(2, 3, 0, "=N(A1:A8)");
5212 m_pDoc->SetString(2, 4, 0, "=N(A1:A8)");
5213 m_pDoc->SetString(2, 5, 0, "=N(A1:A8)");
5214
5215 // Calculate and check the results.
5216 m_pDoc->CalcAll();
5217 double checks1[] = {
5218 0, 0, 0, 1, -1, 12.3, 0, // cell reference
5219 0, 1, -1, 123, 0, 0, 0 // in-line values
5220 };
5221 for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
5222 {
5223 m_pDoc->GetValue(1, i, 0, result);
5224 bool bGood = result == checks1[i];
5225 if (!bGood)
5226 {
5227 cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
5228 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
5229 }
5230 }
5231 double checks2[] = {
5232 0, 1, -1, 12.3 // range references
5233 };
5234 for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
5235 {
5236 m_pDoc->GetValue(1, i+2, 0, result);
5237 bool bGood = result == checks2[i];
5238 if (!bGood)
5239 {
5240 cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
5241 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
5242 }
5243 }
5244
5245 m_pDoc->DeleteTab(0);
5246 }
5247
testFuncCOUNTIF()5248 void TestFormula::testFuncCOUNTIF()
5249 {
5250 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5251
5252 // COUNTIF (test case adopted from OOo i#36381)
5253
5254 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5255 m_pDoc->InsertTab (0, "foo"));
5256
5257 // Empty A1:A39 first.
5258 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5259
5260 // Raw data (rows 1 through 9)
5261 const char* aData[] = {
5262 "1999",
5263 "2000",
5264 "0",
5265 "0",
5266 "0",
5267 "2002",
5268 "2001",
5269 "X",
5270 "2002"
5271 };
5272
5273 SCROW nRows = SAL_N_ELEMENTS(aData);
5274 for (SCROW i = 0; i < nRows; ++i)
5275 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5276
5277 printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
5278
5279 // formulas and results
5280 static const struct {
5281 const char* pFormula; double fResult;
5282 } aChecks[] = {
5283 { "=COUNTIF(A1:A12;1999)", 1 },
5284 { "=COUNTIF(A1:A12;2002)", 2 },
5285 { "=COUNTIF(A1:A12;1998)", 0 },
5286 { "=COUNTIF(A1:A12;\">=1999\")", 5 },
5287 { "=COUNTIF(A1:A12;\">1999\")", 4 },
5288 { "=COUNTIF(A1:A12;\"<2001\")", 5 },
5289 { "=COUNTIF(A1:A12;\">0\")", 5 },
5290 { "=COUNTIF(A1:A12;\">=0\")", 8 },
5291 { "=COUNTIF(A1:A12;0)", 3 },
5292 { "=COUNTIF(A1:A12;\"X\")", 1 },
5293 { "=COUNTIF(A1:A12;)", 3 }
5294 };
5295
5296 nRows = SAL_N_ELEMENTS(aChecks);
5297 for (SCROW i = 0; i < nRows; ++i)
5298 {
5299 SCROW nRow = 20 + i;
5300 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5301 }
5302
5303 for (SCROW i = 0; i < nRows; ++i)
5304 {
5305 double result;
5306 SCROW nRow = 20 + i;
5307 m_pDoc->GetValue(0, nRow, 0, result);
5308 bool bGood = result == aChecks[i].fResult;
5309 if (!bGood)
5310 {
5311 cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
5312 << " expected=" << aChecks[i].fResult << " actual=" << result << endl;
5313 CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
5314 }
5315 }
5316
5317 // Don't count empty strings when searching for a number.
5318
5319 // Clear A1:A2.
5320 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
5321
5322 m_pDoc->SetString(0, 0, 0, "=\"\"");
5323 m_pDoc->SetString(0, 1, 0, "=COUNTIF(A1;1)");
5324
5325 double result = m_pDoc->GetValue(0, 1, 0);
5326 ASSERT_DOUBLES_EQUAL_MESSAGE("We shouldn't count empty string as valid number.", 0.0, result);
5327
5328 // Another test case adopted from fdo#77039.
5329 clearSheet(m_pDoc, 0);
5330
5331 // Set formula cells with blank results in A1:A4.
5332 for (SCROW i = 0; i <=3; ++i)
5333 m_pDoc->SetString(ScAddress(0,i,0), "=\"\"");
5334
5335 // Insert formula into A5 to count all cells with empty strings.
5336 m_pDoc->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
5337
5338 // We should correctly count with empty string key.
5339 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,4,0)));
5340
5341 // Another test case adopted from tdf#99291, empty array elements should
5342 // not match empty cells, but cells with 0.
5343 clearSheet(m_pDoc, 0);
5344 ScMarkData aMark(m_pDoc->GetSheetLimits());
5345 aMark.SelectOneTable(0);
5346 m_pDoc->InsertMatrixFormula(0,0, 0,1, aMark, "=COUNTIF(B1:B5;C1:C2)");
5347 // As we will be testing for 0.0 values, check that formulas are actually present.
5348 OUString aFormula;
5349 m_pDoc->GetFormula(0,0,0, aFormula);
5350 CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
5351 m_pDoc->GetFormula(0,1,0, aFormula);
5352 CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula);
5353 // The 0.0 results expected.
5354 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5355 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5356 // 0.0 in B2, 1.0 in B3 and B4
5357 m_pDoc->SetValue( ScAddress(1,1,0), 0.0);
5358 m_pDoc->SetValue( ScAddress(1,2,0), 1.0);
5359 m_pDoc->SetValue( ScAddress(1,3,0), 1.0);
5360 // Matched by 0.0 produced by empty cell in array, and 1.0 in C2.
5361 m_pDoc->SetValue( ScAddress(2,1,0), 1.0);
5362 CPPUNIT_ASSERT_EQUAL_MESSAGE("One cell with 0.0", 1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
5363 CPPUNIT_ASSERT_EQUAL_MESSAGE("Two cells with 1.0", 2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
5364
5365 m_pDoc->DeleteTab(0);
5366 }
5367
testFuncIF()5368 void TestFormula::testFuncIF()
5369 {
5370 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5371
5372 m_pDoc->InsertTab(0, "Formula");
5373
5374 m_pDoc->SetString(ScAddress(0,0,0), "=IF(B1=2;\"two\";\"not two\")");
5375 CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
5376 m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
5377 CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
5378 m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
5379 CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
5380
5381 // Test nested IF in array/matrix if the nested IF condition is a scalar.
5382 ScMarkData aMark(m_pDoc->GetSheetLimits());
5383 aMark.SelectOneTable(0);
5384 m_pDoc->InsertMatrixFormula(0,2, 1,2, aMark, "=IF({1;0};IF(1;23);42)");
5385 // Results must be 23 and 42.
5386 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0)));
5387 CPPUNIT_ASSERT_EQUAL(42.0, m_pDoc->GetValue(ScAddress(1,2,0)));
5388
5389 // Test nested IF in array/matrix if nested IF conditions are range
5390 // references, data in A5:C8, matrix formula in D4 so there is no
5391 // implicit intersection between formula and ranges.
5392 {
5393 std::vector<std::vector<const char*>> aData = {
5394 { "1", "1", "16" },
5395 { "0", "1", "32" },
5396 { "1", "0", "64" },
5397 { "0", "0", "128" }
5398 };
5399 ScAddress aPos(0,4,0);
5400 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
5401 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
5402 }
5403 m_pDoc->InsertMatrixFormula(3,3, 3,3, aMark, "=SUM(IF(A5:A8;IF(B5:B8;C5:C8;0);0))");
5404 // Result must be 16, only the first row matches all criteria.
5405 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(3,3,0)));
5406
5407 // A11:B11
5408 // Test nested IF in array/matrix if the nested IF has no Else path.
5409 m_pDoc->InsertMatrixFormula(0,10, 1,10, aMark, "=IF(IF({1;0};12);34;56)");
5410 // Results must be 34 and 56.
5411 CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(0,10,0)));
5412 CPPUNIT_ASSERT_EQUAL(56.0, m_pDoc->GetValue(ScAddress(1,10,0)));
5413
5414 m_pDoc->DeleteTab(0);
5415 }
5416
testFuncCHOOSE()5417 void TestFormula::testFuncCHOOSE()
5418 {
5419 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5420
5421 m_pDoc->InsertTab(0, "Formula");
5422
5423 m_pDoc->SetString(ScAddress(0,0,0), "=CHOOSE(B1;\"one\";\"two\";\"three\")");
5424 FormulaError nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
5425 CPPUNIT_ASSERT_MESSAGE("Formula result should be an error since B1 is still empty.", nError != FormulaError::NONE);
5426 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
5427 CPPUNIT_ASSERT_EQUAL(OUString("one"), m_pDoc->GetString(ScAddress(0,0,0)));
5428 m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
5429 CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
5430 m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
5431 CPPUNIT_ASSERT_EQUAL(OUString("three"), m_pDoc->GetString(ScAddress(0,0,0)));
5432 m_pDoc->SetValue(ScAddress(1,0,0), 4.0);
5433 nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
5434 CPPUNIT_ASSERT_MESSAGE("Formula result should be an error due to out-of-bound input..", nError != FormulaError::NONE);
5435
5436 m_pDoc->DeleteTab(0);
5437 }
5438
testFuncIFERROR()5439 void TestFormula::testFuncIFERROR()
5440 {
5441 // IFERROR/IFNA (fdo#56124)
5442
5443 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5444 m_pDoc->InsertTab (0, "foo"));
5445
5446 // Empty A1:A39 first.
5447 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5448
5449 // Raw data (rows 1 through 12)
5450 const char* aData[] = {
5451 "1",
5452 "e",
5453 "=SQRT(4)",
5454 "=SQRT(-2)",
5455 "=A4",
5456 "=1/0",
5457 "=NA()",
5458 "bar",
5459 "4",
5460 "gee",
5461 "=1/0",
5462 "23"
5463 };
5464
5465 SCROW nRows = SAL_N_ELEMENTS(aData);
5466 for (SCROW i = 0; i < nRows; ++i)
5467 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5468
5469 printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
5470
5471 // formulas and results
5472 static const struct {
5473 const char* pFormula; const char* pResult;
5474 } aChecks[] = {
5475 { "=IFERROR(A1;9)", "1" },
5476 { "=IFERROR(A2;9)", "e" },
5477 { "=IFERROR(A3;9)", "2" },
5478 { "=IFERROR(A4;-7)", "-7" },
5479 { "=IFERROR(A5;-7)", "-7" },
5480 { "=IFERROR(A6;-7)", "-7" },
5481 { "=IFERROR(A7;-7)", "-7" },
5482 { "=IFNA(A6;9)", "#DIV/0!" },
5483 { "=IFNA(A7;-7)", "-7" },
5484 { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)", "4" },
5485 { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
5486 { "{=IFERROR(3*A11:A12;1998)}[0]", "1998" }, // um... this is not the correct way to insert a
5487 { "{=IFERROR(3*A11:A12;1998)}[1]", "69" } // matrix formula, just a place holder, see below
5488 };
5489
5490 nRows = SAL_N_ELEMENTS(aChecks);
5491 for (SCROW i = 0; i < nRows-2; ++i)
5492 {
5493 SCROW nRow = 20 + i;
5494 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5495 }
5496
5497 // Create a matrix range in last two rows of the range above, actual data
5498 // of the placeholders.
5499 ScMarkData aMark(m_pDoc->GetSheetLimits());
5500 aMark.SelectOneTable(0);
5501 m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)");
5502
5503 m_pDoc->CalcAll();
5504
5505 for (SCROW i = 0; i < nRows; ++i)
5506 {
5507 SCROW nRow = 20 + i;
5508 OUString aResult = m_pDoc->GetString(0, nRow, 0);
5509 CPPUNIT_ASSERT_EQUAL_MESSAGE(
5510 aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
5511 }
5512
5513 const SCCOL nCols = 3;
5514 std::vector<std::vector<const char*>> aData2 = {
5515 { "1", "2", "3" },
5516 { "4", "=1/0", "6" },
5517 { "7", "8", "9" }
5518 };
5519 const char* aCheck2[][nCols] = {
5520 { "1", "2", "3" },
5521 { "4", "Error","6" },
5522 { "7", "8", "9" }
5523 };
5524
5525 // Data in C1:E3
5526 ScAddress aPos(2,0,0);
5527 ScRange aRange = insertRangeData(m_pDoc, aPos, aData2);
5528 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
5529
5530 // Array formula in F4:H6
5531 const SCROW nElems2 = SAL_N_ELEMENTS(aCheck2);
5532 const SCCOL nStartCol = aPos.Col() + nCols;
5533 const SCROW nStartRow = aPos.Row() + nElems2;
5534 m_pDoc->InsertMatrixFormula( nStartCol, nStartRow, nStartCol+nCols-1, nStartRow+nElems2-1, aMark,
5535 "=IFERROR(C1:E3;\"Error\")");
5536
5537 m_pDoc->CalcAll();
5538
5539 for (SCCOL nCol = nStartCol; nCol < nStartCol + nCols; ++nCol)
5540 {
5541 for (SCROW nRow = nStartRow; nRow < nStartRow + nElems2; ++nRow)
5542 {
5543 OUString aResult = m_pDoc->GetString( nCol, nRow, 0);
5544 CPPUNIT_ASSERT_EQUAL_MESSAGE( "IFERROR array result",
5545 OUString::createFromAscii( aCheck2[nRow-nStartRow][nCol-nStartCol]), aResult);
5546 }
5547 }
5548
5549 m_pDoc->DeleteTab(0);
5550 }
5551
testFuncSHEET()5552 void TestFormula::testFuncSHEET()
5553 {
5554 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5555 m_pDoc->InsertTab (SC_TAB_APPEND, "test1"));
5556
5557 m_pDoc->SetString(0, 0, 0, "=SHEETS()");
5558 m_pDoc->CalcFormulaTree(false, false);
5559 double original;
5560 m_pDoc->GetValue(0, 0, 0, original);
5561
5562 CPPUNIT_ASSERT_EQUAL_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
5563 static_cast<SCTAB>(original), m_pDoc->GetTableCount());
5564
5565 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5566 m_pDoc->InsertTab (SC_TAB_APPEND, "test2"));
5567
5568 double modified;
5569 m_pDoc->GetValue(0, 0, 0, modified);
5570 ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
5571 1.0, modified - original);
5572
5573 SCTAB nTabCount = m_pDoc->GetTableCount();
5574 m_pDoc->DeleteTab(--nTabCount);
5575
5576 m_pDoc->GetValue(0, 0, 0, modified);
5577 ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
5578 0.0, modified - original);
5579
5580 m_pDoc->DeleteTab(--nTabCount);
5581 }
5582
testFuncNOW()5583 void TestFormula::testFuncNOW()
5584 {
5585 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5586 m_pDoc->InsertTab (0, "foo"));
5587
5588 double val = 1;
5589 m_pDoc->SetValue(0, 0, 0, val);
5590 m_pDoc->SetString(0, 1, 0, "=IF(A1>0;NOW();0");
5591 double now1;
5592 m_pDoc->GetValue(0, 1, 0, now1);
5593 CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
5594
5595 val = 0;
5596 m_pDoc->SetValue(0, 0, 0, val);
5597 m_pDoc->CalcFormulaTree(false, false);
5598 double zero;
5599 m_pDoc->GetValue(0, 1, 0, zero);
5600 ASSERT_DOUBLES_EQUAL_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", 0.0, zero);
5601
5602 val = 1;
5603 m_pDoc->SetValue(0, 0, 0, val);
5604 m_pDoc->CalcFormulaTree(false, false);
5605 double now2;
5606 m_pDoc->GetValue(0, 1, 0, now2);
5607 CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
5608
5609 m_pDoc->DeleteTab(0);
5610 }
5611
testFuncNUMBERVALUE()5612 void TestFormula::testFuncNUMBERVALUE()
5613 {
5614 // NUMBERVALUE fdo#57180
5615
5616 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5617 m_pDoc->InsertTab (0, "foo"));
5618
5619 // Empty A1:A39 first.
5620 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
5621
5622 // Raw data (rows 1 through 6)
5623 const char* aData[] = {
5624 "1ag9a9b9",
5625 "1ag34 5g g6 78b9%%",
5626 "1 234d56E-2",
5627 "d4",
5628 "54.4",
5629 "1a2b3e1%"
5630 };
5631
5632 SCROW nRows = SAL_N_ELEMENTS(aData);
5633 for (SCROW i = 0; i < nRows; ++i)
5634 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5635
5636 printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
5637
5638 // formulas and results
5639 static const struct {
5640 const char* pFormula; const char* pResult;
5641 } aChecks[] = {
5642 { "=NUMBERVALUE(A1;\"b\";\"ag\")", "199.9" },
5643 { "=NUMBERVALUE(A2;\"b\";\"ag\")", "134.56789" },
5644 { "=NUMBERVALUE(A2;\"b\";\"g\")", "#VALUE!" },
5645 { "=NUMBERVALUE(A3;\"d\")", "12.3456" },
5646 { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
5647 { "=NUMBERVALUE(A4;)", "Err:502" },
5648 { "=NUMBERVALUE(A5;)", "Err:502" },
5649 { "=NUMBERVALUE(A6;\"b\";\"a\")", "1.23" }
5650 };
5651
5652 nRows = SAL_N_ELEMENTS(aChecks);
5653 for (SCROW i = 0; i < nRows; ++i)
5654 {
5655 SCROW nRow = 20 + i;
5656 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
5657 }
5658 m_pDoc->CalcAll();
5659
5660 for (SCROW i = 0; i < nRows; ++i)
5661 {
5662 SCROW nRow = 20 + i;
5663 OUString aResult = m_pDoc->GetString(0, nRow, 0);
5664 CPPUNIT_ASSERT_EQUAL_MESSAGE(
5665 aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
5666 }
5667
5668 m_pDoc->DeleteTab(0);
5669 }
5670
testFuncLEN()5671 void TestFormula::testFuncLEN()
5672 {
5673 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
5674
5675 m_pDoc->InsertTab(0, "Formula");
5676
5677 // Leave A1:A3 empty, and insert an array of LEN in B1:B3 that references
5678 // these empty cells.
5679
5680 ScMarkData aMark(m_pDoc->GetSheetLimits());
5681 aMark.SelectOneTable(0);
5682 m_pDoc->InsertMatrixFormula(1, 0, 1, 2, aMark, "=LEN(A1:A3)");
5683
5684 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
5685 CPPUNIT_ASSERT(pFC);
5686 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be a matrix origin.",
5687 ScMatrixMode::Formula, pFC->GetMatrixFlag());
5688
5689 // This should be a 1x3 matrix.
5690 SCCOL nCols = -1;
5691 SCROW nRows = -1;
5692 pFC->GetMatColsRows(nCols, nRows);
5693 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
5694 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRows);
5695
5696 // LEN value should be 0 for an empty cell.
5697 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
5698 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
5699 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,2,0)));
5700
5701 m_pDoc->DeleteTab(0);
5702 }
5703
testFuncLOOKUP()5704 void TestFormula::testFuncLOOKUP()
5705 {
5706 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
5707
5708 m_pDoc->InsertTab(0, "Test");
5709
5710 // Raw data
5711 const char* aData[][2] = {
5712 { "=CONCATENATE(\"A\")", "1" },
5713 { "=CONCATENATE(\"B\")", "2" },
5714 { "=CONCATENATE(\"C\")", "3" },
5715 { nullptr, nullptr } // terminator
5716 };
5717
5718 // Insert raw data into A1:B3.
5719 for (SCROW i = 0; aData[i][0]; ++i)
5720 {
5721 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5722 m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5723 }
5724
5725 const char* aData2[][2] = {
5726 { "A", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5727 { "B", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5728 { "C", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5729 { nullptr, nullptr } // terminator
5730 };
5731
5732 // Insert check formulas into A5:B7.
5733 for (SCROW i = 0; aData2[i][0]; ++i)
5734 {
5735 m_pDoc->SetString(0, i+4, 0, OUString::createFromAscii(aData2[i][0]));
5736 m_pDoc->SetString(1, i+4, 0, OUString::createFromAscii(aData2[i][1]));
5737 }
5738
5739 printRange(m_pDoc, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
5740
5741 // Values for B5:B7 should be 1, 2, and 3.
5742 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,4,0))));
5743 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,5,0))));
5744 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1,6,0))));
5745
5746 ASSERT_DOUBLES_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
5747 ASSERT_DOUBLES_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,5,0)));
5748 ASSERT_DOUBLES_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,6,0)));
5749
5750 m_pDoc->DeleteTab(0);
5751 }
5752
testFuncLOOKUParrayWithError()5753 void TestFormula::testFuncLOOKUParrayWithError()
5754 {
5755 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5756 m_pDoc->InsertTab(0, "Test");
5757
5758 std::vector<std::vector<const char*>> aData = {
5759 { "x", "y", "z" },
5760 { "a", "b", "c" }
5761 };
5762 insertRangeData(m_pDoc, ScAddress(2,1,0), aData); // C2:E3
5763 m_pDoc->SetString(0,0,0, "=LOOKUP(2;1/(C2:E2<>\"\");C3:E3)"); // A1
5764
5765 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last column.", OUString("c"), m_pDoc->GetString(0,0,0));
5766 m_pDoc->SetString(4,1,0, ""); // E2
5767 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for second last column.", OUString("b"), m_pDoc->GetString(0,0,0));
5768
5769 m_pDoc->SetString(6,1,0, "one"); // G2
5770 m_pDoc->SetString(6,5,0, "two"); // G6
5771 // Creates an interim array {1,#DIV/0!,#DIV/0!,#DIV/0!,1,#DIV/0!,#DIV/0!,#DIV/0!}
5772 m_pDoc->SetString(7,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(G2:G9)));G2:G9)"); // H9
5773 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last row.", OUString("two"), m_pDoc->GetString(7,8,0));
5774
5775 // Lookup on empty range.
5776 m_pDoc->SetString(9,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(I2:I9)));I2:I9)"); // J9
5777 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find no match.", OUString("#N/A"), m_pDoc->GetString(9,8,0));
5778
5779 m_pDoc->DeleteTab(0);
5780 }
5781
testTdf141146()5782 void TestFormula::testTdf141146()
5783 {
5784 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
5785 m_pDoc->InsertTab(0, "Test1");
5786 m_pDoc->InsertTab(1, "Test2");
5787
5788 std::vector<std::vector<const char*>> aData = {
5789 { "k1", "value1"},
5790 { "k2", "value2"},
5791 { "k3", "value3"}
5792 };
5793
5794 insertRangeData(m_pDoc, ScAddress(0,1,1), aData); // A2:B4
5795 m_pDoc->SetString(4,0,1, "k2"); // E1
5796
5797 m_pDoc->SetString(4,1,1, "=LOOKUP(1;1/(A$2:A$4=E$1);1)");
5798 m_pDoc->SetString(4,2,1, "=LOOKUP(E1;A$2:A$4;B2:B4)");
5799 m_pDoc->SetString(4,3,1, "=LOOKUP(1;1/(A$2:A$4=E$1);B2:B4)");
5800
5801 // Without the fix in place, this test would have failed with
5802 // - Expected: #N/A
5803 // - Actual :
5804 CPPUNIT_ASSERT_EQUAL(OUString("#N/A"), m_pDoc->GetString(4,1,1));
5805 CPPUNIT_ASSERT_EQUAL(OUString("value2"), m_pDoc->GetString(4,2,1));
5806 CPPUNIT_ASSERT_EQUAL(OUString("value2"), m_pDoc->GetString(4,3,1));
5807
5808 m_pDoc->DeleteTab(1);
5809 m_pDoc->DeleteTab(0);
5810 }
5811
testFuncVLOOKUP()5812 void TestFormula::testFuncVLOOKUP()
5813 {
5814 // VLOOKUP
5815
5816 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5817 m_pDoc->InsertTab (0, "foo"));
5818
5819 // Clear A1:F40.
5820 clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
5821
5822 // Raw data
5823 const char* aData[][2] = {
5824 { "Key", "Val" },
5825 { "10", "3" },
5826 { "20", "4" },
5827 { "30", "5" },
5828 { "40", "6" },
5829 { "50", "7" },
5830 { "60", "8" },
5831 { "70", "9" },
5832 { "B", "10" },
5833 { "B", "11" },
5834 { "C", "12" },
5835 { "D", "13" },
5836 { "E", "14" },
5837 { "F", "15" },
5838 { nullptr, nullptr } // terminator
5839 };
5840
5841 // Insert raw data into A1:B14.
5842 for (SCROW i = 0; aData[i][0]; ++i)
5843 {
5844 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
5845 m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
5846 }
5847
5848 printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
5849
5850 // Formula data
5851 static const struct {
5852 const char* pLookup; const char* pFormula; const char* pRes;
5853 } aChecks[] = {
5854 { "Lookup", "Formula", nullptr },
5855 { "12", "=VLOOKUP(D2;A2:B14;2;1)", "3" },
5856 { "29", "=VLOOKUP(D3;A2:B14;2;1)", "4" },
5857 { "31", "=VLOOKUP(D4;A2:B14;2;1)", "5" },
5858 { "45", "=VLOOKUP(D5;A2:B14;2;1)", "6" },
5859 { "56", "=VLOOKUP(D6;A2:B14;2;1)", "7" },
5860 { "65", "=VLOOKUP(D7;A2:B14;2;1)", "8" },
5861 { "78", "=VLOOKUP(D8;A2:B14;2;1)", "9" },
5862 { "Andy", "=VLOOKUP(D9;A2:B14;2;1)", "#N/A" },
5863 { "Bruce", "=VLOOKUP(D10;A2:B14;2;1)", "11" },
5864 { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)", "12" },
5865 { "David", "=VLOOKUP(D12;A2:B14;2;1)", "13" },
5866 { "Edward", "=VLOOKUP(D13;A2:B14;2;1)", "14" },
5867 { "Frank", "=VLOOKUP(D14;A2:B14;2;1)", "15" },
5868 { "Henry", "=VLOOKUP(D15;A2:B14;2;1)", "15" },
5869 { "100", "=VLOOKUP(D16;A2:B14;2;1)", "9" },
5870 { "1000", "=VLOOKUP(D17;A2:B14;2;1)", "9" },
5871 { "Zena", "=VLOOKUP(D18;A2:B14;2;1)", "15" }
5872 };
5873
5874 // Insert formula data into D1:E18.
5875 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5876 {
5877 m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
5878 m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
5879 }
5880 m_pDoc->CalcAll();
5881 printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
5882
5883 // Verify results.
5884 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5885 {
5886 if (i == 0)
5887 // Skip the header row.
5888 continue;
5889
5890 OUString aRes = m_pDoc->GetString(4, i, 0);
5891 bool bGood = aRes.equalsAscii(aChecks[i].pRes);
5892 if (!bGood)
5893 {
5894 cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
5895 << "' expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
5896 CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
5897 }
5898 }
5899
5900 // Clear the sheet and start over.
5901 clearSheet(m_pDoc, 0);
5902
5903 // Lookup on sorted data interspersed with empty cells.
5904
5905 // A1:B8 is the search range.
5906 m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
5907 m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
5908 m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
5909 m_pDoc->SetString(ScAddress(1,2,0), "One");
5910 m_pDoc->SetString(ScAddress(1,4,0), "Two");
5911 m_pDoc->SetString(ScAddress(1,7,0), "Four");
5912
5913 // D1:D5 contain match values.
5914 m_pDoc->SetValue(ScAddress(3,0,0), 1.0);
5915 m_pDoc->SetValue(ScAddress(3,1,0), 2.0);
5916 m_pDoc->SetValue(ScAddress(3,2,0), 3.0);
5917 m_pDoc->SetValue(ScAddress(3,3,0), 4.0);
5918 m_pDoc->SetValue(ScAddress(3,4,0), 5.0);
5919
5920 // E1:E5 contain formulas.
5921 m_pDoc->SetString(ScAddress(4,0,0), "=VLOOKUP(D1;$A$1:$B$8;2)");
5922 m_pDoc->SetString(ScAddress(4,1,0), "=VLOOKUP(D2;$A$1:$B$8;2)");
5923 m_pDoc->SetString(ScAddress(4,2,0), "=VLOOKUP(D3;$A$1:$B$8;2)");
5924 m_pDoc->SetString(ScAddress(4,3,0), "=VLOOKUP(D4;$A$1:$B$8;2)");
5925 m_pDoc->SetString(ScAddress(4,4,0), "=VLOOKUP(D5;$A$1:$B$8;2)");
5926 m_pDoc->CalcAll();
5927
5928 // Check the formula results in E1:E5.
5929 CPPUNIT_ASSERT_EQUAL(OUString("One"), m_pDoc->GetString(ScAddress(4,0,0)));
5930 CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,1,0)));
5931 CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,2,0)));
5932 CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,3,0)));
5933 CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,4,0)));
5934
5935 // Start over again.
5936 clearSheet(m_pDoc, 0);
5937
5938 // Set A,B,...,G to A1:A7.
5939 m_pDoc->SetString(ScAddress(0,0,0), "A");
5940 m_pDoc->SetString(ScAddress(0,1,0), "B");
5941 m_pDoc->SetString(ScAddress(0,2,0), "C");
5942 m_pDoc->SetString(ScAddress(0,3,0), "D");
5943 m_pDoc->SetString(ScAddress(0,4,0), "E");
5944 m_pDoc->SetString(ScAddress(0,5,0), "F");
5945 m_pDoc->SetString(ScAddress(0,6,0), "G");
5946
5947 // Set the formula in C1.
5948 m_pDoc->SetString(ScAddress(2,0,0), "=VLOOKUP(\"C\";A1:A16;1)");
5949 CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc->GetString(ScAddress(2,0,0)));
5950
5951
5952 // A21:E24, test position dependent implicit intersection as argument to a
5953 // scalar value parameter in a function that has a ReferenceOrForceArray
5954 // type parameter somewhere else and formula is not in array mode,
5955 // VLOOKUP(Value;ReferenceOrForceArray;...)
5956 std::vector<std::vector<const char*>> aData2 = {
5957 { "1", "one", "3", "=VLOOKUP(C21:C24;A21:B24;2;0)", "three" },
5958 { "2", "two", "1", "=VLOOKUP(C21:C24;A21:B24;2;0)", "one" },
5959 { "3", "three", "4", "=VLOOKUP(C21:C24;A21:B24;2;0)", "four" },
5960 { "4", "four", "2", "=VLOOKUP(C21:C24;A21:B24;2;0)", "two" }
5961 };
5962
5963 ScAddress aPos2(0,20,0);
5964 ScRange aRange2 = insertRangeData(m_pDoc, aPos2, aData2);
5965 CPPUNIT_ASSERT_EQUAL(aPos2, aRange2.aStart);
5966
5967 aPos2.SetCol(3); // column D formula results
5968 for (size_t i=0; i < aData2.size(); ++i)
5969 {
5970 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aData2[i][4]), m_pDoc->GetString(aPos2));
5971 aPos2.IncRow();
5972 }
5973
5974 m_pDoc->DeleteTab(0);
5975 }
5976
5977 namespace {
5978
5979 struct StrStrCheck {
5980 const char* pVal;
5981 const char* pRes;
5982 };
5983
5984 }
5985
5986 template<size_t DataSize, size_t FormulaSize, int Type>
runTestMATCH(ScDocument * pDoc,const char * aData[DataSize],const StrStrCheck aChecks[FormulaSize])5987 static void runTestMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
5988 {
5989 size_t nDataSize = DataSize;
5990 for (size_t i = 0; i < nDataSize; ++i)
5991 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
5992
5993 for (size_t i = 0; i < FormulaSize; ++i)
5994 {
5995 pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
5996
5997 OUString aFormula = "=MATCH(B" + OUString::number(i+1) + ";A1:A"
5998 + OUString::number(nDataSize) + ";" + OUString::number(Type) + ")";
5999 pDoc->SetString(2, i, 0, aFormula);
6000 }
6001
6002 pDoc->CalcAll();
6003 printRange(pDoc, ScRange(0, 0, 0, 2, FormulaSize-1, 0), "MATCH");
6004
6005 // verify the results.
6006 for (size_t i = 0; i < FormulaSize; ++i)
6007 {
6008 OUString aStr = pDoc->GetString(2, i, 0);
6009 if (!aStr.equalsAscii(aChecks[i].pRes))
6010 {
6011 cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
6012 " criterion='" << aChecks[i].pVal << "'" << endl;
6013 CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
6014 }
6015 }
6016 }
6017
6018 template<size_t DataSize, size_t FormulaSize, int Type>
runTestHorizontalMATCH(ScDocument * pDoc,const char * aData[DataSize],const StrStrCheck aChecks[FormulaSize])6019 static void runTestHorizontalMATCH(ScDocument* pDoc, const char* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
6020 {
6021 size_t nDataSize = DataSize;
6022 for (size_t i = 0; i < nDataSize; ++i)
6023 pDoc->SetString(i, 0, 0, OUString::createFromAscii(aData[i]));
6024
6025 for (size_t i = 0; i < FormulaSize; ++i)
6026 {
6027 pDoc->SetString(i, 1, 0, OUString::createFromAscii(aChecks[i].pVal));
6028
6029 // Assume we don't have more than 26 data columns...
6030 OUString aFormula = "=MATCH(" + OUStringChar(static_cast<sal_Unicode>('A'+i)) + "2;A1:"
6031 + OUStringChar(static_cast<sal_Unicode>('A'+nDataSize)) + "1;" + OUString::number(Type)
6032 + ")";
6033 pDoc->SetString(i, 2, 0, aFormula);
6034 }
6035
6036 pDoc->CalcAll();
6037 printRange(pDoc, ScRange(0, 0, 0, FormulaSize-1, 2, 0), "MATCH");
6038
6039 // verify the results.
6040 for (size_t i = 0; i < FormulaSize; ++i)
6041 {
6042 OUString aStr = pDoc->GetString(i, 2, 0);
6043 if (!aStr.equalsAscii(aChecks[i].pRes))
6044 {
6045 cerr << "column " << char('A'+i) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
6046 " criterion='" << aChecks[i].pVal << "'" << endl;
6047 CPPUNIT_ASSERT_MESSAGE("Unexpected result for horizontal MATCH", false);
6048 }
6049 }
6050 }
6051
testFuncMATCH()6052 void TestFormula::testFuncMATCH()
6053 {
6054 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6055 m_pDoc->InsertTab (0, "foo"));
6056
6057 clearRange(m_pDoc, ScRange(0, 0, 0, 40, 40, 0));
6058 {
6059 // Ascending in-exact match
6060
6061 // data range (A1:A9)
6062 const char* aData[] = {
6063 "1",
6064 "2",
6065 "3",
6066 "4",
6067 "5",
6068 "6",
6069 "7",
6070 "8",
6071 "9",
6072 "B",
6073 "B",
6074 "C",
6075 };
6076
6077 // formula (B1:C12)
6078 static const StrStrCheck aChecks[] = {
6079 { "0.8", "#N/A" },
6080 { "1.2", "1" },
6081 { "2.3", "2" },
6082 { "3.9", "3" },
6083 { "4.1", "4" },
6084 { "5.99", "5" },
6085 { "6.1", "6" },
6086 { "7.2", "7" },
6087 { "8.569", "8" },
6088 { "9.59", "9" },
6089 { "10", "9" },
6090 { "100", "9" },
6091 { "Andy", "#N/A" },
6092 { "Bruce", "11" },
6093 { "Charlie", "12" }
6094 };
6095
6096 runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
6097 clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
6098 runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
6099 clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
6100 }
6101
6102 {
6103 // Descending in-exact match
6104
6105 // data range (A1:A9)
6106 const char* aData[] = {
6107 "D",
6108 "C",
6109 "B",
6110 "9",
6111 "8",
6112 "7",
6113 "6",
6114 "5",
6115 "4",
6116 "3",
6117 "2",
6118 "1"
6119 };
6120
6121 // formula (B1:C12)
6122 static const StrStrCheck aChecks[] = {
6123 { "10", "#N/A" },
6124 { "8.9", "4" },
6125 { "7.8", "5" },
6126 { "6.7", "6" },
6127 { "5.5", "7" },
6128 { "4.6", "8" },
6129 { "3.3", "9" },
6130 { "2.2", "10" },
6131 { "1.1", "11" },
6132 { "0.8", "12" },
6133 { "0", "12" },
6134 { "-2", "12" },
6135 { "Andy", "3" },
6136 { "Bruce", "2" },
6137 { "Charlie", "1" },
6138 { "David", "#N/A" }
6139 };
6140
6141 runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
6142 clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
6143 runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
6144 clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
6145 }
6146
6147 {
6148 // search range contains leading and trailing empty cell ranges.
6149
6150 clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
6151
6152 // A5:A8 contains sorted values.
6153 m_pDoc->SetValue(ScAddress(0,4,0), 1.0);
6154 m_pDoc->SetValue(ScAddress(0,5,0), 2.0);
6155 m_pDoc->SetValue(ScAddress(0,6,0), 3.0);
6156 m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
6157
6158 // Find value 2 which is in A6.
6159 m_pDoc->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
6160 m_pDoc->CalcAll();
6161
6162 CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc->GetString(ScAddress(1,0,0)));
6163 }
6164
6165 {
6166 // Test the ReferenceOrForceArray parameter.
6167
6168 clearRange(m_pDoc, ScRange(0,0,0,1,7,0));
6169
6170 // B1:B5 contain numeric values.
6171 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
6172 m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
6173 m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
6174 m_pDoc->SetValue(ScAddress(1,3,0), 4.0);
6175 m_pDoc->SetValue(ScAddress(1,4,0), 5.0);
6176
6177 // Find string value "33" in concatenated array, no implicit
6178 // intersection is involved, array is forced.
6179 m_pDoc->SetString(ScAddress(0,5,0), "=MATCH(\"33\";B1:B5&B1:B5)");
6180 m_pDoc->CalcAll();
6181 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
6182 }
6183
6184 m_pDoc->DeleteTab(0);
6185 }
6186
testFuncCELL()6187 void TestFormula::testFuncCELL()
6188 {
6189 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6190 m_pDoc->InsertTab (0, "foo"));
6191
6192 clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
6193
6194 {
6195 const char* pContent = "Some random text";
6196 m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
6197 m_pDoc->SetValue(2, 0, 0, 1.2); // Set numeric value to C1;
6198
6199 // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
6200 StrStrCheck aChecks[] = {
6201 { "=CELL(\"COL\";C10)", "3" },
6202 { "=CELL(\"COL\";C5:C10)", "3" },
6203 { "=CELL(\"ROW\";C10)", "10" },
6204 { "=CELL(\"ROW\";C10:E10)", "10" },
6205 { "=CELL(\"SHEET\";C10)", "1" },
6206 { "=CELL(\"ADDRESS\";C10)", "$C$10" },
6207 { "=CELL(\"CONTENTS\";C10)", pContent },
6208 { "=CELL(\"COLOR\";C10)", "0" },
6209 { "=CELL(\"TYPE\";C9)", "b" },
6210 { "=CELL(\"TYPE\";C10)", "l" },
6211 { "=CELL(\"TYPE\";C1)", "v" },
6212 { "=CELL(\"PARENTHESES\";C10)", "0" }
6213 };
6214
6215 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6216 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
6217 m_pDoc->CalcAll();
6218
6219 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6220 {
6221 OUString aVal = m_pDoc->GetString(0, i, 0);
6222 CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
6223 }
6224 }
6225
6226 m_pDoc->DeleteTab(0);
6227 }
6228
6229 /** See also test case document fdo#44456 sheet cpearson */
testFuncDATEDIF()6230 void TestFormula::testFuncDATEDIF()
6231 {
6232 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6233 m_pDoc->InsertTab (0, "foo"));
6234
6235 std::vector<std::vector<const char*>> aData = {
6236 { "2007-01-01", "2007-01-10", "d", "9", "=DATEDIF(A1;B1;C1)" } ,
6237 { "2007-01-01", "2007-01-31", "m", "0", "=DATEDIF(A2;B2;C2)" } ,
6238 { "2007-01-01", "2007-02-01", "m", "1", "=DATEDIF(A3;B3;C3)" } ,
6239 { "2007-01-01", "2007-02-28", "m", "1", "=DATEDIF(A4;B4;C4)" } ,
6240 { "2007-01-01", "2007-12-31", "d", "364", "=DATEDIF(A5;B5;C5)" } ,
6241 { "2007-01-01", "2007-01-31", "y", "0", "=DATEDIF(A6;B6;C6)" } ,
6242 { "2007-01-01", "2008-07-01", "d", "547", "=DATEDIF(A7;B7;C7)" } ,
6243 { "2007-01-01", "2008-07-01", "m", "18", "=DATEDIF(A8;B8;C8)" } ,
6244 { "2007-01-01", "2008-07-01", "ym", "6", "=DATEDIF(A9;B9;C9)" } ,
6245 { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
6246 { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
6247 { "2007-01-01", "2007-01-31", "md", "30", "=DATEDIF(A12;B12;C12)" } ,
6248 { "2007-02-01", "2009-03-01", "md", "0", "=DATEDIF(A13;B13;C13)" } ,
6249 { "2008-02-01", "2009-03-01", "md", "0", "=DATEDIF(A14;B14;C14)" } ,
6250 { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" } // fail date1 > date2
6251 };
6252
6253 clearRange( m_pDoc, ScRange(0, 0, 0, 4, aData.size(), 0));
6254 ScAddress aPos(0,0,0);
6255 ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData);
6256 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
6257
6258 m_pDoc->CalcAll();
6259
6260 for (size_t i = 0; i < aData.size(); ++i)
6261 {
6262 OUString aVal = m_pDoc->GetString( 4, i, 0);
6263 //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
6264 CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
6265 }
6266
6267 m_pDoc->DeleteTab(0);
6268 }
6269
testFuncINDIRECT()6270 void TestFormula::testFuncINDIRECT()
6271 {
6272 OUString aTabName("foo");
6273 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6274 m_pDoc->InsertTab (0, aTabName));
6275 clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
6276
6277 bool bGood = m_pDoc->GetName(0, aTabName);
6278 CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
6279
6280 OUString aTest = "Test", aRefErr = "#REF!";
6281 m_pDoc->SetString(0, 10, 0, aTest);
6282 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cell value.", aTest, m_pDoc->GetString(0,10,0));
6283
6284 OUString aPrefix = "=INDIRECT(\"";
6285
6286 OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
6287 m_pDoc->SetString(0, 0, 0, aFormula);
6288 aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
6289 m_pDoc->SetString(0, 1, 0, aFormula);
6290 aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
6291 m_pDoc->SetString(0, 2, 0, aFormula);
6292 aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
6293 m_pDoc->SetString(0, 3, 0, aFormula);
6294
6295 m_pDoc->CalcAll();
6296 {
6297 // Default (for new documents) is to use current formula syntax
6298 // which is Calc A1
6299 const OUString* aChecks[] = {
6300 &aTest, &aRefErr, &aRefErr, &aTest
6301 };
6302
6303 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6304 {
6305 OUString aVal = m_pDoc->GetString(0, i, 0);
6306 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6307 }
6308 }
6309
6310 ScCalcConfig aConfig;
6311 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO );
6312 m_pDoc->SetCalcConfig(aConfig);
6313 m_pDoc->CalcAll();
6314 {
6315 // Explicit Calc A1 syntax
6316 const OUString* aChecks[] = {
6317 &aTest, &aRefErr, &aRefErr, &aTest
6318 };
6319
6320 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6321 {
6322 OUString aVal = m_pDoc->GetString(0, i, 0);
6323 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6324 }
6325 }
6326
6327 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 );
6328 m_pDoc->SetCalcConfig(aConfig);
6329 m_pDoc->CalcAll();
6330 {
6331 // Excel A1 syntax
6332 const OUString* aChecks[] = {
6333 &aRefErr, &aTest, &aRefErr, &aTest
6334 };
6335
6336 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6337 {
6338 OUString aVal = m_pDoc->GetString(0, i, 0);
6339 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6340 }
6341 }
6342
6343 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
6344 m_pDoc->SetCalcConfig(aConfig);
6345 m_pDoc->CalcAll();
6346 {
6347 // Excel R1C1 syntax
6348 const OUString* aChecks[] = {
6349 &aRefErr, &aRefErr, &aTest, &aTest
6350 };
6351
6352 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
6353 {
6354 OUString aVal = m_pDoc->GetString(0, i, 0);
6355 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
6356 }
6357 }
6358
6359 m_pDoc->DeleteTab(0);
6360 }
6361
6362 // Test case for tdf#83365 - Access across spreadsheet returns Err:504
6363 //
testFuncINDIRECT2()6364 void TestFormula::testFuncINDIRECT2()
6365 {
6366 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6367 m_pDoc->InsertTab (0, "foo"));
6368 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6369 m_pDoc->InsertTab (1, "bar"));
6370 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
6371 m_pDoc->InsertTab (2, "baz"));
6372
6373 m_pDoc->SetValue(0,0,0, 10.0);
6374 m_pDoc->SetValue(0,1,0, 10.0);
6375 m_pDoc->SetValue(0,2,0, 10.0);
6376
6377 // Fill range bar.$A1:bar.$A10 with 1s
6378 for (SCROW i = 0; i < 10; ++i)
6379 m_pDoc->SetValue(0,i,1, 1.0);
6380
6381 // Test range triplet (absolute, relative, relative) : (absolute, relative, relative)
6382 m_pDoc->SetString(0,0,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$1),1)");
6383
6384 // Test range triplet (absolute, relative, relative) : (absolute, absolute, relative)
6385 m_pDoc->SetString(0,1,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$2),1)");
6386
6387 // Test range triplet (absolute, relative, relative) : (absolute, absolute, absolute)
6388 m_pDoc->SetString(0,2,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$3),1)");
6389
6390 // Test range triplet (absolute, absolute, relative) : (absolute, relative, relative)
6391 m_pDoc->SetString(0,3,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
6392
6393 // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
6394 m_pDoc->SetString(0,4,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
6395
6396 // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
6397 m_pDoc->SetString(0,5,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
6398
6399 // Test range triplet (absolute, absolute, absolute) : (absolute, relative, relative)
6400 m_pDoc->SetString(0,6,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
6401
6402 // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, relative)
6403 m_pDoc->SetString(0,7,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
6404
6405 // Check indirect reference "bar.$A\"&foo.$A$1
6406 m_pDoc->SetString(0,8,2, "=COUNTIF(bar.$A$1:INDIRECT(\"bar.$A\"&foo.$A$1),1)");
6407
6408 // This case should return illegal argument error because
6409 // they reference 2 different absolute sheets
6410 // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, absolute)
6411 m_pDoc->SetString(0,9,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
6412
6413 m_pDoc->CalcAll();
6414
6415 // Loop all formulas and check result = 10.0
6416 for (SCROW i = 0; i < 9; ++i)
6417 CPPUNIT_ASSERT_MESSAGE(OString("Failed to INDIRECT reference formula value: " +
6418 OString::number(i)).getStr(), m_pDoc->GetValue(0,i,2) != 10.0);
6419
6420 // Check formula cell error
6421 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,2));
6422 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
6423 CPPUNIT_ASSERT_MESSAGE("This formula cell should be an error.", pFC->GetErrCode() != FormulaError::NONE);
6424
6425 m_pDoc->DeleteTab(2);
6426 m_pDoc->DeleteTab(1);
6427 m_pDoc->DeleteTab(0);
6428 }
6429
6430 // Test for tdf#107724 do not propagate an array context from MATCH to INDIRECT
6431 // as INDIRECT returns ParamClass::Reference
testFunc_MATCH_INDIRECT()6432 void TestFormula::testFunc_MATCH_INDIRECT()
6433 {
6434 CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab( 0, "foo"));
6435
6436 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6437
6438 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
6439 ScRangeData* pRangeData = new ScRangeData( *m_pDoc, "RoleAssignment", "$D$4:$D$13");
6440 pGlobalNames->insert(pRangeData);
6441
6442 // D6: data to match, in 3rd row of named range.
6443 m_pDoc->SetString( 3,5,0, "Test1");
6444 // F15: Formula generating indirect reference of corner addresses taking
6445 // row+offset and column from named range, which are not in array context
6446 // thus don't create arrays of offsets.
6447 m_pDoc->SetString( 5,14,0, "=MATCH(\"Test1\";INDIRECT(ADDRESS(ROW(RoleAssignment)+1;COLUMN(RoleAssignment))&\":\"&ADDRESS(ROW(RoleAssignment)+ROWS(RoleAssignment)-1;COLUMN(RoleAssignment)));0)");
6448
6449 // Match in 2nd row of range offset by 1 expected.
6450 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to not propagate array context from MATCH to INDIRECT",
6451 2.0, m_pDoc->GetValue(5,14,0));
6452
6453 m_pDoc->DeleteTab(0);
6454 }
6455
testFormulaDepTracking()6456 void TestFormula::testFormulaDepTracking()
6457 {
6458 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
6459
6460 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6461
6462 // B2 listens on D2.
6463 m_pDoc->SetString(1, 1, 0, "=D2");
6464 double val = -999.0; // dummy initial value
6465 m_pDoc->GetValue(1, 1, 0, val);
6466 ASSERT_DOUBLES_EQUAL_MESSAGE("Referencing an empty cell should yield zero.", 0.0, val);
6467
6468 // Changing the value of D2 should trigger recalculation of B2.
6469 m_pDoc->SetValue(3, 1, 0, 1.1);
6470 m_pDoc->GetValue(1, 1, 0, val);
6471 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 1.1, val);
6472
6473 // And again.
6474 m_pDoc->SetValue(3, 1, 0, 2.2);
6475 m_pDoc->GetValue(1, 1, 0, val);
6476 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 2.2, val);
6477
6478 clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
6479
6480 // Now, let's test the range dependency tracking.
6481
6482 // B2 listens on D2:E6.
6483 m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
6484 m_pDoc->GetValue(1, 1, 0, val);
6485 ASSERT_DOUBLES_EQUAL_MESSAGE("Summing an empty range should yield zero.", 0.0, val);
6486
6487 // Set value to E3. This should trigger recalc on B2.
6488 m_pDoc->SetValue(4, 2, 0, 2.4);
6489 m_pDoc->GetValue(1, 1, 0, val);
6490 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on single value change.", 2.4, val);
6491
6492 // Set value to D5 to trigger recalc again. Note that this causes an
6493 // addition of 1.2 + 2.4 which is subject to binary floating point
6494 // rounding error. We need to use approxEqual to assess its value.
6495
6496 m_pDoc->SetValue(3, 4, 0, 1.2);
6497 m_pDoc->GetValue(1, 1, 0, val);
6498 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
6499
6500 // Change the value of D2 (boundary case).
6501 m_pDoc->SetValue(3, 1, 0, 1.0);
6502 m_pDoc->GetValue(1, 1, 0, val);
6503 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
6504
6505 // Change the value of E6 (another boundary case).
6506 m_pDoc->SetValue(4, 5, 0, 2.0);
6507 m_pDoc->GetValue(1, 1, 0, val);
6508 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
6509
6510 // Change the value of D6 (another boundary case).
6511 m_pDoc->SetValue(3, 5, 0, 3.0);
6512 m_pDoc->GetValue(1, 1, 0, val);
6513 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
6514
6515 // Change the value of E2 (another boundary case).
6516 m_pDoc->SetValue(4, 1, 0, 0.4);
6517 m_pDoc->GetValue(1, 1, 0, val);
6518 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
6519
6520 // Change the existing non-empty value cell (E2).
6521 m_pDoc->SetValue(4, 1, 0, 2.4);
6522 m_pDoc->GetValue(1, 1, 0, val);
6523 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
6524
6525 clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
6526
6527 // Now, column-based dependency tracking. We now switch to the R1C1
6528 // syntax which is easier to use for repeated relative references.
6529
6530 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
6531
6532 val = 0.0;
6533 for (SCROW nRow = 1; nRow <= 9; ++nRow)
6534 {
6535 // Static value in column 1.
6536 m_pDoc->SetValue(0, nRow, 0, ++val);
6537
6538 // Formula in column 2 that references cell to the left.
6539 m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
6540
6541 // Formula in column 3 that references cell to the left.
6542 m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
6543 }
6544
6545 // Check formula values.
6546 val = 0.0;
6547 for (SCROW nRow = 1; nRow <= 9; ++nRow)
6548 {
6549 ++val;
6550 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val, m_pDoc->GetValue(1, nRow, 0));
6551 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val*2.0, m_pDoc->GetValue(2, nRow, 0));
6552 }
6553
6554 // Intentionally insert a formula in column 1. This will break column 1's
6555 // uniformity of consisting only of static value cells.
6556 m_pDoc->SetString(0, 4, 0, "=R2C3");
6557 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(0, 4, 0));
6558 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(1, 4, 0));
6559 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc->GetValue(2, 4, 0));
6560
6561 m_pDoc->DeleteTab(0);
6562 }
6563
testFormulaDepTracking2()6564 void TestFormula::testFormulaDepTracking2()
6565 {
6566 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
6567
6568 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6569
6570 double val = 2.0;
6571 m_pDoc->SetValue(0, 0, 0, val);
6572 val = 4.0;
6573 m_pDoc->SetValue(1, 0, 0, val);
6574 val = 5.0;
6575 m_pDoc->SetValue(0, 1, 0, val);
6576 m_pDoc->SetString(2, 0, 0, "=A1/B1");
6577 m_pDoc->SetString(1, 1, 0, "=B1*C1");
6578
6579 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
6580
6581 clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
6582
6583 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
6584
6585 m_pDoc->DeleteTab(0);
6586 }
6587
testFormulaDepTracking3()6588 void TestFormula::testFormulaDepTracking3()
6589 {
6590 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6591
6592 m_pDoc->InsertTab(0, "Formula");
6593
6594 std::vector<std::vector<const char*>> aData = {
6595 { "1", "2", "=SUM(A1:B1)", "=SUM(C1:C3)" },
6596 { "3", "4", "=SUM(A2:B2)", nullptr },
6597 { "5", "6", "=SUM(A3:B3)", nullptr },
6598 };
6599
6600 insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
6601
6602 // Check the initial formula results.
6603 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
6604 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(ScAddress(2,1,0)));
6605 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,2,0)));
6606 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(3,0,0)));
6607
6608 // Change B3 and make sure the change gets propagated to D1.
6609 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6610 rFunc.SetValueCell(ScAddress(1,2,0), 60.0, false);
6611 CPPUNIT_ASSERT_EQUAL(65.0, m_pDoc->GetValue(ScAddress(2,2,0)));
6612 CPPUNIT_ASSERT_EQUAL(75.0, m_pDoc->GetValue(ScAddress(3,0,0)));
6613
6614 m_pDoc->DeleteTab(0);
6615 }
6616
testFormulaDepTrackingDeleteRow()6617 void TestFormula::testFormulaDepTrackingDeleteRow()
6618 {
6619 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6620
6621 m_pDoc->InsertTab(0, "Test");
6622
6623 // Values in A1:A3.
6624 m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
6625 m_pDoc->SetValue(ScAddress(0,1,0), 3.0);
6626 m_pDoc->SetValue(ScAddress(0,2,0), 5.0);
6627
6628 // SUM(A1:A3) in A5.
6629 m_pDoc->SetString(ScAddress(0,4,0), "=SUM(A1:A3)");
6630
6631 // A6 to reference A5.
6632 m_pDoc->SetString(ScAddress(0,5,0), "=A5*10");
6633 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
6634 CPPUNIT_ASSERT(pFC);
6635
6636 // A4 should have a broadcaster with A5 listening to it.
6637 SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,4,0));
6638 CPPUNIT_ASSERT(pBC);
6639 SvtBroadcaster::ListenersType* pListeners = &pBC->GetAllListeners();
6640 CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should have one listener.", size_t(1), pListeners->size());
6641 const SvtListener* pListener = pListeners->at(0);
6642 CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 should be listening to A5.", static_cast<const ScFormulaCell*>(pListener), pFC);
6643
6644 // Check initial values.
6645 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
6646 CPPUNIT_ASSERT_EQUAL(90.0, m_pDoc->GetValue(ScAddress(0,5,0)));
6647
6648 // Delete row 2.
6649 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6650 ScMarkData aMark(m_pDoc->GetSheetLimits());
6651 aMark.SelectOneTable(0);
6652 rFunc.DeleteCells(ScRange(0,1,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
6653
6654 pBC = m_pDoc->GetBroadcaster(ScAddress(0,3,0));
6655 CPPUNIT_ASSERT_MESSAGE("Broadcaster at A5 should have shifted to A4.", pBC);
6656 pListeners = &pBC->GetAllListeners();
6657 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should have one listener.", size_t(1), pListeners->size());
6658 pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
6659 CPPUNIT_ASSERT(pFC);
6660 pListener = pListeners->at(0);
6661 CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should be listening to A4.", static_cast<const ScFormulaCell*>(pListener), pFC);
6662
6663 // Check values after row deletion.
6664 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,3,0)));
6665 CPPUNIT_ASSERT_EQUAL(60.0, m_pDoc->GetValue(ScAddress(0,4,0)));
6666
6667 m_pDoc->DeleteTab(0);
6668 }
6669
testFormulaDepTrackingDeleteCol()6670 void TestFormula::testFormulaDepTrackingDeleteCol()
6671 {
6672 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6673
6674 m_pDoc->InsertTab(0, "Formula");
6675
6676 std::vector<std::vector<const char*>> aData = {
6677 { "2", "=A1", "=B1" }, // not grouped
6678 { nullptr, nullptr, nullptr }, // empty row to separate the formula groups.
6679 { "3", "=A3", "=B3" }, // grouped
6680 { "4", "=A4", "=B4" }, // grouped
6681 };
6682
6683 ScAddress aPos(0,0,0);
6684 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
6685 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
6686
6687 // Check the initial values.
6688 for (SCCOL i = 0; i <= 2; ++i)
6689 {
6690 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(i,0,0)));
6691 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(i,2,0)));
6692 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(i,3,0)));
6693 }
6694
6695 // Make sure B3:B4 and C3:C4 are grouped.
6696 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
6697 CPPUNIT_ASSERT(pFC);
6698 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6699 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6700
6701 pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
6702 CPPUNIT_ASSERT(pFC);
6703 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
6704 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6705
6706 // Delete column A. A1, B1, A3:A4 and B3:B4 should all show #REF!.
6707 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
6708 ScMarkData aMark(m_pDoc->GetSheetLimits());
6709 aMark.SelectOneTable(0);
6710 rFunc.DeleteCells(ScRange(0,0,0,0,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
6711
6712 {
6713 // Expected output table content. 0 = empty cell
6714 std::vector<std::vector<const char*>> aOutputCheck = {
6715 { "#REF!", "#REF!" },
6716 { nullptr, nullptr },
6717 { "#REF!", "#REF!" },
6718 { "#REF!", "#REF!" },
6719 };
6720
6721 ScRange aCheckRange(0,0,0,1,3,0);
6722 bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after deleting column A");
6723 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6724 }
6725
6726 // Undo and check the result.
6727 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
6728 CPPUNIT_ASSERT(pUndoMgr);
6729 pUndoMgr->Undo();
6730
6731 {
6732 // Expected output table content. 0 = empty cell
6733 std::vector<std::vector<const char*>> aOutputCheck = {
6734 { "2", "2", "2" },
6735 { nullptr, nullptr, nullptr },
6736 { "3", "3", "3" },
6737 { "4", "4", "4" },
6738 };
6739
6740 ScRange aCheckRange(0,0,0,2,3,0);
6741 bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after undo");
6742 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6743 }
6744
6745 // Redo and check.
6746 pUndoMgr->Redo();
6747 {
6748 // Expected output table content. 0 = empty cell
6749 std::vector<std::vector<const char*>> aOutputCheck = {
6750 { "#REF!", "#REF!" },
6751 { nullptr, nullptr },
6752 { "#REF!", "#REF!" },
6753 { "#REF!", "#REF!" },
6754 };
6755
6756 ScRange aCheckRange(0,0,0,1,3,0);
6757 bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after redo");
6758 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6759 }
6760
6761 // Undo and change the values in column A.
6762 pUndoMgr->Undo();
6763 m_pDoc->SetValue(ScAddress(0,0,0), 22.0);
6764 m_pDoc->SetValue(ScAddress(0,2,0), 23.0);
6765 m_pDoc->SetValue(ScAddress(0,3,0), 24.0);
6766
6767 {
6768 // Expected output table content. 0 = empty cell
6769 std::vector<std::vector<const char*>> aOutputCheck = {
6770 { "22", "22", "22" },
6771 { nullptr, nullptr, nullptr },
6772 { "23", "23", "23" },
6773 { "24", "24", "24" },
6774 };
6775
6776 ScRange aCheckRange(0,0,0,2,3,0);
6777 bool bSuccess = checkOutput(m_pDoc, aCheckRange, aOutputCheck, "Check after undo & value change in column A");
6778 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
6779 }
6780
6781 m_pDoc->DeleteTab(0);
6782 }
6783
testFormulaMatrixResultUpdate()6784 void TestFormula::testFormulaMatrixResultUpdate()
6785 {
6786 m_pDoc->InsertTab(0, "Test");
6787
6788 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
6789
6790 // Set a numeric value to A1.
6791 m_pDoc->SetValue(ScAddress(0,0,0), 11.0);
6792
6793 ScMarkData aMark(m_pDoc->GetSheetLimits());
6794 aMark.SelectOneTable(0);
6795 m_pDoc->InsertMatrixFormula(1, 0, 1, 0, aMark, "=A1");
6796 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
6797 ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
6798 CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC);
6799 pFC->SetChanged(false); // Clear this flag to simulate displaying of formula cell value on screen.
6800
6801 m_pDoc->SetString(ScAddress(0,0,0), "ABC");
6802 CPPUNIT_ASSERT_EQUAL(OUString("ABC"), m_pDoc->GetString(ScAddress(1,0,0)));
6803 pFC->SetChanged(false);
6804
6805 // Put a new value into A1. The formula should update.
6806 m_pDoc->SetValue(ScAddress(0,0,0), 13.0);
6807 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(1,0,0)));
6808
6809 m_pDoc->DeleteTab(0);
6810 }
6811
testExternalRef()6812 void TestFormula::testExternalRef()
6813 {
6814 ScDocShellRef xExtDocSh = new ScDocShell;
6815 xExtDocSh->SetIsInUcalc();
6816 OUString aExtDocName("file:///extdata.fake");
6817 OUString aExtSh1Name("Data1");
6818 OUString aExtSh2Name("Data2");
6819 OUString aExtSh3Name("Data3");
6820 SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6821 xExtDocSh->DoLoad(pMed);
6822 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6823 findLoadedDocShellByName(aExtDocName) != nullptr);
6824
6825 // Populate the external source document.
6826 ScDocument& rExtDoc = xExtDocSh->GetDocument();
6827 rExtDoc.InsertTab(0, aExtSh1Name);
6828 rExtDoc.InsertTab(1, aExtSh2Name);
6829 rExtDoc.InsertTab(2, aExtSh3Name);
6830
6831 OUString const name("Name");
6832 OUString const value("Value");
6833
6834 // Sheet 1
6835 rExtDoc.SetString(0, 0, 0, name);
6836 rExtDoc.SetString(0, 1, 0, "Andy");
6837 rExtDoc.SetString(0, 2, 0, "Bruce");
6838 rExtDoc.SetString(0, 3, 0, "Charlie");
6839 rExtDoc.SetString(0, 4, 0, "David");
6840 rExtDoc.SetString(1, 0, 0, value);
6841 double val = 10;
6842 rExtDoc.SetValue(1, 1, 0, val);
6843 val = 11;
6844 rExtDoc.SetValue(1, 2, 0, val);
6845 val = 12;
6846 rExtDoc.SetValue(1, 3, 0, val);
6847 val = 13;
6848 rExtDoc.SetValue(1, 4, 0, val);
6849
6850 // Sheet 2 remains empty.
6851
6852 // Sheet 3
6853 rExtDoc.SetString(0, 0, 2, name);
6854 rExtDoc.SetString(0, 1, 2, "Edward");
6855 rExtDoc.SetString(0, 2, 2, "Frank");
6856 rExtDoc.SetString(0, 3, 2, "George");
6857 rExtDoc.SetString(0, 4, 2, "Henry");
6858 rExtDoc.SetString(1, 0, 2, value);
6859 val = 99;
6860 rExtDoc.SetValue(1, 1, 2, val);
6861 val = 98;
6862 rExtDoc.SetValue(1, 2, 2, val);
6863 val = 97;
6864 rExtDoc.SetValue(1, 3, 2, val);
6865 val = 96;
6866 rExtDoc.SetValue(1, 4, 2, val);
6867
6868 // Test external references on the main document while the external
6869 // document is still in memory.
6870 m_pDoc->InsertTab(0, "Test Sheet");
6871 m_pDoc->SetString(0, 0, 0, "='file:///extdata.fake'#Data1.A1");
6872 OUString test = m_pDoc->GetString(0, 0, 0);
6873 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test, name);
6874
6875 // After the initial access to the external document, the external ref
6876 // manager should create sheet cache entries for *all* sheets from that
6877 // document. Note that the doc may have more than 3 sheets but ensure
6878 // that the first 3 are what we expect.
6879 ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
6880 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
6881 vector<OUString> aTabNames;
6882 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
6883 CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
6884 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[0], aExtSh1Name);
6885 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[1], aExtSh2Name);
6886 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[2], aExtSh3Name);
6887
6888 m_pDoc->SetString(1, 0, 0, "='file:///extdata.fake'#Data1.B1");
6889 test = m_pDoc->GetString(1, 0, 0);
6890 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test, value);
6891
6892 m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#Data1.A2");
6893 m_pDoc->SetString(0, 2, 0, "='file:///extdata.fake'#Data1.A3");
6894 m_pDoc->SetString(0, 3, 0, "='file:///extdata.fake'#Data1.A4");
6895 m_pDoc->SetString(0, 4, 0, "='file:///extdata.fake'#Data1.A5");
6896 m_pDoc->SetString(0, 5, 0, "='file:///extdata.fake'#Data1.A6");
6897
6898 {
6899 // Referencing an empty cell should display '0'.
6900 const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
6901 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6902 {
6903 test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
6904 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6905 }
6906 }
6907 m_pDoc->SetString(1, 1, 0, "='file:///extdata.fake'#Data1.B2");
6908 m_pDoc->SetString(1, 2, 0, "='file:///extdata.fake'#Data1.B3");
6909 m_pDoc->SetString(1, 3, 0, "='file:///extdata.fake'#Data1.B4");
6910 m_pDoc->SetString(1, 4, 0, "='file:///extdata.fake'#Data1.B5");
6911 m_pDoc->SetString(1, 5, 0, "='file:///extdata.fake'#Data1.B6");
6912 {
6913 double pChecks[] = { 10, 11, 12, 13, 0 };
6914 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6915 {
6916 m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
6917 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected cell value.", pChecks[i], val);
6918 }
6919 }
6920
6921 m_pDoc->SetString(2, 0, 0, "='file:///extdata.fake'#Data3.A1");
6922 m_pDoc->SetString(2, 1, 0, "='file:///extdata.fake'#Data3.A2");
6923 m_pDoc->SetString(2, 2, 0, "='file:///extdata.fake'#Data3.A3");
6924 m_pDoc->SetString(2, 3, 0, "='file:///extdata.fake'#Data3.A4");
6925 {
6926 const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
6927 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6928 {
6929 test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
6930 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6931 }
6932 }
6933
6934 m_pDoc->SetString(3, 0, 0, "='file:///extdata.fake'#Data3.B1");
6935 m_pDoc->SetString(3, 1, 0, "='file:///extdata.fake'#Data3.B2");
6936 m_pDoc->SetString(3, 2, 0, "='file:///extdata.fake'#Data3.B3");
6937 m_pDoc->SetString(3, 3, 0, "='file:///extdata.fake'#Data3.B4");
6938 {
6939 const char* pChecks[] = { "Value", "99", "98", "97" };
6940 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
6941 {
6942 test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
6943 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
6944 }
6945 }
6946
6947 // At this point, all accessed cell data from the external document should
6948 // have been cached.
6949 ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
6950 nFileId, aExtSh1Name, false);
6951 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab);
6952 ScRange aCachedRange = getCachedRange(pCacheTab);
6953 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6954 SCCOL(0), aCachedRange.aStart.Col());
6955 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6956 SCCOL(1), aCachedRange.aEnd.Col());
6957 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6958 SCROW(0), aCachedRange.aStart.Row());
6959 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6960 SCROW(4), aCachedRange.aEnd.Row());
6961
6962 // Sheet2 is not referenced at all; the cache table shouldn't even exist.
6963 pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
6964 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", !pCacheTab);
6965
6966 // Sheet3's row 5 is not referenced; it should not be cached.
6967 pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
6968 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab);
6969 aCachedRange = getCachedRange(pCacheTab);
6970 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6971 SCCOL(0), aCachedRange.aStart.Col());
6972 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6973 SCCOL(1), aCachedRange.aEnd.Col());
6974 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6975 SCROW(0), aCachedRange.aStart.Row());
6976 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cached data range.",
6977 SCROW(3), aCachedRange.aEnd.Row());
6978
6979 // Unload the external document shell.
6980 xExtDocSh->DoClose();
6981 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6982 !findLoadedDocShellByName(aExtDocName));
6983
6984 m_pDoc->DeleteTab(0);
6985 }
6986
testExternalRangeName()6987 void TestFormula::testExternalRangeName()
6988 {
6989 ScDocShellRef xExtDocSh = new ScDocShell;
6990 xExtDocSh->SetIsInUcalc();
6991 OUString const aExtDocName("file:///extdata.fake");
6992 SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
6993 xExtDocSh->DoLoad(pMed);
6994 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6995 findLoadedDocShellByName(aExtDocName) != nullptr);
6996
6997 ScDocument& rExtDoc = xExtDocSh->GetDocument();
6998 rExtDoc.InsertTab(0, "Data1");
6999 rExtDoc.SetValue(0, 0, 0, 123.456);
7000
7001 ScRangeName* pRangeName = rExtDoc.GetRangeName();
7002 ScRangeData* pRangeData = new ScRangeData(rExtDoc, "ExternalName",
7003 "$Data1.$A$1");
7004 pRangeName->insert(pRangeData);
7005
7006 m_pDoc->InsertTab(0, "Test Sheet");
7007 m_pDoc->SetString(0, 1, 0, "='file:///extdata.fake'#ExternalName");
7008
7009 double nVal = m_pDoc->GetValue(0, 1, 0);
7010 ASSERT_DOUBLES_EQUAL(123.456, nVal);
7011
7012 xExtDocSh->DoClose();
7013 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
7014 !findLoadedDocShellByName(aExtDocName));
7015 m_pDoc->DeleteTab(0);
7016 }
7017
testExtRefFuncT(ScDocument * pDoc,ScDocument & rExtDoc)7018 static void testExtRefFuncT(ScDocument* pDoc, ScDocument& rExtDoc)
7019 {
7020 clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7021 clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7022
7023 rExtDoc.SetString(0, 0, 0, "'1.2");
7024 rExtDoc.SetString(0, 1, 0, "Foo");
7025 rExtDoc.SetValue(0, 2, 0, 12.3);
7026 pDoc->SetString(0, 0, 0, "=T('file:///extdata.fake'#Data.A1)");
7027 pDoc->SetString(0, 1, 0, "=T('file:///extdata.fake'#Data.A2)");
7028 pDoc->SetString(0, 2, 0, "=T('file:///extdata.fake'#Data.A3)");
7029 pDoc->CalcAll();
7030
7031 OUString aRes = pDoc->GetString(0, 0, 0);
7032 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("1.2"), aRes);
7033 aRes = pDoc->GetString(0, 1, 0);
7034 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("Foo"), aRes);
7035 aRes = pDoc->GetString(0, 2, 0);
7036 CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
7037 }
7038
testExtRefFuncOFFSET(ScDocument * pDoc,ScDocument & rExtDoc)7039 static void testExtRefFuncOFFSET(ScDocument* pDoc, ScDocument& rExtDoc)
7040 {
7041 clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7042 clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7043
7044 sc::AutoCalcSwitch aACSwitch(*pDoc, true);
7045
7046 // External document has sheet named 'Data', and the internal doc has sheet named 'Test'.
7047 rExtDoc.SetValue(ScAddress(0,1,0), 1.2); // Set 1.2 to A2.
7048 pDoc->SetString(ScAddress(0,0,0), "=OFFSET('file:///extdata.fake'#Data.$A$1;1;0;1;1)");
7049 CPPUNIT_ASSERT_EQUAL(1.2, pDoc->GetValue(ScAddress(0,0,0)));
7050 }
7051
testExtRefFuncVLOOKUP(ScDocument * pDoc,ScDocument & rExtDoc)7052 static void testExtRefFuncVLOOKUP(ScDocument* pDoc, ScDocument& rExtDoc)
7053 {
7054 clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7055 clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7056
7057 // Populate the external document.
7058 rExtDoc.SetString(ScAddress(0,0,0), "A1");
7059 rExtDoc.SetString(ScAddress(0,1,0), "A2");
7060 rExtDoc.SetString(ScAddress(0,2,0), "A3");
7061 rExtDoc.SetString(ScAddress(0,3,0), "A4");
7062 rExtDoc.SetString(ScAddress(0,4,0), "A5");
7063
7064 rExtDoc.SetString(ScAddress(1,0,0), "B1");
7065 rExtDoc.SetString(ScAddress(1,1,0), "B2");
7066 rExtDoc.SetString(ScAddress(1,2,0), "B3");
7067 rExtDoc.SetString(ScAddress(1,3,0), "B4");
7068 rExtDoc.SetString(ScAddress(1,4,0), "B5");
7069
7070 // Put formula in the source document.
7071
7072 pDoc->SetString(ScAddress(0,0,0), "A2");
7073
7074 // Sort order TRUE
7075 pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;1)");
7076 CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
7077
7078 // Sort order FALSE. It should return the same result.
7079 pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;0)");
7080 CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
7081 }
7082
testExtRefConcat(ScDocument * pDoc,ScDocument & rExtDoc)7083 static void testExtRefConcat(ScDocument* pDoc, ScDocument& rExtDoc)
7084 {
7085 clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
7086 clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
7087
7088 sc::AutoCalcSwitch aACSwitch(*pDoc, true);
7089
7090 // String and number
7091 rExtDoc.SetString(ScAddress(0,0,0), "Answer: ");
7092 rExtDoc.SetValue(ScAddress(0,1,0), 42);
7093
7094 // Concat operation should combine string and number converted to string
7095 pDoc->SetString(ScAddress(0,0,0), "='file:///extdata.fake'#Data.A1 & 'file:///extdata.fake'#Data.A2");
7096 CPPUNIT_ASSERT_EQUAL(OUString("Answer: 42"), pDoc->GetString(ScAddress(0,0,0)));
7097 }
7098
testExternalRefFunctions()7099 void TestFormula::testExternalRefFunctions()
7100 {
7101 ScDocShellRef xExtDocSh = new ScDocShell;
7102 xExtDocSh->SetIsInUcalc();
7103 OUString aExtDocName("file:///extdata.fake");
7104 SfxMedium* pMed = new SfxMedium(aExtDocName, StreamMode::STD_READWRITE);
7105 xExtDocSh->DoLoad(pMed);
7106 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
7107 findLoadedDocShellByName(aExtDocName) != nullptr);
7108
7109 ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
7110 CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
7111 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
7112 const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
7113 CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
7114 pFileName);
7115 CPPUNIT_ASSERT_EQUAL_MESSAGE("file name registration has somehow failed.",
7116 aExtDocName, *pFileName);
7117
7118 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7119
7120 // Populate the external source document.
7121 ScDocument& rExtDoc = xExtDocSh->GetDocument();
7122 rExtDoc.InsertTab(0, "Data");
7123 double val = 1;
7124 rExtDoc.SetValue(0, 0, 0, val);
7125 // leave cell B1 empty.
7126 val = 2;
7127 rExtDoc.SetValue(0, 1, 0, val);
7128 rExtDoc.SetValue(1, 1, 0, val);
7129 val = 3;
7130 rExtDoc.SetValue(0, 2, 0, val);
7131 rExtDoc.SetValue(1, 2, 0, val);
7132 val = 4;
7133 rExtDoc.SetValue(0, 3, 0, val);
7134 rExtDoc.SetValue(1, 3, 0, val);
7135
7136 m_pDoc->InsertTab(0, "Test");
7137
7138 static const struct {
7139 const char* pFormula; double fResult;
7140 } aChecks[] = {
7141 { "=SUM('file:///extdata.fake'#Data.A1:A4)", 10 },
7142 { "=SUM('file:///extdata.fake'#Data.B1:B4)", 9 },
7143 { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
7144 { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
7145 { "=COUNT('file:///extdata.fake'#Data.A1:A4)", 4 },
7146 { "=COUNT('file:///extdata.fake'#Data.B1:B4)", 3 },
7147 // Should not crash, MUST be 0,m_pDoc->MaxRow() and/or 0,m_pDoc->MaxCol() range (here both)
7148 // to yield a result instead of 1x1 error matrix.
7149 { "=SUM('file:///extdata.fake'#Data.1:1048576)", 19 }
7150 };
7151
7152 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
7153 {
7154 m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
7155 m_pDoc->GetValue(0, 0, 0, val);
7156 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected result involving external ranges.", aChecks[i].fResult, val, 1e-15);
7157 }
7158
7159 // A huge external range should not crash, the matrix generated from the
7160 // external range reference should be 1x1 and have one error value.
7161 // XXX NOTE: in case we supported sparse matrix that can hold this large
7162 // areas these tests may be adapted.
7163 m_pDoc->SetString(0, 0, 0, "=SUM('file:///extdata.fake'#Data.B1:AMJ1048575)");
7164 ScFormulaCell* pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
7165 FormulaError nErr = pFC->GetErrCode();
7166 CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize), static_cast<int>(nErr));
7167
7168 ScMarkData aMark(m_pDoc->GetSheetLimits());
7169 aMark.SelectOneTable(0);
7170 m_pDoc->InsertMatrixFormula(0,0,0,0, aMark, "'file:///extdata.fake'#Data.B1:AMJ1048575");
7171 pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
7172 nErr = pFC->GetErrCode();
7173 CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize), static_cast<int>(nErr));
7174 SCSIZE nMatCols, nMatRows;
7175 const ScMatrix* pMat = pFC->GetMatrix();
7176 CPPUNIT_ASSERT_MESSAGE("matrix expected", pMat != nullptr);
7177 pMat->GetDimensions( nMatCols, nMatRows);
7178 CPPUNIT_ASSERT_EQUAL_MESSAGE("1x1 matrix expected", SCSIZE(1), nMatCols);
7179 CPPUNIT_ASSERT_EQUAL_MESSAGE("1x1 matrix expected", SCSIZE(1), nMatRows);
7180
7181 pRefMgr->clearCache(nFileId);
7182 testExtRefFuncT(m_pDoc, rExtDoc);
7183 testExtRefFuncOFFSET(m_pDoc, rExtDoc);
7184 testExtRefFuncVLOOKUP(m_pDoc, rExtDoc);
7185 testExtRefConcat(m_pDoc, rExtDoc);
7186
7187 // Unload the external document shell.
7188 xExtDocSh->DoClose();
7189 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
7190 !findLoadedDocShellByName(aExtDocName));
7191
7192 m_pDoc->DeleteTab(0);
7193 }
7194
testExternalRefUnresolved()7195 void TestFormula::testExternalRefUnresolved()
7196 {
7197 #if !defined(_WIN32) //FIXME
7198 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7199 m_pDoc->InsertTab(0, "Test");
7200
7201 // Test error propagation of unresolved (not existing document) external
7202 // references. Well, let's hope no build machine has such file with sheet...
7203
7204 std::vector<std::vector<const char*>> aData = {
7205 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1" },
7206 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23" },
7207 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\"" },
7208 { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7209 { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7210 { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7211 { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7212 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7213 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
7214 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23)" },
7215 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\")" },
7216 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=0" },
7217 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=\"\"" },
7218 { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1\")" },
7219 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2" },
7220 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23" },
7221 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\"" },
7222 { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7223 { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7224 { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7225 { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7226 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7227 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
7228 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23)" },
7229 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\")" },
7230 // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=0" },
7231 // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=\"\"" },
7232 { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2\")" },
7233 };
7234
7235 ScAddress aPos(0,0,0);
7236 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7237 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7238
7239 std::vector<std::vector<const char*>> aOutputCheck = {
7240 { "#REF!" }, // plain single ref
7241 { "#REF!" }, // +23
7242 { "#REF!" }, // &"W"
7243 { "FALSE" }, // ISREF
7244 { "TRUE" }, // ISERROR
7245 { "TRUE" }, // ISERR
7246 { "FALSE" }, // ISBLANK
7247 { "FALSE" }, // ISNUMBER
7248 { "FALSE" }, // ISTEXT
7249 { "FALSE" }, // ISNUMBER
7250 { "FALSE" }, // ISTEXT
7251 { "#REF!" }, // =0
7252 { "#REF!" }, // =""
7253 { "#REF!" }, // INDIRECT
7254 { "#REF!" }, // A1:A2 range
7255 { "#REF!" }, // +23
7256 { "#REF!" }, // &"W"
7257 { "FALSE" }, // ISREF
7258 { "TRUE" }, // ISERROR
7259 { "TRUE" }, // ISERR
7260 { "FALSE" }, // ISBLANK
7261 { "FALSE" }, // ISNUMBER
7262 { "FALSE" }, // ISTEXT
7263 { "FALSE" }, // ISNUMBER
7264 { "FALSE" }, // ISTEXT
7265 // TODO: gives Err:504 FIXME { "#REF!" }, // =0
7266 // TODO: gives Err:504 FIXME { "#REF!" }, // =""
7267 { "#REF!" }, // INDIRECT
7268 };
7269
7270 bool bSuccess = checkOutput(m_pDoc, aRange, aOutputCheck, "Check unresolved external reference.");
7271 CPPUNIT_ASSERT_MESSAGE("Unresolved reference check failed", bSuccess);
7272
7273 m_pDoc->DeleteTab(0);
7274 #endif
7275 }
7276
testMatrixOp()7277 void TestFormula::testMatrixOp()
7278 {
7279 m_pDoc->InsertTab(0, "Test");
7280
7281 for (SCROW nRow = 0; nRow < 4; ++nRow)
7282 {
7283 m_pDoc->SetValue(0, nRow, 0, nRow);
7284 }
7285 m_pDoc->SetValue(1, 0, 0, 2.0);
7286 m_pDoc->SetValue(3, 0, 0, 1.0);
7287 m_pDoc->SetValue(3, 1, 0, 2.0);
7288 m_pDoc->SetString(2, 0, 0, "=SUMPRODUCT((A1:A4)*B1+D1)");
7289 m_pDoc->SetString(2, 1, 0, "=SUMPRODUCT((A1:A4)*B1-D2)");
7290
7291 double nVal = m_pDoc->GetValue(2, 0, 0);
7292 CPPUNIT_ASSERT_EQUAL(16.0, nVal);
7293
7294 nVal = m_pDoc->GetValue(2, 1, 0);
7295 CPPUNIT_ASSERT_EQUAL(4.0, nVal);
7296
7297 m_pDoc->SetString(4, 0, 0, "=SUMPRODUCT({1;2;4}+8)");
7298 m_pDoc->SetString(4, 1, 0, "=SUMPRODUCT(8+{1;2;4})");
7299 m_pDoc->SetString(4, 2, 0, "=SUMPRODUCT({1;2;4}-8)");
7300 m_pDoc->SetString(4, 3, 0, "=SUMPRODUCT(8-{1;2;4})");
7301 m_pDoc->SetString(4, 4, 0, "=SUMPRODUCT({1;2;4}+{8;16;32})");
7302 m_pDoc->SetString(4, 5, 0, "=SUMPRODUCT({8;16;32}+{1;2;4})");
7303 m_pDoc->SetString(4, 6, 0, "=SUMPRODUCT({1;2;4}-{8;16;32})");
7304 m_pDoc->SetString(4, 7, 0, "=SUMPRODUCT({8;16;32}-{1;2;4})");
7305 double fResult[8] = { 31.0, 31.0, -17.0, 17.0, 63.0, 63.0, -49.0, 49.0 };
7306 for (size_t i = 0; i < SAL_N_ELEMENTS(fResult); ++i)
7307 {
7308 CPPUNIT_ASSERT_EQUAL( fResult[i], m_pDoc->GetValue(4, i, 0));
7309 }
7310
7311 m_pDoc->DeleteTab(0);
7312 }
7313
testFuncRangeOp()7314 void TestFormula::testFuncRangeOp()
7315 {
7316 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7317
7318 m_pDoc->InsertTab(0, "Sheet1");
7319 m_pDoc->InsertTab(1, "Sheet2");
7320 m_pDoc->InsertTab(2, "Sheet3");
7321
7322 // Sheet1.B1:B3
7323 m_pDoc->SetValue(1,0,0, 1.0);
7324 m_pDoc->SetValue(1,1,0, 2.0);
7325 m_pDoc->SetValue(1,2,0, 4.0);
7326 // Sheet2.B1:B3
7327 m_pDoc->SetValue(1,0,1, 8.0);
7328 m_pDoc->SetValue(1,1,1, 16.0);
7329 m_pDoc->SetValue(1,2,1, 32.0);
7330 // Sheet3.B1:B3
7331 m_pDoc->SetValue(1,0,2, 64.0);
7332 m_pDoc->SetValue(1,1,2, 128.0);
7333 m_pDoc->SetValue(1,2,2, 256.0);
7334
7335 // Range operator should extend concatenated literal references during
7336 // parse time already, so with this we can test ScComplexRefData::Extend()
7337
7338 // Current sheet is Sheet1, so B1:B2 implies relative Sheet1.B1:B2
7339
7340 ScAddress aPos(0,0,0);
7341 m_pDoc->SetString( aPos, "=SUM(B1:B2:B3)");
7342 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
7343 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
7344
7345 aPos.IncRow();
7346 m_pDoc->SetString( aPos, "=SUM(B1:B3:B2)");
7347 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
7348 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
7349
7350 aPos.IncRow();
7351 m_pDoc->SetString( aPos, "=SUM(B2:B3:B1)");
7352 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B1:B3)", "Wrong formula.");
7353 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(aPos));
7354
7355 aPos.IncRow();
7356 m_pDoc->SetString( aPos, "=SUM(Sheet2.B1:B2:B3)");
7357 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet2.B1:B3)", "Wrong formula.");
7358 CPPUNIT_ASSERT_EQUAL( 56.0, m_pDoc->GetValue(aPos));
7359
7360 aPos.IncRow();
7361 m_pDoc->SetString( aPos, "=SUM(B2:B2:Sheet1.B2)");
7362 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B2:B2)", "Wrong formula.");
7363 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(aPos));
7364
7365 aPos.IncRow();
7366 m_pDoc->SetString( aPos, "=SUM(B2:B3:Sheet2.B1)");
7367 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet2.B3)", "Wrong formula.");
7368 CPPUNIT_ASSERT_EQUAL( 63.0, m_pDoc->GetValue(aPos));
7369
7370 aPos.IncRow();
7371 m_pDoc->SetString( aPos, "=SUM(Sheet1.B1:Sheet2.B2:Sheet3.B3)");
7372 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
7373 CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc->GetValue(aPos));
7374
7375 // B1:Sheet2.B2 would be ambiguous, Sheet1.B1:Sheet2.B2 or Sheet2.B1:B2
7376 // The actual representation of the error case may change, so this test may
7377 // have to be adapted.
7378 aPos.IncRow();
7379 m_pDoc->SetString( aPos, "=SUM(B1:Sheet2.B2:Sheet3.B3)");
7380 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(#REF!.B2:#REF!.B3)", "Wrong formula.");
7381 CPPUNIT_ASSERT_EQUAL( OUString("#REF!"), m_pDoc->GetString(aPos));
7382
7383 aPos.IncRow();
7384 m_pDoc->SetString( aPos, "=SUM(Sheet1.B1:Sheet3.B2:Sheet2.B3)");
7385 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
7386 CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc->GetValue(aPos));
7387
7388 aPos.IncRow();
7389 m_pDoc->SetString( aPos, "=SUM(B$2:B$2:B2)");
7390 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(B$2:B2)", "Wrong formula.");
7391 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(aPos));
7392
7393 m_pDoc->DeleteTab(2);
7394 m_pDoc->DeleteTab(1);
7395 m_pDoc->DeleteTab(0);
7396 }
7397
testFuncFORMULA()7398 void TestFormula::testFuncFORMULA()
7399 {
7400 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7401
7402 m_pDoc->InsertTab(0, "Sheet1");
7403
7404 // Data in B1:D3
7405 std::vector<std::vector<const char*>> aData = {
7406 { "=A1", "=FORMULA(B1)", "=FORMULA(B1:B3)" },
7407 { nullptr, "=FORMULA(B2)", "=FORMULA(B1:B3)" },
7408 { "=A3", "=FORMULA(B3)", "=FORMULA(B1:B3)" },
7409 };
7410
7411 ScAddress aPos(1,0,0);
7412 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7413 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7414
7415 // Checks of C1:D3, where Cy==Dy, and D4:D6
7416 const char* aChecks[] = {
7417 "=A1",
7418 "#N/A",
7419 "=A3",
7420 };
7421 for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
7422 {
7423 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(2,i,0));
7424 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i,0));
7425 }
7426
7427 // Matrix in D4:D6, no intersection with B1:B3
7428 ScMarkData aMark(m_pDoc->GetSheetLimits());
7429 aMark.SelectOneTable(0);
7430 m_pDoc->InsertMatrixFormula(3, 3, 3, 5, aMark, "=FORMULA(B1:B3)");
7431 for (size_t i=0; i < SAL_N_ELEMENTS(aChecks); ++i)
7432 {
7433 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks[i]), m_pDoc->GetString(3,i+3,0));
7434 }
7435
7436 m_pDoc->DeleteTab(0);
7437 }
7438
testFuncTableRef()7439 void TestFormula::testFuncTableRef()
7440 {
7441 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
7442
7443 m_pDoc->InsertTab(0, "Sheet1");
7444 ScMarkData aMark(m_pDoc->GetSheetLimits());
7445 aMark.SelectOneTable(0);
7446 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
7447
7448 {
7449 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
7450 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
7451
7452 // Insert "table" database range definition for A1:B4, with default
7453 // HasHeader=true and HasTotals=false.
7454 std::unique_ptr<ScDBData> pData(new ScDBData( "table", 0,0,0, 1,3));
7455 bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
7456 CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"table\" database range.", bInserted);
7457 }
7458
7459 {
7460 // Populate "table" database range with headers and data in A1:B4
7461 std::vector<std::vector<const char*>> aData = {
7462 { "Header1", "Header2" },
7463 { "1", "2" },
7464 { "4", "8" },
7465 { "16", "32" }
7466 };
7467 ScAddress aPos(0,0,0);
7468 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7469 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7470 }
7471
7472 // Named expressions that use Table structured references.
7473 /* TODO: should the item/header separator really be equal to the parameter
7474 * separator, thus be locale dependent and ';' semicolon here, or should it
7475 * be a fixed ',' comma instead? */
7476 static const struct {
7477 const char* pName;
7478 const char* pExpr;
7479 const char* pCounta; // expected result when used in row 2 (first data row) as argument to COUNTA()
7480 const char* pSum3; // expected result when used in row 3 (second data row) as argument to SUM().
7481 const char* pSum4; // expected result when used in row 4 (third data row) as argument to SUM().
7482 const char* pSumX; // expected result when used in row 5 (non-intersecting) as argument to SUM().
7483 } aNames[] = {
7484 { "all", "table[[#All]]", "8", "63", "63", "63" },
7485 { "data_implicit", "table[]", "6", "63", "63", "63" },
7486 { "data", "table[[#Data]]", "6", "63", "63", "63" },
7487 { "headers", "table[[#Headers]]", "2", "0", "0", "0" },
7488 { "header1", "table[[Header1]]", "3", "21", "21", "21" },
7489 { "header2", "table[[Header2]]", "3", "42", "42", "42" },
7490 { "data_header1", "table[[#Data];[Header1]]", "3", "21", "21", "21" },
7491 { "data_header2", "table[[#Data];[Header2]]", "3", "42", "42", "42" },
7492 { "this_row", "table[[#This Row]]", "2", "12", "48", "#VALUE!" },
7493 { "this_row_header1", "table[[#This Row];[Header1]]", "1", "4", "16", "#VALUE!" },
7494 { "this_row_header2", "table[[#This Row];[Header2]]", "1", "8", "32", "#VALUE!" },
7495 { "this_row_range_header_1_to_2", "table[[#This Row];[Header1]:[Header2]]", "2", "12", "48", "#VALUE!" }
7496 };
7497
7498 {
7499 // Insert named expressions.
7500 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
7501 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
7502
7503 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7504 {
7505 // Choose base position that does not intersect with the database
7506 // range definition to test later use of [#This Row] results in
7507 // proper rows.
7508 ScRangeData* pName = new ScRangeData(
7509 *m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
7510 ScAddress(2,4,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
7511 bool bInserted = pGlobalNames->insert(pName);
7512 CPPUNIT_ASSERT_MESSAGE(
7513 OString(OString::Concat("Failed to insert named expression ") + aNames[i].pName +".").getStr(), bInserted);
7514 }
7515 }
7516
7517 // Use the named expressions in COUNTA() formulas, on row 2 that intersects.
7518 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7519 {
7520 OUString aFormula( "=COUNTA(" + OUString::createFromAscii( aNames[i].pName) + ")");
7521 ScAddress aPos(3+i,1,0);
7522 m_pDoc->SetString( aPos, aFormula);
7523 // For easier "debugability" have position and formula in assertion.
7524 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7525 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pCounta)),
7526 OUString(aPrefix + m_pDoc->GetString( aPos)));
7527 }
7528
7529 // Use the named expressions in SUM() formulas, on row 3 that intersects.
7530 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7531 {
7532 OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7533 ScAddress aPos(3+i,2,0);
7534 m_pDoc->SetString( aPos, aFormula);
7535 // For easier "debugability" have position and formula in assertion.
7536 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7537 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum3)),
7538 OUString(aPrefix + m_pDoc->GetString( aPos)));
7539 }
7540
7541 // Use the named expressions in SUM() formulas, on row 4 that intersects.
7542 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7543 {
7544 OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7545 ScAddress aPos(3+i,3,0);
7546 m_pDoc->SetString( aPos, aFormula);
7547 // For easier "debugability" have position and formula in assertion.
7548 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7549 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum4)),
7550 OUString(aPrefix + m_pDoc->GetString( aPos)));
7551 }
7552
7553 // Use the named expressions in SUM() formulas, on row 5 that does not intersect.
7554 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7555 {
7556 OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7557 ScAddress aPos(3+i,4,0);
7558 m_pDoc->SetString( aPos, aFormula);
7559 // For easier "debugability" have position and formula in assertion.
7560 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7561 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSumX)),
7562 OUString(aPrefix + m_pDoc->GetString( aPos)));
7563 }
7564
7565 // Insert a column at column B to extend database range from column A,B to
7566 // A,B,C. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7567 rDocFunc.InsertCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
7568
7569 // Re-verify the named expression in SUM() formula, on row 4 that
7570 // intersects, now starting at column E, still works.
7571 m_pDoc->CalcAll();
7572 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
7573 {
7574 OUString aFormula( "=SUM(" + OUString::createFromAscii( aNames[i].pName) + ")");
7575 ScAddress aPos(4+i,3,0);
7576 // For easier "debugability" have position and formula in assertion.
7577 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7578 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aNames[i].pSum4)),
7579 OUString(aPrefix + m_pDoc->GetString( aPos)));
7580 }
7581
7582 const char* pColumn2Formula = "=SUM(table[[#Data];[Column2]])";
7583 {
7584 // Populate "table" database range with empty header and data in newly
7585 // inserted column, B1:B4 plus a table formula in B6. The empty header
7586 // should result in the internal table column name "Column2" that is
7587 // used in the formula.
7588 std::vector<std::vector<const char*>> aData = {
7589 { "" },
7590 { "64" },
7591 { "128" },
7592 { "256" },
7593 { "" },
7594 { pColumn2Formula }
7595 };
7596 ScAddress aPos(1,0,0);
7597 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7598 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7599 }
7600
7601 // Verify the formula result in B6 (64+128+256=448).
7602 {
7603 OUString aFormula( OUString::createFromAscii( pColumn2Formula));
7604 ScAddress aPos(1,5,0);
7605 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7606 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + "448"), OUString(aPrefix + m_pDoc->GetString(aPos)));
7607 }
7608
7609 // Set header in column B. Use ScDocFunc to have table column names refreshed.
7610 rDocFunc.SetStringCell(ScAddress(1,0,0), "NewHeader",true);
7611 // Verify that formula adapted using the updated table column names.
7612 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader]])", "Wrong formula");
7613
7614 // Set header in column A to identical string. Internal table column name
7615 // for B should get a "2" appended.
7616 rDocFunc.SetStringCell(ScAddress(0,0,0), "NewHeader",true);
7617 // Verify that formula adapted using the updated table column names.
7618 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7619
7620 // Set header in column B to empty string, effectively clearing the cell.
7621 rDocFunc.SetStringCell(ScAddress(1,0,0), "",true);
7622 // Verify that formula is still using the previous table column name.
7623 ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7624
7625 // === header-less ===
7626
7627 {
7628 ScDBCollection* pDBs = m_pDoc->GetDBCollection();
7629 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
7630
7631 // Insert "headerless" database range definition for E10:F12, without headers.
7632 std::unique_ptr<ScDBData> pData(new ScDBData( "hltable", 0, 4,9, 5,11, true, false));
7633 bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
7634 CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"hltable\" database range.", bInserted);
7635 }
7636
7637 {
7638 // Populate "hltable" database range with data in E10:F12
7639 std::vector<std::vector<const char*>> aData = {
7640 { "1", "2" },
7641 { "4", "8" },
7642 { "16", "32" }
7643 };
7644 ScAddress aPos(4,9,0);
7645 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7646 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7647 }
7648
7649 // Named expressions that use header-less Table structured references.
7650 static const struct {
7651 const char* pName;
7652 const char* pExpr;
7653 const char* pCounta; // expected result when used in row 10 (first data row) as argument to COUNTA()
7654 const char* pSum3; // expected result when used in row 11 (second data row) as argument to SUM().
7655 const char* pSum4; // expected result when used in row 12 (third data row) as argument to SUM().
7656 const char* pSumX; // expected result when used in row 13 (non-intersecting) as argument to SUM().
7657 } aHlNames[] = {
7658 { "hl_all", "hltable[[#All]]", "6", "63", "63", "63" },
7659 { "hl_data_implicit", "hltable[]", "6", "63", "63", "63" },
7660 { "hl_data", "hltable[[#Data]]", "6", "63", "63", "63" },
7661 { "hl_headers", "hltable[[#Headers]]", "1", "#REF!", "#REF!", "#REF!" },
7662 { "hl_column1", "hltable[[Column1]]", "3", "21", "21", "21" },
7663 { "hl_column2", "hltable[[Column2]]", "3", "42", "42", "42" },
7664 { "hl_data_column1", "hltable[[#Data];[Column1]]", "3", "21", "21", "21" },
7665 { "hl_data_column2", "hltable[[#Data];[Column2]]", "3", "42", "42", "42" },
7666 { "hl_this_row", "hltable[[#This Row]]", "2", "12", "48", "#VALUE!" },
7667 { "hl_this_row_column1", "hltable[[#This Row];[Column1]]", "1", "4", "16", "#VALUE!" },
7668 { "hl_this_row_column2", "hltable[[#This Row];[Column2]]", "1", "8", "32", "#VALUE!" },
7669 { "hl_this_row_range_column_1_to_2", "hltable[[#This Row];[Column1]:[Column2]]", "2", "12", "48", "#VALUE!" }
7670 };
7671
7672 {
7673 // Insert named expressions.
7674 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
7675 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
7676
7677 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7678 {
7679 // Choose base position that does not intersect with the database
7680 // range definition to test later use of [#This Row] results in
7681 // proper rows.
7682 ScRangeData* pName = new ScRangeData(
7683 *m_pDoc, OUString::createFromAscii(aHlNames[i].pName), OUString::createFromAscii(aHlNames[i].pExpr),
7684 ScAddress(6,12,0), ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_NATIVE);
7685 bool bInserted = pGlobalNames->insert(pName);
7686 CPPUNIT_ASSERT_MESSAGE(
7687 OString(OString::Concat("Failed to insert named expression ") + aHlNames[i].pName +".").getStr(), bInserted);
7688 }
7689 }
7690
7691 // Use the named expressions in COUNTA() formulas, on row 10 that intersects.
7692 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7693 {
7694 OUString aFormula( "=COUNTA(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7695 ScAddress aPos(7+i,9,0);
7696 m_pDoc->SetString( aPos, aFormula);
7697 // For easier "debugability" have position and formula in assertion.
7698 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7699 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pCounta)),
7700 OUString(aPrefix + m_pDoc->GetString( aPos)));
7701 }
7702
7703 // Use the named expressions in SUM() formulas, on row 11 that intersects.
7704 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7705 {
7706 OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7707 ScAddress aPos(7+i,10,0);
7708 m_pDoc->SetString( aPos, aFormula);
7709 // For easier "debugability" have position and formula in assertion.
7710 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7711 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum3)),
7712 OUString(aPrefix + m_pDoc->GetString( aPos)));
7713 }
7714
7715 // Use the named expressions in SUM() formulas, on row 12 that intersects.
7716 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7717 {
7718 OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7719 ScAddress aPos(7+i,11,0);
7720 m_pDoc->SetString( aPos, aFormula);
7721 // For easier "debugability" have position and formula in assertion.
7722 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7723 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum4)),
7724 OUString(aPrefix + m_pDoc->GetString( aPos)));
7725 }
7726
7727 // Use the named expressions in SUM() formulas, on row 13 that does not intersect.
7728 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7729 {
7730 OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7731 ScAddress aPos(7+i,12,0);
7732 m_pDoc->SetString( aPos, aFormula);
7733 // For easier "debugability" have position and formula in assertion.
7734 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7735 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSumX)),
7736 OUString(aPrefix + m_pDoc->GetString( aPos)));
7737 }
7738
7739 // Insert a column at column F to extend database range from column E,F to
7740 // E,F,G. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7741 rDocFunc.InsertCells(ScRange(5,0,0,5,m_pDoc->MaxRow(),0), &aMark, INS_INSCOLS_BEFORE, false, true);
7742
7743 // Re-verify the named expression in SUM() formula, on row 12 that
7744 // intersects, now starting at column I, still works.
7745 m_pDoc->CalcAll();
7746 for (size_t i = 0; i < SAL_N_ELEMENTS(aHlNames); ++i)
7747 {
7748 OUString aFormula( "=SUM(" + OUString::createFromAscii( aHlNames[i].pName) + ")");
7749 ScAddress aPos(8+i,11,0);
7750 // For easier "debugability" have position and formula in assertion.
7751 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7752 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + OUString::createFromAscii( aHlNames[i].pSum4)),
7753 OUString(aPrefix + m_pDoc->GetString( aPos)));
7754 }
7755
7756 const char* pColumn3Formula = "=SUM(hltable[[#Data];[Column3]])";
7757 {
7758 // Populate "hltable" database range with data in newly inserted
7759 // column, F10:F12 plus a table formula in F14. The new header should
7760 // result in the internal table column name "Column3" that is used in
7761 // the formula.
7762 std::vector<std::vector<const char*>> aData = {
7763 { "64" },
7764 { "128" },
7765 { "256" },
7766 { "" },
7767 { pColumn3Formula }
7768 };
7769 ScAddress aPos(5,9,0);
7770 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
7771 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
7772 }
7773
7774 // Verify the formula result in F14 (64+128+256=448).
7775 {
7776 OUString aFormula( OUString::createFromAscii( pColumn3Formula));
7777 ScAddress aPos(5,13,0);
7778 OUString aPrefix( aPos.Format(ScRefFlags::VALID) + " " + aFormula + " : ");
7779 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix + "448"), OUString(aPrefix + m_pDoc->GetString(aPos)));
7780 }
7781
7782 m_pDoc->DeleteTab(0);
7783 }
7784
testFuncFTEST()7785 void TestFormula::testFuncFTEST()
7786 {
7787 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7788
7789 m_pDoc->InsertTab(0, "FTest");
7790
7791 ScAddress aPos(6,0,0);
7792 m_pDoc->SetString(aPos, "=FTEST(A1:C3;D1:F3)");
7793 m_pDoc->SetValue(0, 0, 0, 9.0); // A1
7794 OUString aVal = m_pDoc->GetString(aPos);
7795 CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7796 OUString("#VALUE!"), aVal);
7797 m_pDoc->SetValue(0, 1, 0, 8.0); // A2
7798 aVal = m_pDoc->GetString(aPos);
7799 CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7800 OUString("#VALUE!"), aVal);
7801 m_pDoc->SetValue(3, 0, 0, 5.0); // D1
7802 aVal = m_pDoc->GetString(aPos);
7803 CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7804 OUString("#VALUE!"), aVal);
7805 m_pDoc->SetValue(3, 1, 0, 6.0); // D2
7806 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 1.0000, m_pDoc->GetValue(aPos), 10e-4);
7807 m_pDoc->SetValue(1, 0, 0, 6.0); // B1
7808 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.6222, m_pDoc->GetValue(aPos), 10e-4);
7809 m_pDoc->SetValue(1, 1, 0, 8.0); // B2
7810 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.7732, m_pDoc->GetValue(aPos), 10e-4);
7811 m_pDoc->SetValue(4, 0, 0, 7.0); // E1
7812 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.8194, m_pDoc->GetValue(aPos), 10e-4);
7813 m_pDoc->SetValue(4, 1, 0, 4.0); // E2
7814 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9674, m_pDoc->GetValue(aPos), 10e-4);
7815 m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7816 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.3402, m_pDoc->GetValue(aPos), 10e-4);
7817 m_pDoc->SetValue(5, 0, 0, 28.0); // F1
7818 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc->GetValue(aPos), 10e-4);
7819 m_pDoc->SetValue(2, 1, 0, 9.0); // C2
7820 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0063, m_pDoc->GetValue(aPos), 10e-4);
7821 m_pDoc->SetValue(5, 1, 0, 4.0); // F2
7822 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0081, m_pDoc->GetValue(aPos), 10e-4);
7823 m_pDoc->SetValue(0, 2, 0, 2.0); // A3
7824 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0122, m_pDoc->GetValue(aPos), 10e-4);
7825 m_pDoc->SetValue(3, 2, 0, 8.0); // D3
7826 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0178, m_pDoc->GetValue(aPos), 10e-4);
7827 m_pDoc->SetValue(1, 2, 0, 4.0); // B3
7828 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0093, m_pDoc->GetValue(aPos), 10e-4);
7829 m_pDoc->SetValue(4, 2, 0, 7.0); // E3
7830 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0132, m_pDoc->GetValue(aPos), 10e-4);
7831 m_pDoc->SetValue(5, 2, 0, 5.0); // F3
7832 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0168, m_pDoc->GetValue(aPos), 10e-4);
7833 m_pDoc->SetValue(2, 2, 0, 13.0); // C3
7834 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0422, m_pDoc->GetValue(aPos), 10e-4);
7835
7836 m_pDoc->SetString(0, 2, 0, "a"); // A3
7837 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0334, m_pDoc->GetValue(aPos), 10e-4);
7838 m_pDoc->SetString(2, 0, 0, "b"); // C1
7839 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0261, m_pDoc->GetValue(aPos), 10e-4);
7840 m_pDoc->SetString(5, 1, 0, "c"); // F2
7841 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0219, m_pDoc->GetValue(aPos), 10e-4);
7842 m_pDoc->SetString(4, 2, 0, "d"); // E3
7843 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc->GetValue(aPos), 10e-4);
7844 m_pDoc->SetString(3, 2, 0, "e"); // D3
7845 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0110, m_pDoc->GetValue(aPos), 10e-4);
7846
7847 m_pDoc->DeleteTab(0);
7848 m_pDoc->InsertTab(0, "FTest2");
7849
7850 /* Summary of the following test
7851 A1:A5 = SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7852 A6:A10 = -SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7853 B1:B10 = SQRT(C2*19/20)*{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
7854 B11:B20 = -SQRT(C2*19/20)*{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
7855 C1 = POWER(1.5, D1) ; This is going to be the sample variance of the vector A1:A10
7856 C2 = POWER(1.5, D2) ; This is going to be the sample variance of the vector B1:B20
7857 D1 and D2 are varied over { -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 }
7858
7859 Result of FTEST(A1:A10;B1:B20) in Calc is compared with that from Octave's var_test() function for each value of D1 and D2.
7860
7861 The minimum variance ratio obtained in this way is 0.017342 and the maximum variance ratio is 57.665039
7862 */
7863
7864 const size_t nNumParams = 11;
7865 const double fParameter[nNumParams] = { -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
7866
7867 // Results of var_test() from Octave
7868 const double fResults[nNumParams][nNumParams] = {
7869 { 0.9451191535603041,0.5429768686792684,0.213130093422756,0.06607644828558357,0.0169804365506927,0.003790723514148109,
7870 0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06,7.495090956766148e-07 },
7871 { 0.4360331979746912,0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927,
7872 0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06 },
7873 { 0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.2131300934227565,0.06607644828558357,
7874 0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05 },
7875 { 0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603058,0.5429768686792689,0.2131300934227565,
7876 0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777 },
7877 { 0.002886791075972228,0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603041,0.5429768686792689,
7878 0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703 },
7879 { 0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746912,0.9451191535603036,
7880 0.5429768686792689,0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109 },
7881 { 1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,
7882 0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927 },
7883 { 5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,
7884 0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.213130093422756,0.06607644828558357 },
7885 { 1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,
7886 0.02453502500565086,0.1309752286653509,0.4360331979746914,0.9451191535603041,0.5429768686792684,0.2131300934227565 },
7887 { 5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,
7888 0.002886791075972228,0.02453502500565108,0.1309752286653516,0.4360331979746914,0.9451191535603058,0.5429768686792684 },
7889 { 1.354649725726631e-11,5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,
7890 0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,0.9451191535603054 }
7891 };
7892
7893 m_pDoc->SetValue(3, 0, 0, fParameter[0]); // D1
7894 m_pDoc->SetValue(3, 1, 0, fParameter[0]); // D2
7895 aPos.Set(2,0,0); // C1
7896 m_pDoc->SetString(aPos, "=POWER(1.5;D1)" ); // C1
7897 aPos.Set(2, 1, 0); // C2
7898 m_pDoc->SetString(aPos, "=POWER(1.5;D2)" ); // C2
7899 for ( SCROW nRow = 0; nRow < 5; ++nRow ) // Set A1:A5 = SQRT(C1*9/10), and A6:A10 = -SQRT(C1*9/10)
7900 {
7901 aPos.Set(0, nRow, 0);
7902 m_pDoc->SetString(aPos, "=SQRT(C1*9/10)");
7903 aPos.Set(0, nRow + 5, 0);
7904 m_pDoc->SetString(aPos, "=-SQRT(C1*9/10)");
7905 }
7906
7907 for ( SCROW nRow = 0; nRow < 10; ++nRow ) // Set B1:B10 = SQRT(C2*19/20), and B11:B20 = -SQRT(C2*19/20)
7908 {
7909 aPos.Set(1, nRow, 0);
7910 m_pDoc->SetString(aPos, "=SQRT(C2*19/20)");
7911 aPos.Set(1, nRow + 10, 0);
7912 m_pDoc->SetString(aPos, "=-SQRT(C2*19/20)");
7913 }
7914
7915 aPos.Set(4, 0, 0); // E1
7916 m_pDoc->SetString(aPos, "=FTEST(A1:A10;B1:B20)");
7917 aPos.Set(4, 1, 0); // E2
7918 m_pDoc->SetString(aPos, "=FTEST(B1:B20;A1:A10)");
7919
7920 ScAddress aPosRev(4, 1, 0); // E2
7921 aPos.Set(4, 0, 0); // E1
7922
7923 for ( size_t nFirstIdx = 0; nFirstIdx < nNumParams; ++nFirstIdx )
7924 {
7925 m_pDoc->SetValue(3, 0, 0, fParameter[nFirstIdx]); // Set D1
7926 for ( size_t nSecondIdx = 0; nSecondIdx < nNumParams; ++nSecondIdx )
7927 {
7928 m_pDoc->SetValue(3, 1, 0, fParameter[nSecondIdx]); // Set D2
7929 double fExpected = fResults[nFirstIdx][nSecondIdx];
7930 // Here a dynamic error limit is used. This is to handle correctly when the expected value is lower than the fixed error limit of 10e-5
7931 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected, m_pDoc->GetValue(aPos), std::min(10e-5, fExpected*0.0001) );
7932 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected, m_pDoc->GetValue(aPosRev), std::min(10e-5, fExpected*0.0001) );
7933 }
7934 }
7935 m_pDoc->DeleteTab(0);
7936 }
7937
testFuncFTESTBug()7938 void TestFormula::testFuncFTESTBug()
7939 {
7940 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7941
7942 m_pDoc->InsertTab(0, "FTest");
7943
7944 ScAddress aPos(9,0,0);
7945 m_pDoc->SetString(aPos, "=FTEST(H1:H3;I1:I3)");
7946
7947 m_pDoc->SetValue(7, 0, 0, 9.0); // H1
7948 m_pDoc->SetValue(7, 1, 0, 8.0); // H2
7949 m_pDoc->SetValue(7, 2, 0, 6.0); // H3
7950 m_pDoc->SetValue(8, 0, 0, 5.0); // I1
7951 m_pDoc->SetValue(8, 1, 0, 7.0); // I2
7952 // tdf#93329
7953 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9046, m_pDoc->GetValue(aPos), 10e-4);
7954
7955 m_pDoc->DeleteTab(0);
7956 }
7957
testFuncCHITEST()7958 void TestFormula::testFuncCHITEST()
7959 {
7960 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
7961
7962 m_pDoc->InsertTab(0, "ChiTest");
7963
7964 ScAddress aPos(6,0,0);
7965 // 2x2 matrices test
7966 m_pDoc->SetString(aPos, "=CHITEST(A1:B2;D1:E2)");
7967 OUString aVal = m_pDoc->GetString(aPos);
7968 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with empty cells",
7969 OUString("Err:502"), aVal);
7970
7971 m_pDoc->SetValue(0, 0, 0, 1.0); // A1
7972 m_pDoc->SetValue(0, 1, 0, 2.0); // A2
7973 m_pDoc->SetValue(1, 0, 0, 2.0); // B1
7974 m_pDoc->SetValue(1, 1, 0, 1.0); // B2
7975 aVal = m_pDoc->GetString(aPos);
7976 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrix with empty cells",
7977 OUString("Err:502"), aVal);
7978
7979 m_pDoc->SetValue(3, 0, 0, 2.0); // D1
7980 m_pDoc->SetValue(3, 1, 0, 3.0); // D2
7981 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc->GetValue(aPos), 10e-4);
7982
7983 m_pDoc->SetValue(4, 1, 0, 1.0); // E2
7984 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc->GetValue(aPos), 10e-4);
7985 m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7986 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.2801, m_pDoc->GetValue(aPos), 10e-4);
7987 m_pDoc->SetValue(4, 0, 0, 0.0); // E1
7988 aVal = m_pDoc->GetString(aPos);
7989 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0 for expected values of 0", OUString("#DIV/0!"), aVal);
7990 m_pDoc->SetValue(4, 0, 0, 3.0); // E1
7991 m_pDoc->SetValue(1, 1, 0, 0.0); // B2
7992 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1410, m_pDoc->GetValue(aPos), 10e-4);
7993
7994 // 3x3 matrices test
7995 m_pDoc->SetString(aPos, "=CHITEST(A1:C3;D1:F3)");
7996 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.7051, m_pDoc->GetValue(aPos), 10e-4);
7997
7998 m_pDoc->SetValue(2, 0, 0, 3.0); // C1
7999 m_pDoc->SetValue(2, 1, 0, 2.0); // C2
8000 m_pDoc->SetValue(2, 2, 0, 3.0); // C3
8001 m_pDoc->SetValue(0, 2, 0, 4.0); // A3
8002 m_pDoc->SetValue(1, 2, 0, 2.0); // B3
8003 m_pDoc->SetValue(5, 0, 0, 1.0); // F1
8004 m_pDoc->SetValue(5, 1, 0, 2.0); // F2
8005 m_pDoc->SetValue(5, 2, 0, 3.0); // F3
8006 m_pDoc->SetValue(3, 2, 0, 3.0); // D3
8007 m_pDoc->SetValue(4, 2, 0, 1.0); // E3
8008 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc->GetValue(aPos), 10e-4);
8009
8010 // test with strings
8011 m_pDoc->SetString(4, 2, 0, "a"); // E3
8012 aVal = m_pDoc->GetString(aPos);
8013 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
8014 OUString("Err:502"), aVal);
8015 m_pDoc->SetString(1, 2, 0, "a"); // B3
8016 aVal = m_pDoc->GetString(aPos);
8017 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
8018 OUString("Err:502"), aVal);
8019 m_pDoc->SetValue(4, 2, 0, 1.0); // E3
8020 aVal = m_pDoc->GetString(aPos);
8021 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
8022 OUString("Err:502"), aVal);
8023 m_pDoc->SetValue(1, 2, 0, 2.0); // B3
8024 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc->GetValue(aPos), 10e-4);
8025
8026 m_pDoc->SetValue(4, 1, 0, 5.0); // E2
8027 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0215, m_pDoc->GetValue(aPos), 10e-4);
8028 m_pDoc->SetValue(1, 2, 0, 1.0); // B3
8029 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0328, m_pDoc->GetValue(aPos), 10e-4);
8030 m_pDoc->SetValue(5, 0, 0, 3.0); // F1
8031 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1648, m_pDoc->GetValue(aPos), 10e-4);
8032 m_pDoc->SetValue(0, 1, 0, 3.0); // A2
8033 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1870, m_pDoc->GetValue(aPos), 10e-4);
8034 m_pDoc->SetValue(3, 1, 0, 5.0); // D2
8035 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1377, m_pDoc->GetValue(aPos), 10e-4);
8036 m_pDoc->SetValue(3, 2, 0, 4.0); // D3
8037 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1566, m_pDoc->GetValue(aPos), 10e-4);
8038
8039 m_pDoc->SetValue(0, 0, 0, 0.0); // A1
8040 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0868, m_pDoc->GetValue(aPos), 10e-4);
8041
8042 // no convergence error
8043 m_pDoc->SetValue(4, 0, 0, 1.0E308); // E1
8044 aVal = m_pDoc->GetString(aPos);
8045 CPPUNIT_ASSERT_EQUAL(OUString("Err:523"), aVal);
8046 m_pDoc->SetValue(4, 0, 0, 3.0); // E1
8047
8048 // zero in all cells
8049 m_pDoc->SetValue(0, 1, 0, 0.0); // A2
8050 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0150, m_pDoc->GetValue(aPos), 10e-4);
8051 m_pDoc->SetValue(0, 2, 0, 0.0); // A3
8052 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0026, m_pDoc->GetValue(aPos), 10e-4);
8053 m_pDoc->SetValue(1, 0, 0, 0.0); // B1
8054 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.00079, m_pDoc->GetValue(aPos), 10e-5);
8055 m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8056 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0005, m_pDoc->GetValue(aPos), 10e-4);
8057 m_pDoc->SetValue(2, 0, 0, 0.0); // C1
8058 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0001, m_pDoc->GetValue(aPos), 10e-4);
8059 m_pDoc->SetValue(2, 1, 0, 0.0); // C2
8060 m_pDoc->SetValue(2, 2, 0, 0.0); // C3
8061 m_pDoc->SetValue(3, 0, 0, 0.0); // D1
8062 aVal = m_pDoc->GetString(aPos);
8063 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
8064 OUString("#DIV/0!"), aVal);
8065 m_pDoc->SetValue(3, 1, 0, 0.0); // D2
8066 m_pDoc->SetValue(3, 2, 0, 0.0); // D3
8067 m_pDoc->SetValue(4, 0, 0, 0.0); // E1
8068 m_pDoc->SetValue(4, 1, 0, 0.0); // E2
8069 m_pDoc->SetValue(4, 2, 0, 0.0); // E3
8070 m_pDoc->SetValue(5, 0, 0, 0.0); // F1
8071 m_pDoc->SetValue(5, 1, 0, 0.0); // F2
8072 m_pDoc->SetValue(5, 2, 0, 0.0); // F3
8073 aVal = m_pDoc->GetString(aPos);
8074 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
8075 OUString("#DIV/0!"), aVal);
8076
8077 m_pDoc->DeleteTab(0);
8078 }
8079
testFuncTTEST()8080 void TestFormula::testFuncTTEST()
8081 {
8082 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8083
8084 m_pDoc->InsertTab(0, "TTest");
8085
8086 ScAddress aPos(6,0,0);
8087 // type 1, mode/tails 1
8088 m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;1;1)");
8089 OUString aVal = m_pDoc->GetString(aPos);
8090 CPPUNIT_ASSERT_EQUAL_MESSAGE("TTEST should return #VALUE! for empty matrices",
8091 OUString("#VALUE!"), aVal);
8092
8093 m_pDoc->SetValue(0, 0, 0, 8.0); // A1
8094 m_pDoc->SetValue(1, 0, 0, 2.0); // B1
8095 m_pDoc->SetValue(3, 0, 0, 3.0); // D1
8096 m_pDoc->SetValue(4, 0, 0, 1.0); // E1
8097 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc->GetValue(aPos), 10e-5);
8098 m_pDoc->SetValue(2, 0, 0, 1.0); // C1
8099 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc->GetValue(aPos), 10e-5);
8100 m_pDoc->SetValue(5, 0, 0, 6.0); // F1
8101 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc->GetValue(aPos), 10e-5);
8102 m_pDoc->SetValue(0, 1, 0, -4.0); // A2
8103 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc->GetValue(aPos), 10e-5);
8104 m_pDoc->SetValue(3, 1, 0, 1.0); // D2
8105 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc->GetValue(aPos), 10e-5);
8106 m_pDoc->SetValue(1, 1, 0, 5.0); // B2
8107 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc->GetValue(aPos), 10e-5);
8108 m_pDoc->SetValue(4, 1, 0, -2.0); // E2
8109 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc->GetValue(aPos), 10e-5);
8110 m_pDoc->SetValue(2, 1, 0, -1.0); // C2
8111 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc->GetValue(aPos), 10e-5);
8112 m_pDoc->SetValue(5, 1, 0, -3.0); // F2
8113 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc->GetValue(aPos), 10e-5);
8114 m_pDoc->SetValue(0, 2, 0, 10.0); // A3
8115 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc->GetValue(aPos), 10e-5);
8116 m_pDoc->SetValue(3, 2, 0, 10.0); // D3
8117 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc->GetValue(aPos), 10e-5);
8118 m_pDoc->SetValue(1, 2, 0, 3.0); // B3
8119 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc->GetValue(aPos), 10e-5);
8120 m_pDoc->SetValue(4, 2, 0, 9.0); // E3
8121 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc->GetValue(aPos), 10e-5);
8122 m_pDoc->SetValue(2, 2, 0, -5.0); // C3
8123 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc->GetValue(aPos), 10e-5);
8124 m_pDoc->SetValue(5, 2, 0, 6.0); // F3
8125 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25529, m_pDoc->GetValue(aPos), 10e-5);
8126
8127 m_pDoc->SetString(1, 1, 0, "a"); // B2
8128 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc->GetValue(aPos), 10e-5);
8129 m_pDoc->SetString(4, 1, 0, "b"); // E2
8130 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc->GetValue(aPos), 10e-5);
8131 m_pDoc->SetString(2, 2, 0, "c"); // C3
8132 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25030, m_pDoc->GetValue(aPos), 10e-5);
8133 m_pDoc->SetString(5, 1, 0, "d"); // F2
8134 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.19637, m_pDoc->GetValue(aPos), 10e-5);
8135
8136 // type 1, mode/tails 2
8137 m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;2;1)");
8138 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc->GetValue(aPos), 10e-5);
8139 m_pDoc->SetValue(1, 1, 0, 4.0); // B2
8140 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc->GetValue(aPos), 10e-5);
8141 m_pDoc->SetValue(4, 1, 0, 3.0); // E2
8142 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43970, m_pDoc->GetValue(aPos), 10e-5);
8143 m_pDoc->SetValue(2, 2, 0, -2.0); // C3
8144 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22217, m_pDoc->GetValue(aPos), 10e-5);
8145 m_pDoc->SetValue(5, 1, 0, -10.0); // F2
8146 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.64668, m_pDoc->GetValue(aPos), 10e-5);
8147 m_pDoc->SetValue(0, 1, 0, 3.0); // A2
8148 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.95266, m_pDoc->GetValue(aPos), 10e-5);
8149 m_pDoc->SetValue(3, 2, 0, -1.0); // D3
8150 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62636, m_pDoc->GetValue(aPos), 10e-5);
8151
8152 // type 2, mode/tails 2
8153 m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;2;2)");
8154 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62549, m_pDoc->GetValue(aPos), 10e-5);
8155 m_pDoc->SetValue(5, 1, 0, -1.0); // F2
8156 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.94952, m_pDoc->GetValue(aPos), 10e-5);
8157 m_pDoc->SetValue(2, 2, 0, 5.0); // C3
8158 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.58876, m_pDoc->GetValue(aPos), 10e-5);
8159 m_pDoc->SetValue(2, 1, 0, 2.0); // C2
8160 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43205, m_pDoc->GetValue(aPos), 10e-5);
8161 m_pDoc->SetValue(3, 2, 0, -4.0); // D3
8162 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36165, m_pDoc->GetValue(aPos), 10e-5);
8163 m_pDoc->SetValue(0, 1, 0, 1.0); // A2
8164 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.44207, m_pDoc->GetValue(aPos), 10e-5);
8165
8166 // type 3, mode/tails 1
8167 m_pDoc->SetString(aPos, "=TTEST(A1:C3;D1:F3;1;3)");
8168 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22132, m_pDoc->GetValue(aPos), 10e-5);
8169 m_pDoc->SetValue(0, 0, 0, 1.0); // A1
8170 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36977, m_pDoc->GetValue(aPos), 10e-5);
8171 m_pDoc->SetValue(0, 2, 0, -30.0); // A3
8172 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16871, m_pDoc->GetValue(aPos), 10e-5);
8173 m_pDoc->SetValue(3, 1, 0, 5.0); // D2
8174 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.14396, m_pDoc->GetValue(aPos), 10e-5);
8175 m_pDoc->SetValue(5, 1, 0, 2.0); // F2
8176 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12590, m_pDoc->GetValue(aPos), 10e-5);
8177 m_pDoc->SetValue(4, 2, 0, 2.0); // E3
8178 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16424, m_pDoc->GetValue(aPos), 10e-5);
8179 m_pDoc->SetValue(5, 0, 0, -1.0); // F1
8180 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.21472, m_pDoc->GetValue(aPos), 10e-5);
8181
8182 m_pDoc->DeleteTab(0);
8183 }
8184
testFuncSUMX2PY2()8185 void TestFormula::testFuncSUMX2PY2()
8186 {
8187 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8188
8189 m_pDoc->InsertTab(0, "SumX2PY2 Test");
8190
8191 OUString aVal;
8192 ScAddress aPos(6,0,0);
8193 m_pDoc->SetString(aPos, "=SUMX2PY2(A1:C3;D1:F3)");
8194 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc->GetValue(aPos));
8195
8196 m_pDoc->SetValue(0, 0, 0, 1.0); // A1
8197 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc->GetValue(aPos));
8198 m_pDoc->SetValue(3, 0, 0, 2.0); // D1
8199 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc->GetValue(aPos));
8200 m_pDoc->SetValue(1, 0, 0, 2.0); // B1
8201 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc->GetValue(aPos));
8202 m_pDoc->SetValue(4, 0, 0, 0.0); // E1
8203 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 9.0, m_pDoc->GetValue(aPos));
8204 m_pDoc->SetValue(2, 0, 0, 3.0); // C1
8205 m_pDoc->SetValue(5, 0, 0, 3.0); // F1
8206 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 27.0, m_pDoc->GetValue(aPos));
8207 m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8208 m_pDoc->SetValue(3, 1, 0, -10.0); // D2
8209 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc->GetValue(aPos));
8210 m_pDoc->SetValue(1, 1, 0, -5.0); // B2
8211 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc->GetValue(aPos));
8212 m_pDoc->SetValue(4, 1, 0, -5.0); // E2
8213 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc->GetValue(aPos));
8214 m_pDoc->SetValue(2, 1, 0, 0.0); // C2
8215 m_pDoc->SetValue(5, 1, 0, 0.0); // F2
8216 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc->GetValue(aPos));
8217 m_pDoc->SetValue(0, 2, 0, -8.0); // A3
8218 m_pDoc->SetValue(3, 2, 0, 8.0); // D3
8219 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc->GetValue(aPos));
8220 m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8221 m_pDoc->SetValue(4, 2, 0, 0.0); // E3
8222 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc->GetValue(aPos));
8223 m_pDoc->SetValue(2, 2, 0, 1.0); // C3
8224 m_pDoc->SetValue(5, 2, 0, 1.0); // F3
8225 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 407.0, m_pDoc->GetValue(aPos));
8226
8227 // add some strings
8228 m_pDoc->SetString(4, 1, 0, "a"); // E2
8229 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc->GetValue(aPos));
8230 m_pDoc->SetString(1, 1, 0, "a"); // B2
8231 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc->GetValue(aPos));
8232 m_pDoc->SetString(0, 0, 0, "a"); // A1
8233 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc->GetValue(aPos));
8234 m_pDoc->SetString(3, 0, 0, "a"); // D1
8235 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc->GetValue(aPos));
8236
8237 m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3};{2;3;4})");
8238 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 43.0, m_pDoc->GetValue(aPos));
8239 m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3};{2;3})");
8240 aVal = m_pDoc->GetString(aPos);
8241 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 should return #VALUE! for matrices with different sizes",
8242 OUString("#VALUE!"), aVal);
8243 m_pDoc->SetString(aPos, "=SUMX2PY2({1;2;3})");
8244 aVal = m_pDoc->GetString(aPos);
8245 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 needs two parameters",
8246 OUString("Err:511"), aVal);
8247
8248 m_pDoc->DeleteTab(0);
8249 }
8250
testFuncSUMX2MY2()8251 void TestFormula::testFuncSUMX2MY2()
8252 {
8253 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8254
8255 m_pDoc->InsertTab(0, "SumX2MY2 Test");
8256
8257 OUString aVal;
8258 ScAddress aPos(6,0,0);
8259 m_pDoc->SetString(aPos, "=SUMX2MY2(A1:C3;D1:F3)");
8260 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc->GetValue(aPos));
8261
8262 m_pDoc->SetValue(0, 0, 0, 10.0); // A1
8263 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc->GetValue(aPos));
8264 m_pDoc->SetValue(3, 0, 0, -9.0); // D1
8265 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc->GetValue(aPos));
8266 m_pDoc->SetValue(1, 0, 0, 2.0); // B1
8267 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc->GetValue(aPos));
8268 m_pDoc->SetValue(4, 0, 0, 1.0); // E1
8269 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8270 m_pDoc->SetValue(2, 0, 0, 3.0); // C1
8271 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8272 m_pDoc->SetValue(5, 0, 0, 3.0); // F1
8273 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8274 m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8275 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8276 m_pDoc->SetValue(3, 1, 0, -10.0); // D2
8277 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8278 m_pDoc->SetValue(1, 1, 0, -5.0); // B2
8279 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8280 m_pDoc->SetValue(4, 1, 0, -5.0); // E2
8281 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8282 m_pDoc->SetValue(2, 1, 0, -3.0); // C2
8283 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8284 m_pDoc->SetValue(5, 1, 0, 3.0); // F2
8285 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8286 m_pDoc->SetValue(0, 2, 0, -8.0); // A3
8287 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc->GetValue(aPos));
8288 m_pDoc->SetValue(3, 2, 0, 3.0); // D3
8289 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc->GetValue(aPos));
8290 m_pDoc->SetValue(1, 2, 0, 2.0); // B3
8291 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc->GetValue(aPos));
8292 m_pDoc->SetValue(4, 2, 0, -6.0); // E3
8293 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
8294 m_pDoc->SetValue(2, 2, 0, -4.0); // C3
8295 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
8296 m_pDoc->SetValue(5, 2, 0, 6.0); // F3
8297 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 25.0, m_pDoc->GetValue(aPos));
8298
8299 // add some strings
8300 m_pDoc->SetString(5, 2, 0, "a"); // F3
8301 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc->GetValue(aPos));
8302 m_pDoc->SetString(0, 2, 0, "a"); // A3
8303 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -10.0, m_pDoc->GetValue(aPos));
8304 m_pDoc->SetString(1, 0, 0, "a"); // B1
8305 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -13.0, m_pDoc->GetValue(aPos));
8306 m_pDoc->SetString(3, 0, 0, "a"); // D1
8307 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -32.0, m_pDoc->GetValue(aPos));
8308
8309 m_pDoc->SetString(aPos, "=SUMX2MY2({1;3;5};{0;4;4})");
8310 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc->GetValue(aPos));
8311 m_pDoc->SetString(aPos, "=SUMX2MY2({1;-3;-5};{0;-4;4})");
8312 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc->GetValue(aPos));
8313 m_pDoc->SetString(aPos, "=SUMX2MY2({9;5;1};{3;-3;3})");
8314 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 80.0, m_pDoc->GetValue(aPos));
8315 m_pDoc->SetString(aPos, "=SUMX2MY2({1;2;3};{2;3})");
8316 aVal = m_pDoc->GetString(aPos);
8317 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 should return #VALUE! for matrices with different sizes",
8318 OUString("#VALUE!"), aVal);
8319 m_pDoc->SetString(aPos, "=SUMX2MY2({1;2;3})");
8320 aVal = m_pDoc->GetString(aPos);
8321 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 needs two parameters",
8322 OUString("Err:511"), aVal);
8323
8324 m_pDoc->DeleteTab(0);
8325 }
8326
testFuncGCD()8327 void TestFormula::testFuncGCD()
8328 {
8329 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8330
8331 m_pDoc->InsertTab(0, "GCDTest");
8332
8333 OUString aVal;
8334 ScAddress aPos(4,0,0);
8335
8336 m_pDoc->SetString(aPos, "=GCD(A1)");
8337 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc->GetValue(aPos));
8338 m_pDoc->SetValue(0, 0, 0, 10.0); // A1
8339 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
8340 m_pDoc->SetValue(0, 0, 0, -2.0); // A1
8341 aVal = m_pDoc->GetString(aPos);
8342 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for values less than 0",
8343 OUString("Err:502"), aVal);
8344 m_pDoc->SetString(0, 0, 0, "a"); // A1
8345 aVal = m_pDoc->GetString(aPos);
8346 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for a single string",
8347 OUString("#VALUE!"), aVal);
8348
8349 m_pDoc->SetString(aPos, "=GCD(A1:B2)");
8350 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc->GetValue(aPos));
8351 m_pDoc->SetValue(0, 1, 0, -12.0); // B1
8352 aVal = m_pDoc->GetString(aPos);
8353 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for a matrix with values less than 0",
8354 OUString("Err:502"), aVal);
8355 m_pDoc->SetValue(0, 0, 0, 15.0); // A1
8356 m_pDoc->SetValue(0, 1, 0, 0.0); // B1
8357 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 15.0, m_pDoc->GetValue(aPos));
8358 m_pDoc->SetValue(1, 0, 0, 5.0); // B1
8359 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8360 m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8361 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8362 m_pDoc->SetValue(1, 0, 0, 30.0); // B1
8363 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8364 m_pDoc->SetValue(0, 0, 0, 20.0); // A1
8365 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
8366 m_pDoc->SetValue(1, 1, 0, 120.0); // B2
8367 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc->GetValue(aPos));
8368 m_pDoc->SetValue(0, 1, 0, 80.0); // A2
8369 m_pDoc->SetValue(1, 0, 0, 40.0); // B1
8370 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 20.0, m_pDoc->GetValue(aPos));
8371 m_pDoc->SetValue(1, 0, 0, 45.0); // B1
8372 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8373
8374 // with floor
8375 m_pDoc->SetValue(1, 0, 0, 45.381); // B1
8376 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8377 m_pDoc->SetValue(1, 1, 0, 120.895); // B2
8378 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8379 m_pDoc->SetValue(0, 0, 0, 20.97); // A1
8380 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8381 m_pDoc->SetValue(0, 1, 0, 10.15); // A2
8382 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc->GetValue(aPos));
8383
8384 // inline array
8385 m_pDoc->SetString(aPos, "=GCD({3;6;9})");
8386 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc->GetValue(aPos));
8387 m_pDoc->SetString(aPos, "=GCD({150;0})");
8388 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc->GetValue(aPos));
8389 m_pDoc->SetString(aPos, "=GCD({-3;6;9})");
8390 aVal = m_pDoc->GetString(aPos);
8391 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
8392 OUString("Err:502"), aVal);
8393 m_pDoc->SetString(aPos, "=GCD({\"a\";6;9})");
8394 aVal = m_pDoc->GetString(aPos);
8395 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
8396 OUString("Err:502"), aVal);
8397
8398 //many inline array
8399 m_pDoc->SetString(aPos, "=GCD({6;6;6};{3;6;9})");
8400 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc->GetValue(aPos));
8401 m_pDoc->SetString(aPos, "=GCD({300;300;300};{150;0})");
8402 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc->GetValue(aPos));
8403 m_pDoc->SetString(aPos,"=GCD({3;6;9};{3;-6;9})");
8404 aVal = m_pDoc->GetString(aPos);
8405 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
8406 OUString("Err:502"), aVal);
8407 m_pDoc->SetString(aPos, "=GCD({3;6;9};{\"a\";6;9})");
8408 aVal = m_pDoc->GetString(aPos);
8409 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
8410 OUString("Err:502"), aVal);
8411
8412 // inline list of values
8413 m_pDoc->SetString(aPos, "=GCD(12;24;36;48;60)");
8414 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc->GetValue(aPos));
8415 m_pDoc->SetString(aPos, "=GCD(0;12;24;36;48;60)");
8416 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc->GetValue(aPos));
8417 m_pDoc->SetString(aPos, "=GCD(\"a\";1)");
8418 aVal = m_pDoc->GetString(aPos);
8419 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for an array with strings",
8420 OUString("#VALUE!"), aVal);
8421
8422 m_pDoc->DeleteTab(0);
8423 }
8424
testFuncLCM()8425 void TestFormula::testFuncLCM()
8426 {
8427 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8428
8429 m_pDoc->InsertTab(0, "LCMTest");
8430
8431 OUString aVal;
8432 ScAddress aPos(4,0,0);
8433
8434 m_pDoc->SetString(aPos, "=LCM(A1)");
8435 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8436 m_pDoc->SetValue(0, 0, 0, 10.0); // A1
8437 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 10.0, m_pDoc->GetValue(aPos));
8438 m_pDoc->SetValue(0, 0, 0, -2.0); // A1
8439 aVal = m_pDoc->GetString(aPos);
8440 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for values less than 0",
8441 OUString("Err:502"), aVal);
8442 m_pDoc->SetString(0, 0, 0, "a"); // A1
8443 aVal = m_pDoc->GetString(aPos);
8444 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for a single string",
8445 OUString("#VALUE!"), aVal);
8446
8447 m_pDoc->SetString(aPos, "=LCM(A1:B2)");
8448 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 1.0, m_pDoc->GetValue(aPos));
8449 m_pDoc->SetValue(0, 1, 0, -12.0); // B1
8450 aVal = m_pDoc->GetString(aPos);
8451 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for a matrix with values less than 0",
8452 OUString("Err:502"), aVal);
8453 m_pDoc->SetValue(0, 0, 0, 15.0); // A1
8454 m_pDoc->SetValue(0, 1, 0, 0.0); // A2
8455 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8456 m_pDoc->SetValue(1, 0, 0, 5.0); // B1
8457 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8458 m_pDoc->SetValue(0, 1, 0, 10.0); // A2
8459 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc->GetValue(aPos));
8460 m_pDoc->SetValue(1, 0, 0, 30.0); // B1
8461 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc->GetValue(aPos));
8462 m_pDoc->SetValue(0, 0, 0, 20.0); // A1
8463 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 60.0, m_pDoc->GetValue(aPos));
8464 m_pDoc->SetValue(1, 1, 0, 125.0); // B2
8465 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1500.0, m_pDoc->GetValue(aPos));
8466 m_pDoc->SetValue(1, 0, 0, 99.0); // B1
8467 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 49500.0, m_pDoc->GetValue(aPos));
8468 m_pDoc->SetValue(0, 1, 0, 37.0); // A2
8469 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc->GetValue(aPos));
8470
8471 // with floor
8472 m_pDoc->SetValue(1, 0, 0, 99.89); // B1
8473 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc->GetValue(aPos));
8474 m_pDoc->SetValue(1, 1, 0, 11.32); // B2
8475 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 73260.0, m_pDoc->GetValue(aPos));
8476 m_pDoc->SetValue(0, 0, 0, 22.58); // A1
8477 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 7326.0, m_pDoc->GetValue(aPos));
8478 m_pDoc->SetValue(0, 1, 0, 3.99); // A2
8479 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 198.0, m_pDoc->GetValue(aPos));
8480
8481 // inline array
8482 m_pDoc->SetString(aPos, "=LCM({3;6;9})");
8483 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc->GetValue(aPos));
8484 m_pDoc->SetString(aPos, "=LCM({150;0})");
8485 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8486 m_pDoc->SetString(aPos, "=LCM({-3;6;9})");
8487 aVal = m_pDoc->GetString(aPos);
8488 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
8489 OUString("Err:502"), aVal);
8490 m_pDoc->SetString(aPos, "=LCM({\"a\";6;9})");
8491 aVal = m_pDoc->GetString(aPos);
8492 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
8493 OUString("Err:502"), aVal);
8494
8495 //many inline array
8496 m_pDoc->SetString(aPos, "=LCM({6;6;6};{3;6;9})");
8497 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc->GetValue(aPos));
8498 m_pDoc->SetString(aPos, "=LCM({300;300;300};{150;0})");
8499 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8500 m_pDoc->SetString(aPos,"=LCM({3;6;9};{3;-6;9})");
8501 aVal = m_pDoc->GetString(aPos);
8502 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
8503 OUString("Err:502"), aVal);
8504 m_pDoc->SetString(aPos, "=LCM({3;6;9};{\"a\";6;9})");
8505 aVal = m_pDoc->GetString(aPos);
8506 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
8507 OUString("Err:502"), aVal);
8508
8509 m_pDoc->SetString(aPos, "=LCM(12;24;36;48;60)");
8510 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 720.0, m_pDoc->GetValue(aPos));
8511 m_pDoc->SetString(aPos, "=LCM(0;12;24;36;48;60)");
8512 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc->GetValue(aPos));
8513 m_pDoc->SetString(aPos, "=LCM(\"a\";1)");
8514 aVal = m_pDoc->GetString(aPos);
8515 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for an array with strings",
8516 OUString("#VALUE!"), aVal);
8517
8518 m_pDoc->DeleteTab(0);
8519 }
8520
testFuncSUMSQ()8521 void TestFormula::testFuncSUMSQ()
8522 {
8523 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8524
8525 m_pDoc->InsertTab(0, "SUMSQTest");
8526
8527 ScAddress aPos(4,0,0);
8528
8529 m_pDoc->SetString(aPos, "=SUMSQ(A1)");
8530 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 0.0, m_pDoc->GetValue(aPos));
8531 m_pDoc->SetValue(0, 0, 0, 1.0); // A1
8532 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8533 m_pDoc->SetValue(0, 0, 0, -1.0); // A1
8534 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8535 m_pDoc->SetValue(0, 1, 0, -2.0); // A2
8536 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc->GetValue(aPos));
8537
8538 m_pDoc->SetString(aPos, "=SUMSQ(A1:A3)");
8539 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc->GetValue(aPos));
8540 m_pDoc->SetValue(1, 0, 0, 3.0); // B1
8541 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc->GetValue(aPos));
8542 m_pDoc->SetString(aPos, "=SUMSQ(A1:C3)");
8543 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc->GetValue(aPos));
8544 m_pDoc->SetValue(1, 1, 0, -4.0); // B2
8545 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 30.0, m_pDoc->GetValue(aPos));
8546 m_pDoc->SetString(1, 2, 0, "a"); // B3
8547 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc->GetValue(aPos));
8548 m_pDoc->SetValue(1, 2, 0, 0.0); // B3
8549 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc->GetValue(aPos));
8550 m_pDoc->SetValue(0, 2, 0, 6.0); // A3
8551 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 66.0, m_pDoc->GetValue(aPos));
8552 m_pDoc->SetValue(2, 0, 0, -5.0); // C1
8553 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 91.0, m_pDoc->GetValue(aPos));
8554 m_pDoc->SetValue(2, 1, 0, 3.0); // C2
8555 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 100.0, m_pDoc->GetValue(aPos));
8556 m_pDoc->SetValue(2, 2, 0, 2.0); // C3
8557 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 104.0, m_pDoc->GetValue(aPos));
8558
8559 // inline array
8560 m_pDoc->SetString(aPos, "=SUMSQ({1;2;3})");
8561 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc->GetValue(aPos));
8562 m_pDoc->SetString(aPos, "=SUMSQ({3;6;9})");
8563 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 126.0, m_pDoc->GetValue(aPos));
8564 m_pDoc->SetString(aPos, "=SUMSQ({15;0})");
8565 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 225.0, m_pDoc->GetValue(aPos));
8566 m_pDoc->SetString(aPos, "=SUMSQ({-3;3;1})");
8567 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 19.0, m_pDoc->GetValue(aPos));
8568 m_pDoc->SetString(aPos, "=SUMSQ({\"a\";-4;-5})");
8569 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 41.0, m_pDoc->GetValue(aPos));
8570
8571 m_pDoc->SetString(aPos, "=SUMSQ({2;3};{4;5})");
8572 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 54.0, m_pDoc->GetValue(aPos));
8573 m_pDoc->SetString(aPos, "=SUMSQ({-3;3;1};{-1})");
8574 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 20.0, m_pDoc->GetValue(aPos));
8575 m_pDoc->SetString(aPos, "=SUMSQ({-4};{1;4;2};{-5;7};{9})");
8576 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 192.0, m_pDoc->GetValue(aPos));
8577 m_pDoc->SetString(aPos, "=SUMSQ({-2;2};{1};{-1};{0;0;0;4})");
8578 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc->GetValue(aPos));
8579
8580 m_pDoc->SetString(aPos, "=SUMSQ(4;1;-3)");
8581 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc->GetValue(aPos));
8582 m_pDoc->SetString(aPos, "=SUMSQ(0;5;13;-7;-4)");
8583 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 259.0, m_pDoc->GetValue(aPos));
8584 m_pDoc->SetString(aPos, "=SUMSQ(0;12;24;36;48;60)");
8585 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc->GetValue(aPos));
8586 m_pDoc->SetString(aPos, "=SUMSQ(0;-12;-24;36;-48;60)");
8587 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc->GetValue(aPos));
8588 m_pDoc->SetString(aPos, "=SUMSQ(\"a\";1;\"d\";-4;2)");
8589 OUString aVal = m_pDoc->GetString(aPos);
8590 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMSQ should return #VALUE! for an array with strings",
8591 OUString("#VALUE!"), aVal);
8592
8593 m_pDoc->DeleteTab(0);
8594 }
8595
testFuncMDETERM()8596 void TestFormula::testFuncMDETERM()
8597 {
8598 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8599
8600 m_pDoc->InsertTab(0, "MDETERM_test");
8601 ScAddress aPos(8,0,0);
8602 OUString const aColCodes("ABCDEFGH");
8603 OUStringBuffer aFormulaBuffer("=MDETERM(A1:B2)");
8604 for( SCSIZE nSize = 3; nSize <= 8; nSize++ )
8605 {
8606 double fVal = 1.0;
8607 // Generate a singular integer matrix
8608 for( SCROW nRow = 0; nRow < static_cast<SCROW>(nSize); nRow++ )
8609 {
8610 for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(nSize); nCol++ )
8611 {
8612 m_pDoc->SetValue(nCol, nRow, 0, fVal);
8613 fVal += 1.0;
8614 }
8615 }
8616 aFormulaBuffer[12] = aColCodes[nSize-1];
8617 aFormulaBuffer[13] = static_cast<sal_Unicode>( '0' + nSize );
8618 m_pDoc->SetString(aPos, aFormulaBuffer.toString());
8619
8620 #if SAL_TYPES_SIZEOFPOINTER == 4
8621 // On crappy 32-bit targets, presumably without extended precision on
8622 // interim results or optimization not catching it, this test fails
8623 // when comparing to 0.0, so have a narrow error margin. See also
8624 // commit message of 8140309d636d4a870875f2dd75ed3dfff2c0fbaf
8625 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8626 0.0, m_pDoc->GetValue(aPos), 1e-12);
8627 #else
8628 // Even on one (and only one) x86_64 target the result was
8629 // 6.34413156928661e-17 instead of 0.0 (tdf#99730) so lower the bar to
8630 // 10e-14.
8631 // Then again on aarch64, ppc64* and s390x it also fails.
8632 // Sigh... why do we even test this? The original complaint in tdf#32834
8633 // was about -9.51712667007776E-016
8634 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8635 0.0, m_pDoc->GetValue(aPos), 1e-14);
8636 #endif
8637 }
8638
8639 int const aVals[] = {23, 31, 13, 12, 34, 64, 34, 31, 98, 32, 33, 63, 45, 54, 65, 76};
8640 int nIdx = 0;
8641 for( SCROW nRow = 0; nRow < 4; nRow++ )
8642 for( SCCOL nCol = 0; nCol < 4; nCol++ )
8643 m_pDoc->SetValue(nCol, nRow, 0, static_cast<double>(aVals[nIdx++]));
8644 m_pDoc->SetString(aPos, "=MDETERM(A1:D4)");
8645 // Following test is conservative in the sense that on Linux x86_64 the error is less that 1.0E-9
8646 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for non-singular integer matrix",
8647 -180655.0, m_pDoc->GetValue(aPos), 1.0E-6);
8648 m_pDoc->DeleteTab(0);
8649 }
8650
testFormulaErrorPropagation()8651 void TestFormula::testFormulaErrorPropagation()
8652 {
8653 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
8654
8655 m_pDoc->InsertTab(0, "Sheet1");
8656
8657 ScMarkData aMark(m_pDoc->GetSheetLimits());
8658 aMark.SelectOneTable(0);
8659 ScAddress aPos, aPos2;
8660 const OUString aTRUE("TRUE");
8661 const OUString aFALSE("FALSE");
8662
8663 aPos.Set(0,0,0);// A1
8664 m_pDoc->SetValue( aPos, 1.0);
8665 aPos.IncCol(); // B1
8666 m_pDoc->SetValue( aPos, 2.0);
8667 aPos.IncCol();
8668
8669 aPos.IncRow(); // C2
8670 m_pDoc->SetString( aPos, "=ISERROR(A1:B1+3)");
8671 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8672
8673 aPos.IncRow(); // C3
8674 m_pDoc->SetString( aPos, "=ISERROR(A1:B1+{3})");
8675 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8676 aPos.IncRow(); // C4
8677 aPos2 = aPos;
8678 aPos2.IncCol(); // D4
8679 m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(A1:B1+{3})");
8680 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8681 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8682
8683 aPos.IncRow(); // C5
8684 m_pDoc->SetString( aPos, "=ISERROR({1;\"x\"}+{3;4})");
8685 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8686 aPos.IncRow(); // C6
8687 aPos2 = aPos;
8688 aPos2.IncCol(); // D6
8689 m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR({1;\"x\"}+{3;4})");
8690 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8691 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos2));
8692
8693 aPos.IncRow(); // C7
8694 m_pDoc->SetString( aPos, "=ISERROR({\"x\";2}+{3;4})");
8695 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8696 aPos.IncRow(); // C8
8697 aPos2 = aPos;
8698 aPos2.IncCol(); // D8
8699 m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR({\"x\";2}+{3;4})");
8700 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8701 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8702
8703 aPos.IncRow(); // C9
8704 m_pDoc->SetString( aPos, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8705 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8706 aPos.IncRow(); // C10
8707 aPos2 = aPos;
8708 aPos2.IncCol(); // D10
8709 m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8710 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos));
8711 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos2));
8712
8713 aPos.IncRow(); // C11
8714 m_pDoc->SetString( aPos, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8715 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8716 aPos.IncRow(); // C12
8717 aPos2 = aPos;
8718 aPos2.IncCol(); // D12
8719 m_pDoc->InsertMatrixFormula(aPos.Col(), aPos.Row(), aPos2.Col(), aPos2.Row(), aMark, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8720 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos.Format(ScRefFlags::VALID).toUtf8().getStr(), aTRUE, m_pDoc->GetString(aPos));
8721 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2.Format(ScRefFlags::VALID).toUtf8().getStr(), aFALSE, m_pDoc->GetString(aPos2));
8722
8723 m_pDoc->DeleteTab(0);
8724 }
8725
testTdf97369()8726 void TestFormula::testTdf97369()
8727 {
8728 const SCROW TOTAL_ROWS = 330;
8729 const SCROW ROW_RANGE = 10;
8730 const SCROW START1 = 9;
8731 const SCROW END1 = 159;
8732 const SCROW START2 = 169;
8733 const SCROW END2 = 319;
8734
8735 const double SHIFT1 = 200;
8736 const double SHIFT2 = 400;
8737
8738 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8739 m_pDoc->InsertTab (0, "tdf97369"));
8740
8741 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8742
8743 // set up columns A, B, C
8744 for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8745 {
8746 m_pDoc->SetValue(ScAddress(0, i, 0), i); // A
8747 m_pDoc->SetValue(ScAddress(1, i, 0), i + SHIFT1); // B
8748 m_pDoc->SetValue(ScAddress(2, i, 0), i + SHIFT2); // C
8749 }
8750
8751 const ColumnTest columnTest( m_pDoc, TOTAL_ROWS, START1, END1, START2, END2 );
8752
8753 auto lExpectedinD = [=] (SCROW n) {
8754 return 3.0 * (n-START1) + SHIFT1 + SHIFT2;
8755 };
8756 columnTest(3, "=SUM(A1:C1)", lExpectedinD);
8757
8758 auto lExpectedinE = [=] (SCROW ) {
8759 return SHIFT1 + SHIFT2;
8760 };
8761 columnTest(4, "=SUM(A$1:C$1)", lExpectedinE);
8762
8763 auto lExpectedinF = [=] (SCROW n) {
8764 return ((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2.0;
8765 };
8766 columnTest(5, "=SUM(A1:A10)", lExpectedinF);
8767
8768 auto lExpectedinG = [=] (SCROW n) {
8769 return ((n + 1) * n) / 2.0;
8770 };
8771 columnTest(6, "=SUM(A$1:A10)", lExpectedinG);
8772
8773 auto lExpectedinH = [=] (SCROW n) {
8774 return 3.0 * (((2*n + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8775 };
8776 columnTest(7, "=SUM(A1:C10)", lExpectedinH);
8777
8778 auto lExpectedinI = [=] (SCROW ) {
8779 return 3.0 * (((2*START1 + 1 - ROW_RANGE) * ROW_RANGE) / 2) + ROW_RANGE * (SHIFT1 + SHIFT2);
8780 };
8781 columnTest(8, "=SUM(A$1:C$10)", lExpectedinI);
8782
8783 m_pDoc->DeleteTab(0);
8784 }
8785
testTdf97587()8786 void TestFormula::testTdf97587()
8787 {
8788 const SCROW TOTAL_ROWS = 150;
8789 const SCROW ROW_RANGE = 10;
8790
8791 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8792 m_pDoc->InsertTab (0, "tdf97587"));
8793
8794 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
8795
8796 std::set<SCROW> emptyCells = {0, 100};
8797 for( SCROW i = 0; i < ROW_RANGE; ++i )
8798 {
8799 emptyCells.insert(i + TOTAL_ROWS / 3);
8800 emptyCells.insert(i + TOTAL_ROWS);
8801 }
8802
8803 // set up columns A
8804 for( SCROW i = 0; i < TOTAL_ROWS; ++i )
8805 {
8806 if( emptyCells.find(i) != emptyCells.end() )
8807 continue;
8808 m_pDoc->SetValue(ScAddress(0, i, 0), 1.0);
8809 }
8810
8811 ScDocument aClipDoc(SCDOCMODE_CLIP);
8812 ScMarkData aMark(m_pDoc->GetSheetLimits());
8813
8814 ScAddress aPos(1, 0, 0);
8815 m_pDoc->SetString(aPos, "=SUM(A1:A10)");
8816
8817 // Copy formula cell to clipboard.
8818 ScClipParam aClipParam(aPos, false);
8819 aMark.SetMarkArea(aPos);
8820 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
8821
8822 // Paste it to first range.
8823 ScRange aDestRange(1, 1, 0, 1, TOTAL_ROWS + ROW_RANGE, 0);
8824 aMark.SetMarkArea(aDestRange);
8825 m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
8826
8827 // Check the formula results in column B.
8828 for( SCROW i = 0; i < TOTAL_ROWS + 1; ++i )
8829 {
8830 int k = std::count_if( emptyCells.begin(), emptyCells.end(),
8831 [=](SCROW n) { return (i <= n && n < i + ROW_RANGE); } );
8832 double fExpected = ROW_RANGE - k;
8833 ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(1,i,0)));
8834 }
8835 m_pDoc->DeleteTab(0);
8836 }
8837
testTdf93415()8838 void TestFormula::testTdf93415()
8839 {
8840 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Sheet1"));
8841
8842 ScCalcConfig aConfig;
8843 aConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
8844 m_pDoc->SetCalcConfig(aConfig);
8845 m_pDoc->CalcAll();
8846
8847 ScAddress aPos(0,0,0);
8848 m_pDoc->SetString(aPos, "=ADDRESS(1;1;;;\"Sheet1\")");
8849
8850 // Without the fix in place, this would have failed with
8851 // - Expected: Sheet1!$A$1
8852 // - Actual : Sheet1.$A$1
8853 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1!$A$1"), m_pDoc->GetString(aPos));
8854
8855 m_pDoc->DeleteTab(0);
8856 }
8857
testTdf100818()8858 void TestFormula::testTdf100818()
8859 {
8860 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Sheet1"));
8861
8862 //Insert local range name
8863 ScRangeData* pLocal = new ScRangeData( *m_pDoc, "local", "$Sheet1.$A$1");
8864 std::unique_ptr<ScRangeName> pLocalRangeName(new ScRangeName);
8865 pLocalRangeName->insert(pLocal);
8866 m_pDoc->SetRangeName(0, std::move(pLocalRangeName));
8867
8868 m_pDoc->SetValue(0, 0, 0, 1.0);
8869
8870 CPPUNIT_ASSERT(m_pDoc->InsertTab (1, "Sheet2"));
8871
8872 m_pDoc->SetString(0, 0, 1, "=INDIRECT(\"Sheet1.local\")");
8873
8874 // Without the fix in place, this test would have failed with
8875 // - Expected: 1
8876 // - Actual : #REF!
8877 CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(0,0,1));
8878
8879 m_pDoc->DeleteTab(1);
8880 m_pDoc->SetRangeName(0,nullptr); // Delete the names.
8881 m_pDoc->DeleteTab(0);
8882 }
8883
testMatConcat()8884 void TestFormula::testMatConcat()
8885 {
8886 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8887
8888 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8889 {
8890 for (SCROW nRow = 0; nRow < 10; ++nRow)
8891 {
8892 m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8893 }
8894 }
8895
8896 ScMarkData aMark(m_pDoc->GetSheetLimits());
8897 aMark.SelectOneTable(0);
8898 m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J10");
8899
8900 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8901 {
8902 for (SCROW nRow = 12; nRow < 22; ++nRow)
8903 {
8904 OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8905 CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + OUString::number(nCol * (nRow - 12))), aStr);
8906 }
8907 }
8908
8909 { // Data in A12:B16
8910 std::vector<std::vector<const char*>> aData = {
8911 { "q", "w" },
8912 { "a", "" },
8913 { "", "x" },
8914 { "", "" },
8915 { "e", "r" },
8916 };
8917
8918 ScAddress aPos(0,11,0);
8919 ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
8920 CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
8921 }
8922 // Matrix formula in C17:C21
8923 m_pDoc->InsertMatrixFormula(2, 16, 2, 20, aMark, "=A12:A16&B12:B16");
8924 // Check proper concatenation including empty cells.
8925 OUString aStr;
8926 ScAddress aPos(2,16,0);
8927 aStr = m_pDoc->GetString(aPos);
8928 CPPUNIT_ASSERT_EQUAL(OUString("qw"),aStr);
8929 aPos.IncRow();
8930 aStr = m_pDoc->GetString(aPos);
8931 CPPUNIT_ASSERT_EQUAL(OUString("a"),aStr);
8932 aPos.IncRow();
8933 aStr = m_pDoc->GetString(aPos);
8934 CPPUNIT_ASSERT_EQUAL(OUString("x"),aStr);
8935 aPos.IncRow();
8936 aStr = m_pDoc->GetString(aPos);
8937 CPPUNIT_ASSERT_EQUAL(OUString(),aStr);
8938 aPos.IncRow();
8939 aStr = m_pDoc->GetString(aPos);
8940 CPPUNIT_ASSERT_EQUAL(OUString("er"),aStr);
8941
8942 m_pDoc->DeleteTab(0);
8943 }
8944
testMatConcatReplication()8945 void TestFormula::testMatConcatReplication()
8946 {
8947 // if one of the matrices is a one column or row matrix
8948 // the matrix is replicated across the larger matrix
8949 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8950
8951 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8952 {
8953 for (SCROW nRow = 0; nRow < 10; ++nRow)
8954 {
8955 m_pDoc->SetValue(ScAddress(nCol, nRow, 0), nCol*nRow);
8956 }
8957 }
8958
8959 ScMarkData aMark(m_pDoc->GetSheetLimits());
8960 aMark.SelectOneTable(0);
8961 m_pDoc->InsertMatrixFormula(0, 12, 9, 21, aMark, "=A1:J10&A1:J1");
8962
8963 for (SCCOL nCol = 0; nCol < 10; ++nCol)
8964 {
8965 for (SCROW nRow = 12; nRow < 22; ++nRow)
8966 {
8967 OUString aStr = m_pDoc->GetString(ScAddress(nCol, nRow, 0));
8968 CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol * (nRow - 12)) + "0"), aStr);
8969 }
8970 }
8971
8972 m_pDoc->DeleteTab(0);
8973 }
8974
testRefR1C1WholeCol()8975 void TestFormula::testRefR1C1WholeCol()
8976 {
8977 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8978
8979 ScAddress aPos(1, 1, 1);
8980 ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
8981 std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString("=C[10]"));
8982 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
8983 OUString aFormula = pTokens->CreateString(aCxt, aPos);
8984
8985 CPPUNIT_ASSERT_EQUAL(OUString("L:L"), aFormula);
8986
8987 m_pDoc->DeleteTab(0);
8988 }
8989
testRefR1C1WholeRow()8990 void TestFormula::testRefR1C1WholeRow()
8991 {
8992 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
8993
8994 ScAddress aPos(1, 1, 1);
8995 ScCompiler aComp(*m_pDoc, aPos, FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
8996 std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString("=R[3]"));
8997 sc::TokenStringContext aCxt(*m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
8998 OUString aFormula = pTokens->CreateString(aCxt, aPos);
8999
9000 CPPUNIT_ASSERT_EQUAL(OUString("5:5"), aFormula);
9001
9002 m_pDoc->DeleteTab(0);
9003 }
9004
testSingleCellCopyColumnLabel()9005 void TestFormula::testSingleCellCopyColumnLabel()
9006 {
9007 ScDocOptions aOptions = m_pDoc->GetDocOptions();
9008 aOptions.SetLookUpColRowNames(true);
9009 m_pDoc->SetDocOptions(aOptions);
9010 m_pDoc->InsertTab(0, "Test");
9011
9012 m_pDoc->SetString(0, 0, 0, "a");
9013 m_pDoc->SetValue(0, 1, 0, 1.0);
9014 m_pDoc->SetValue(0, 2, 0, 2.0);
9015 m_pDoc->SetValue(0, 3, 0, 3.0);
9016 m_pDoc->SetString(1, 1, 0, "='a'");
9017
9018 double nVal = m_pDoc->GetValue(1, 1, 0);
9019 ASSERT_DOUBLES_EQUAL(1.0, nVal);
9020
9021 ScDocument aClipDoc(SCDOCMODE_CLIP);
9022 copyToClip(m_pDoc, ScRange(1, 1, 0), &aClipDoc);
9023 pasteOneCellFromClip(m_pDoc, ScRange(1, 2, 0), &aClipDoc);
9024 nVal = m_pDoc->GetValue(1, 2, 0);
9025 ASSERT_DOUBLES_EQUAL(2.0, nVal);
9026
9027 m_pDoc->DeleteTab(0);
9028 }
9029
9030 // Significant whitespace operator intersection in Excel syntax, tdf#96426
testIntersectionOpExcel()9031 void TestFormula::testIntersectionOpExcel()
9032 {
9033 CPPUNIT_ASSERT(m_pDoc->InsertTab (0, "Test"));
9034
9035 ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
9036 // Horizontal cell range covering C2.
9037 pGlobalNames->insert( new ScRangeData( *m_pDoc, "horz", "$B$2:$D$2"));
9038 // Vertical cell range covering C2.
9039 pGlobalNames->insert( new ScRangeData( *m_pDoc, "vert", "$C$1:$C$3"));
9040 // Data in C2.
9041 m_pDoc->SetValue(2,1,0, 1.0);
9042
9043 m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH_XL_A1);
9044
9045 // Choose formula positions that don't intersect with those data ranges.
9046 ScAddress aPos(0,3,0);
9047 m_pDoc->SetString(aPos,"=B2:D2 C1:C3");
9048 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 intersecting references failed", 1.0, m_pDoc->GetValue(aPos));
9049 aPos.IncRow();
9050 m_pDoc->SetString(aPos,"=horz vert");
9051 CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 intersecting named expressions failed", 1.0, m_pDoc->GetValue(aPos));
9052 aPos.IncRow();
9053 m_pDoc->SetString(aPos,"=(horz vert)*2");
9054 CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 calculating with intersecting named expressions failed", 2.0, m_pDoc->GetValue(aPos));
9055 aPos.IncRow();
9056 m_pDoc->SetString(aPos,"=2*(horz vert)");
9057 CPPUNIT_ASSERT_EQUAL_MESSAGE("A7 calculating with intersecting named expressions failed", 2.0, m_pDoc->GetValue(aPos));
9058
9059 m_pDoc->SetGrammar(FormulaGrammar::GRAM_ENGLISH);
9060
9061 m_pDoc->DeleteTab(0);
9062 }
9063
9064 //Test Subtotal and Aggregate during hide rows #tdf93171
testFuncRowsHidden()9065 void TestFormula::testFuncRowsHidden()
9066 {
9067 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9068 m_pDoc->InsertTab(0, "Test");
9069 m_pDoc->SetValue(0, 0, 0, 1); //A1
9070 m_pDoc->SetValue(0, 1, 0, 2); //A2
9071 m_pDoc->SetValue(0, 2, 0, 4); //A3
9072 m_pDoc->SetValue(0, 3, 0, 8); //A4
9073 m_pDoc->SetValue(0, 4, 0, 16); //A5
9074 m_pDoc->SetValue(0, 5, 0, 32); //A6
9075
9076 ScAddress aPos(0,6,0);
9077 m_pDoc->SetString(aPos, "=SUBTOTAL(109; A1:A6)");
9078 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc->GetValue(aPos));
9079 //Hide row 1
9080 m_pDoc->SetRowHidden(0, 0, 0, true);
9081 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 62.0, m_pDoc->GetValue(aPos));
9082 m_pDoc->SetRowHidden(0, 0, 0, false);
9083 //Hide row 2 and 3
9084 m_pDoc->SetRowHidden(1, 2, 0, true);
9085 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 57.0, m_pDoc->GetValue(aPos));
9086 m_pDoc->SetRowHidden(1, 2, 0, false);
9087 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc->GetValue(aPos));
9088
9089 m_pDoc->SetString(aPos, "=AGGREGATE(9; 5; A1:A6)"); //9=SUM 5=Ignore only hidden rows
9090 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc->GetValue(aPos));
9091 //Hide row 1
9092 m_pDoc->SetRowHidden(0, 0, 0, true);
9093 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 62.0, m_pDoc->GetValue(aPos));
9094 m_pDoc->SetRowHidden(0, 0, 0, false);
9095 //Hide rows 3 to 5
9096 m_pDoc->SetRowHidden(2, 4, 0, true);
9097 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 35.0, m_pDoc->GetValue(aPos));
9098 m_pDoc->SetRowHidden(2, 4, 0, false);
9099 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc->GetValue(aPos));
9100
9101 m_pDoc->SetString(aPos, "=SUM(A1:A6)");
9102 m_pDoc->SetRowHidden(2, 4, 0, true);
9103 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUM failed", 63.0, m_pDoc->GetValue(aPos));
9104
9105 m_pDoc->DeleteTab(0);
9106 }
9107
9108 // Test COUNTIFS, SUMIFS, AVERAGEIFS in array context.
testFuncSUMIFS()9109 void TestFormula::testFuncSUMIFS()
9110 {
9111 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9112 m_pDoc->InsertTab(0, "Test");
9113
9114 // Data in A1:B7, query in A9:A11
9115 std::vector<std::vector<const char*>> aData = {
9116 { "a", "1" },
9117 { "b", "2" },
9118 { "c", "4" },
9119 { "d", "8" },
9120 { "a", "16" },
9121 { "b", "32" },
9122 { "c", "64" },
9123 { "" }, // {} doesn't work with some compilers
9124 { "a" },
9125 { "b" },
9126 { "c" },
9127 };
9128
9129 insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
9130
9131 ScMarkData aMark(m_pDoc->GetSheetLimits());
9132 aMark.SelectOneTable(0);
9133 // Matrix formula in C8:C10 with SUMIFS
9134 m_pDoc->InsertMatrixFormula(2, 7, 2, 9, aMark, "=SUMIFS(B1:B7;A1:A7;A9:A11)");
9135 // Matrix formula in D8:D10 with COUNTIFS
9136 m_pDoc->InsertMatrixFormula(3, 7, 3, 9, aMark, "=COUNTIFS(A1:A7;A9:A11)");
9137 // Matrix formula in E8:E10 with AVERAGEIFS
9138 m_pDoc->InsertMatrixFormula(4, 7, 4, 9, aMark, "=AVERAGEIFS(B1:B7;A1:A7;A9:A11)");
9139
9140 {
9141 // Result B1+B5, B2+B6, B3+B7 and counts and averages.
9142 std::vector<std::vector<const char*>> aCheck = {
9143 { "17", "2", "8.5" },
9144 { "34", "2", "17" },
9145 { "68", "2", "34" }
9146 };
9147 bool bGood = checkOutput(m_pDoc, ScRange(2,7,0, 4,9,0), aCheck,
9148 "SUMIFS, COUNTIFS and AVERAGEIFS in array context");
9149 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS in array context failed", bGood);
9150 }
9151
9152 // Matrix formula in G8:G10 with SUMIFS and reference list arrays.
9153 m_pDoc->InsertMatrixFormula(6, 7, 6, 9, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
9154 // Matrix formula in H8:H10 with COUNTIFS and reference list arrays.
9155 m_pDoc->InsertMatrixFormula(7, 7, 7, 9, aMark, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\")");
9156 // Matrix formula in I8:I10 with AVERAGEIFS and reference list arrays.
9157 m_pDoc->InsertMatrixFormula(8, 7, 8, 9, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
9158
9159 {
9160 // Result sums, counts and averages.
9161 std::vector<std::vector<const char*>> aCheck = {
9162 { "0", "0", "#DIV/0!" },
9163 { "8", "1", "8" },
9164 { "24", "2", "12" }
9165 };
9166 bool bGood = checkOutput(m_pDoc, ScRange(6,7,0, 8,9,0), aCheck,
9167 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays");
9168 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays failed", bGood);
9169 }
9170
9171 // Matrix formula in K8:K10 with SUMIFS and reference list array condition
9172 // and "normal" data range.
9173 m_pDoc->InsertMatrixFormula(10, 7, 10, 9, aMark, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
9174 // Matrix formula in L8:L10 with AVERAGEIFS and reference list array
9175 // condition and "normal" data range.
9176 m_pDoc->InsertMatrixFormula(11, 7, 11, 9, aMark, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
9177
9178 {
9179 // Result sums and averages.
9180 std::vector<std::vector<const char*>> aCheck = {
9181 { "0", "#DIV/0!" },
9182 { "2", "2" },
9183 { "3", "1.5" }
9184 };
9185 bool bGood = checkOutput(m_pDoc, ScRange(10,7,0, 11,9,0), aCheck,
9186 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal range");
9187 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal range failed", bGood);
9188 }
9189
9190 // Matrix formula in G18:G20 with SUMIFS and reference list arrays and a
9191 // "normal" criteria range.
9192 m_pDoc->InsertMatrixFormula(6, 17, 6, 19, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9193 // Matrix formula in H18:H20 with COUNTIFS and reference list arrays and a
9194 // "normal" criteria range.
9195 m_pDoc->InsertMatrixFormula(7, 17, 7, 19, aMark, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9196 // Matrix formula in I18:I20 with AVERAGEIFS and reference list arrays and
9197 // a "normal" criteria range.
9198 m_pDoc->InsertMatrixFormula(8, 17, 8, 19, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9199
9200 {
9201 // Result sums, counts and averages.
9202 std::vector<std::vector<const char*>> aCheck = {
9203 { "0", "0", "#DIV/0!" },
9204 { "8", "1", "8" },
9205 { "16", "1", "16" }
9206 };
9207 bool bGood = checkOutput(m_pDoc, ScRange(6,17,0, 8,19,0), aCheck,
9208 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range");
9209 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed", bGood);
9210 }
9211
9212 // Matrix formula in K18:K20 with SUMIFS and reference list array condition
9213 // and "normal" data range and a "normal" criteria range.
9214 m_pDoc->InsertMatrixFormula(10, 17, 10, 19, aMark, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9215 // Matrix formula in L18:L20 with AVERAGEIFS and reference list array
9216 // condition and "normal" data range and a "normal" criteria range.
9217 m_pDoc->InsertMatrixFormula(11, 17, 11, 19, aMark, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
9218
9219 {
9220 // Result sums and averages.
9221 std::vector<std::vector<const char*>> aCheck = {
9222 { "0", "#DIV/0!" },
9223 { "2", "2" },
9224 { "2", "2" }
9225 };
9226 bool bGood = checkOutput(m_pDoc, ScRange(10,17,0, 11,19,0), aCheck,
9227 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range");
9228 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed", bGood);
9229 }
9230
9231 // Same, but swapped normal and array criteria.
9232
9233 // Matrix formula in G28:G30 with SUMIFS and reference list arrays and a
9234 // "normal" criteria range, swapped.
9235 m_pDoc->InsertMatrixFormula(6, 27, 6, 29, aMark, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9236 // Matrix formula in H28:H30 with COUNTIFS and reference list arrays and a
9237 // "normal" criteria range, swapped.
9238 m_pDoc->InsertMatrixFormula(7, 27, 7, 29, aMark, "=COUNTIFS(B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9239 // Matrix formula in I28:I30 with AVERAGEIFS and reference list arrays and
9240 // a "normal" criteria range, swapped.
9241 m_pDoc->InsertMatrixFormula(8, 27, 8, 29, aMark, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9242
9243 {
9244 // Result sums, counts and averages.
9245 std::vector<std::vector<const char*>> aCheck = {
9246 { "0", "0", "#DIV/0!" },
9247 { "8", "1", "8" },
9248 { "16", "1", "16" }
9249 };
9250 bool bGood = checkOutput(m_pDoc, ScRange(6,27,0, 8,29,0), aCheck,
9251 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range, swapped");
9252 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed, swapped", bGood);
9253 }
9254
9255 // Matrix formula in K28:K30 with SUMIFS and reference list array condition
9256 // and "normal" data range and a "normal" criteria range, swapped.
9257 m_pDoc->InsertMatrixFormula(10, 27, 10, 29, aMark, "=SUMIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9258 // Matrix formula in L28:L30 with AVERAGEIFS and reference list array
9259 // condition and "normal" data range and a "normal" criteria range,
9260 // swapped.
9261 m_pDoc->InsertMatrixFormula(11, 27, 11, 29, aMark, "=AVERAGEIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
9262
9263 {
9264 // Result sums and averages.
9265 std::vector<std::vector<const char*>> aCheck = {
9266 { "0", "#DIV/0!" },
9267 { "2", "2" },
9268 { "2", "2" }
9269 };
9270 bool bGood = checkOutput(m_pDoc, ScRange(10,27,0, 11,29,0), aCheck,
9271 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range, swapped");
9272 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed, swapped", bGood);
9273 }
9274
9275 m_pDoc->DeleteTab(0);
9276 }
9277
9278 // Test SUBTOTAL with reference lists in array context.
testFuncRefListArraySUBTOTAL()9279 void TestFormula::testFuncRefListArraySUBTOTAL()
9280 {
9281 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9282 m_pDoc->InsertTab(0, "Test");
9283
9284 m_pDoc->SetValue(0,0,0, 1.0); // A1
9285 m_pDoc->SetValue(0,1,0, 2.0); // A2
9286 m_pDoc->SetValue(0,2,0, 4.0); // A3
9287 m_pDoc->SetValue(0,3,0, 8.0); // A4
9288 m_pDoc->SetValue(0,4,0, 16.0); // A5
9289 m_pDoc->SetValue(0,5,0, 32.0); // A6
9290
9291 // Matrix in B7:B9, individual SUM of A2:A3, A3:A4 and A4:A5
9292 ScMarkData aMark(m_pDoc->GetSheetLimits());
9293 aMark.SelectOneTable(0);
9294 m_pDoc->InsertMatrixFormula(1, 6, 1, 8, aMark, "=SUBTOTAL(9;OFFSET(A1;ROW(1:3);0;2))");
9295 ScAddress aPos(1,6,0);
9296 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A2:A3 failed", 6.0, m_pDoc->GetValue(aPos));
9297 aPos.IncRow();
9298 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A3:A4 failed", 12.0, m_pDoc->GetValue(aPos));
9299 aPos.IncRow();
9300 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A4:A5 failed", 24.0, m_pDoc->GetValue(aPos));
9301
9302 // Matrix in C7:C9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
9303 m_pDoc->InsertMatrixFormula(2, 6, 2, 8, aMark, "=SUBTOTAL(1;OFFSET(A1;ROW(1:3);0;2))");
9304 aPos.Set(2,6,0);
9305 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A2:A3 failed", 3.0, m_pDoc->GetValue(aPos));
9306 aPos.IncRow();
9307 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A3:A4 failed", 6.0, m_pDoc->GetValue(aPos));
9308 aPos.IncRow();
9309 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A4:A5 failed", 12.0, m_pDoc->GetValue(aPos));
9310
9311 // Matrix in D7:D9, individual MIN of A2:A3, A3:A4 and A4:A5
9312 m_pDoc->InsertMatrixFormula(3, 6, 3, 8, aMark, "=SUBTOTAL(5;OFFSET(A1;ROW(1:3);0;2))");
9313 aPos.Set(3,6,0);
9314 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A2:A3 failed", 2.0, m_pDoc->GetValue(aPos));
9315 aPos.IncRow();
9316 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A3:A4 failed", 4.0, m_pDoc->GetValue(aPos));
9317 aPos.IncRow();
9318 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A4:A5 failed", 8.0, m_pDoc->GetValue(aPos));
9319
9320 // Matrix in E7:E9, individual MAX of A2:A3, A3:A4 and A4:A5
9321 m_pDoc->InsertMatrixFormula(4, 6, 4, 8, aMark, "=SUBTOTAL(4;OFFSET(A1;ROW(1:3);0;2))");
9322 aPos.Set(4,6,0);
9323 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A2:A3 failed", 4.0, m_pDoc->GetValue(aPos));
9324 aPos.IncRow();
9325 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A3:A4 failed", 8.0, m_pDoc->GetValue(aPos));
9326 aPos.IncRow();
9327 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A4:A5 failed", 16.0, m_pDoc->GetValue(aPos));
9328
9329 // Matrix in F7:F9, individual STDEV of A2:A3, A3:A4 and A4:A5
9330 m_pDoc->InsertMatrixFormula(5, 6, 5, 8, aMark, "=SUBTOTAL(7;OFFSET(A1;ROW(1:3);0;2))");
9331 aPos.Set(5,6,0);
9332 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A2:A3 failed", 1.414214, m_pDoc->GetValue(aPos), 1e-6);
9333 aPos.IncRow();
9334 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A3:A4 failed", 2.828427, m_pDoc->GetValue(aPos), 1e-6);
9335 aPos.IncRow();
9336 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A4:A5 failed", 5.656854, m_pDoc->GetValue(aPos), 1e-6);
9337
9338 // Matrix in G7:G9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
9339 // Plus two "ordinary" ranges, one before and one after.
9340 m_pDoc->InsertMatrixFormula(6, 6, 6, 8, aMark, "=SUBTOTAL(1;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
9341 aPos.Set(6,6,0);
9342 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A2:A3,A5:A6 failed", 9.5, m_pDoc->GetValue(aPos));
9343 aPos.IncRow();
9344 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A3:A4,A5:A6 failed", 10.5, m_pDoc->GetValue(aPos));
9345 aPos.IncRow();
9346 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A4:A5,A5:A6 failed", 12.5, m_pDoc->GetValue(aPos));
9347
9348 // Matrix in H7:H9, individual MAX of A2:A3, A3:A4 and A4:A5
9349 // Plus two "ordinary" ranges, one before and one after.
9350 m_pDoc->InsertMatrixFormula(7, 6, 7, 8, aMark, "=SUBTOTAL(4;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
9351 aPos.Set(7,6,0);
9352 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A2:A3,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
9353 aPos.IncRow();
9354 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A3:A4,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
9355 aPos.IncRow();
9356 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A4:A5,A5:A6 failed", 32.0, m_pDoc->GetValue(aPos));
9357
9358 // Matrix in I7:I9, individual STDEV of A2:A3, A3:A4 and A4:A5
9359 // Plus two "ordinary" ranges, one before and one after.
9360 m_pDoc->InsertMatrixFormula(8, 6, 8, 8, aMark, "=SUBTOTAL(7;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
9361 aPos.Set(8,6,0);
9362 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A2:A3,A5:A6 failed", 12.35718, m_pDoc->GetValue(aPos), 1e-5);
9363 aPos.IncRow();
9364 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A3:A4,A5:A6 failed", 11.86170, m_pDoc->GetValue(aPos), 1e-5);
9365 aPos.IncRow();
9366 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A4:A5,A5:A6 failed", 11.55422, m_pDoc->GetValue(aPos), 1e-5);
9367
9368 // Empty two cells such that they affect two ranges.
9369 m_pDoc->SetString(0,1,0, ""); // A2
9370 m_pDoc->SetString(0,2,0, ""); // A3
9371 // Matrix in J7:J9, individual COUNTBLANK of A2:A3, A3:A4 and A4:A5
9372 m_pDoc->InsertMatrixFormula(9, 6, 9, 8, aMark, "=COUNTBLANK(OFFSET(A1;ROW(1:3);0;2))");
9373 aPos.Set(9,6,0);
9374 CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A2:A3,A5:A6 failed", 2.0, m_pDoc->GetValue(aPos));
9375 aPos.IncRow();
9376 CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A3:A4,A5:A6 failed", 1.0, m_pDoc->GetValue(aPos));
9377 aPos.IncRow();
9378 CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A4:A5,A5:A6 failed", 0.0, m_pDoc->GetValue(aPos));
9379
9380 // Restore these two cell values so we'd catch failures below.
9381 m_pDoc->SetValue(0,1,0, 2.0); // A2
9382 m_pDoc->SetValue(0,2,0, 4.0); // A3
9383 // Hide rows 2 to 4.
9384 m_pDoc->SetRowHidden(1,3,0, true);
9385 // Matrix in K7, array of references as OFFSET result.
9386 m_pDoc->InsertMatrixFormula(10, 6, 10, 6, aMark, "=SUM(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
9387 aPos.Set(10,6,0);
9388 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM SUBTOTAL failed", 49.0, m_pDoc->GetValue(aPos));
9389 aPos.IncRow();
9390 // ForceArray in K8, array of references as OFFSET result.
9391 m_pDoc->SetString( aPos, "=SUMPRODUCT(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
9392 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMPRODUCT SUBTOTAL failed", 49.0, m_pDoc->GetValue(aPos));
9393
9394 m_pDoc->DeleteTab(0);
9395 }
9396
9397 // tdf#115493 jump commands return the matrix result instead of the reference
9398 // list array.
testFuncJumpMatrixArrayIF()9399 void TestFormula::testFuncJumpMatrixArrayIF()
9400 {
9401 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9402 m_pDoc->InsertTab(0, "Test");
9403
9404 m_pDoc->SetString(0,0,0, "a"); // A1
9405 std::vector<std::vector<const char*>> aData = {
9406 { "a", "1" },
9407 { "b", "2" },
9408 { "a", "4" }
9409 }; // A7:B9
9410 insertRangeData(m_pDoc, ScAddress(0,6,0), aData);
9411
9412 ScMarkData aMark(m_pDoc->GetSheetLimits());
9413 aMark.SelectOneTable(0);
9414
9415 // Matrix in C10, summing B7,B9
9416 m_pDoc->InsertMatrixFormula( 2,9, 2,9, aMark, "=SUM(IF(EXACT(A7:A9;A$1);B7:B9;0))");
9417 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C10 failed", 5.0, m_pDoc->GetValue(ScAddress(2,9,0)));
9418
9419 // Matrix in C11, summing B7,B9
9420 m_pDoc->InsertMatrixFormula( 2,10, 2,10, aMark,
9421 "=SUM(IF(EXACT(OFFSET(A7;0;0):OFFSET(A7;2;0);A$1);OFFSET(A7;0;1):OFFSET(A7;2;1);0))");
9422 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C11 failed", 5.0, m_pDoc->GetValue(ScAddress(2,10,0)));
9423
9424 m_pDoc->DeleteTab(0);
9425 }
9426
9427 // tdf#123477 OFFSET() returns the matrix result instead of the reference list
9428 // array if result is not used as ReferenceOrRefArray.
testFuncJumpMatrixArrayOFFSET()9429 void TestFormula::testFuncJumpMatrixArrayOFFSET()
9430 {
9431 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9432 m_pDoc->InsertTab(0, "Test");
9433
9434 std::vector<std::vector<const char*>> aData = {
9435 { "abc" },
9436 { "bcd" },
9437 { "cde" }
9438 };
9439 insertRangeData(m_pDoc, ScAddress(0,0,0), aData); // A1:A3
9440
9441 ScMarkData aMark(m_pDoc->GetSheetLimits());
9442 aMark.SelectOneTable(0);
9443
9444 // Matrix in C5:C7, COLUMN()-3 here offsets by 0 but the entire expression
9445 // is in array/matrix context.
9446 m_pDoc->InsertMatrixFormula( 2,4, 2,6, aMark, "=FIND(\"c\";OFFSET(A1:A3;0;COLUMN()-3))");
9447 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C5 failed", 3.0, m_pDoc->GetValue(ScAddress(2,4,0)));
9448 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C6 failed", 2.0, m_pDoc->GetValue(ScAddress(2,5,0)));
9449 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C7 failed", 1.0, m_pDoc->GetValue(ScAddress(2,6,0)));
9450
9451 m_pDoc->DeleteTab(0);
9452 }
9453
9454 // Test iterations with circular chain of references.
testIterations()9455 void TestFormula::testIterations()
9456 {
9457 ScDocOptions aDocOpts = m_pDoc->GetDocOptions();
9458 aDocOpts.SetIter( true );
9459 m_pDoc->SetDocOptions( aDocOpts );
9460
9461 m_pDoc->InsertTab(0, "Test");
9462
9463 m_pDoc->SetValue( 0, 0, 0, 0.01 ); // A1
9464 m_pDoc->SetString( 0, 1, 0, "=A1" ); // A2
9465 m_pDoc->SetString( 0, 2, 0, "=COS(A2)" ); // A3
9466 m_pDoc->CalcAll();
9467
9468 // Establish reference cycle for the computation of the fixed point of COS() function
9469 m_pDoc->SetString( 0, 0, 0, "=A3" ); // A1
9470 m_pDoc->CalcAll();
9471
9472 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error", FormulaError::NONE, m_pDoc->GetErrCode( ScAddress( 0, 2, 0) ) );
9473 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate fixed point of cos() failed", 0.7387, m_pDoc->GetValue(0, 2, 0), 1e-4 );
9474
9475 // Modify the formula
9476 m_pDoc->SetString( 0, 2, 0, "=COS(A2)+0.001" ); // A3
9477 m_pDoc->CalcAll();
9478
9479 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error after perturbation", FormulaError::NONE, m_pDoc->GetErrCode( ScAddress( 0, 2, 0) ) );
9480 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate perturbed fixed point of cos() failed", 0.7399, m_pDoc->GetValue(0, 2, 0), 1e-4 );
9481
9482 m_pDoc->DeleteTab(0);
9483
9484 aDocOpts.SetIter( false );
9485 m_pDoc->SetDocOptions( aDocOpts );
9486 }
9487
9488 // tdf#111428 CellStoreEvent and its counter used for quick "has a column
9489 // formula cells" must point to the correct column.
testInsertColCellStoreEventSwap()9490 void TestFormula::testInsertColCellStoreEventSwap()
9491 {
9492 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9493 m_pDoc->InsertTab(0, "Test");
9494
9495 m_pDoc->SetValue( 0,0,0, 1.0 ); // A1
9496 m_pDoc->SetString( 1,0,0, "=A1" ); // B1
9497 // Insert column left of B
9498 m_pDoc->InsertCol( ScRange(1,0,0, 1,m_pDoc->MaxRow(),0));
9499 ScAddress aPos(2,0,0); // C1, new formula position
9500 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Should be formula cell having value", 1.0, m_pDoc->GetValue(aPos));
9501 // After having swapped in an empty column, editing or adding a formula
9502 // cell has to use the correct store context. To test this,
9503 // ScDocument::SetString() can't be used as it doesn't expose the behavior
9504 // in question, use ScDocFunc::SetFormulaCell() instead which actually is
9505 // also called when editing a cell and creating a formula cell.
9506 ScFormulaCell* pCell = new ScFormulaCell(*m_pDoc, aPos, "=A1+1");
9507 ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
9508 rDocFunc.SetFormulaCell( aPos, pCell, false); // C1, change formula
9509 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Initial calculation failed", 2.0, m_pDoc->GetValue(aPos));
9510 m_pDoc->SetValue( 0,0,0, 2.0 ); // A1, change value
9511 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Recalculation failed", 3.0, m_pDoc->GetValue(aPos));
9512
9513 m_pDoc->DeleteTab(0);
9514 }
9515
testFormulaAfterDeleteRows()9516 void TestFormula::testFormulaAfterDeleteRows()
9517 {
9518 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
9519 m_pDoc->InsertTab(0, "Test");
9520
9521 // Fill A1:A70000 with 1.0
9522 std::vector<double> aVals(70000, 1.0);
9523 m_pDoc->SetValues(ScAddress(0, 0, 0), aVals);
9524 // Set A70001 with formula "=SUM(A1:A70000)"
9525 m_pDoc->SetString(0, 70000, 0, "=SUM(A1:A70000)");
9526
9527 // Delete rows 2:69998
9528 m_pDoc->DeleteRow(ScRange(0, 1, 0, m_pDoc->MaxCol(), 69997, 0));
9529
9530 const ScAddress aPos(0, 3, 0); // A4
9531 ASSERT_FORMULA_EQUAL(*m_pDoc, aPos, "SUM(A1:A3)", "Wrong formula in A4.");
9532
9533 ASSERT_DOUBLES_EQUAL_MESSAGE("Wrong value at A4", 3.0, m_pDoc->GetValue(aPos));
9534 }
9535
9536 CPPUNIT_TEST_SUITE_REGISTRATION(TestFormula);
9537
9538 CPPUNIT_PLUGIN_IMPLEMENT();
9539
9540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
9541