Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / qa / unit / parallelism.cxx
blob9af1daaca9e020677adf4cd6fda471738dcfef0c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 #include <sal/config.h>
4 #include <test/bootstrapfixture.hxx>
6 #include <scdll.hxx>
7 #include <sfx2/sfxmodelfactory.hxx>
9 #include "helper/qahelper.hxx"
11 #include <docsh.hxx>
12 #include <document.hxx>
13 #include <clipparam.hxx>
14 #include <markdata.hxx>
15 #include <undoblk.hxx>
16 #include <formulacell.hxx>
17 #include <formulagroup.hxx>
18 #include <scopetools.hxx>
20 #include <officecfg/Office/Calc.hxx>
22 using namespace css;
23 using namespace css::uno;
25 class ScParallelismTest : public ScBootstrapFixture
27 public:
28 ScParallelismTest();
30 virtual void setUp() override;
31 virtual void tearDown() override;
33 void getNewDocShell(ScDocShellRef& rDocShellRef);
35 void testSUMIFS();
36 void testDivision();
37 void testVLOOKUP();
38 void testVLOOKUPSUM();
39 void testSingleRef();
40 void testSUMIFImplicitRange();
41 void testFGCycleWithPlainFormulaCell1();
42 void testFGCycleWithPlainFormulaCell2();
43 void testMultipleFGColumn();
44 void testFormulaGroupSpanEval();
45 void testFormulaGroupSpanEvalNonGroup();
46 void testArrayFormulaGroup();
47 void testDependentFormulaGroupCollection();
48 void testFormulaGroupWithForwardSelfReference();
49 void testFormulaGroupsInCyclesAndWithSelfReference();
50 void testFormulaGroupsInCyclesAndWithSelfReference2();
52 CPPUNIT_TEST_SUITE(ScParallelismTest);
53 CPPUNIT_TEST(testSUMIFS);
54 CPPUNIT_TEST(testDivision);
55 CPPUNIT_TEST(testVLOOKUP);
56 CPPUNIT_TEST(testVLOOKUPSUM);
57 CPPUNIT_TEST(testSingleRef);
58 CPPUNIT_TEST(testSUMIFImplicitRange);
59 CPPUNIT_TEST(testFGCycleWithPlainFormulaCell1);
60 CPPUNIT_TEST(testFGCycleWithPlainFormulaCell2);
61 CPPUNIT_TEST(testMultipleFGColumn);
62 CPPUNIT_TEST(testFormulaGroupSpanEval);
63 CPPUNIT_TEST(testFormulaGroupSpanEvalNonGroup);
64 CPPUNIT_TEST(testArrayFormulaGroup);
65 CPPUNIT_TEST(testDependentFormulaGroupCollection);
66 CPPUNIT_TEST(testFormulaGroupWithForwardSelfReference);
67 CPPUNIT_TEST(testFormulaGroupsInCyclesAndWithSelfReference);
68 CPPUNIT_TEST(testFormulaGroupsInCyclesAndWithSelfReference2);
69 CPPUNIT_TEST_SUITE_END();
71 private:
73 bool getThreadingFlag();
74 void setThreadingFlag(bool bSet);
76 static ScUndoCut* cutToClip(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pClipDoc, bool bCreateUndo);
77 static void pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc);
79 ScDocument *m_pDoc;
81 ScDocShellRef m_xDocShell;
82 bool m_bThreadingFlagCfg;
85 ScParallelismTest::ScParallelismTest()
86 : ScBootstrapFixture( "sc/qa/unit/data" )
90 bool ScParallelismTest::getThreadingFlag()
92 return officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
95 void ScParallelismTest::setThreadingFlag( bool bSet )
97 std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
98 officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::set(bSet, xBatch);
99 xBatch->commit();
102 ScUndoCut* ScParallelismTest::cutToClip(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pClipDoc, bool bCreateUndo)
104 ScDocument* pSrcDoc = &rDocSh.GetDocument();
106 ScClipParam aClipParam(rRange, true);
107 ScMarkData aMark(MAXROW, MAXCOL);
108 aMark.SetMarkArea(rRange);
109 pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false);
111 // Taken from ScViewFunc::CutToClip()
112 ScDocumentUniquePtr pUndoDoc;
113 if (bCreateUndo)
115 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
116 pUndoDoc->InitUndoSelected( pSrcDoc, aMark );
117 // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
118 ScRange aCopyRange = rRange;
119 aCopyRange.aStart.SetTab(0);
120 aCopyRange.aEnd.SetTab(pSrcDoc->GetTableCount()-1);
121 pSrcDoc->CopyToDocument( aCopyRange,
122 (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS,
123 false, *pUndoDoc );
126 aMark.MarkToMulti();
127 pSrcDoc->DeleteSelection( InsertDeleteFlags::ALL, aMark );
128 aMark.MarkToSimple();
130 if (pUndoDoc)
131 return new ScUndoCut( &rDocSh, rRange, rRange.aEnd, aMark, std::move(pUndoDoc) );
133 return nullptr;
136 void ScParallelismTest::pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc)
138 ScMarkData aMark(MAXROW, MAXCOL);
139 aMark.SetMarkArea(rDestRange);
140 pDestDoc->CopyFromClip(rDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pClipDoc);
143 void ScParallelismTest::setUp()
145 test::BootstrapFixture::setUp();
147 ScDLL::Init();
149 getNewDocShell(m_xDocShell);
150 m_pDoc = &m_xDocShell->GetDocument();
152 sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly();
154 m_bThreadingFlagCfg = getThreadingFlag();
155 if (!m_bThreadingFlagCfg)
156 setThreadingFlag(true);
159 void ScParallelismTest::tearDown()
161 // Restore threading flag
162 if (!m_bThreadingFlagCfg)
163 setThreadingFlag(false);
165 test::BootstrapFixture::tearDown();
168 void ScParallelismTest::getNewDocShell( ScDocShellRef& rDocShellRef )
170 rDocShellRef = new ScDocShell(
171 SfxModelFlags::EMBEDDED_OBJECT |
172 SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
173 SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
176 void ScParallelismTest::testSUMIFS()
178 m_pDoc->InsertTab(0, "1");
180 m_pDoc->SetValue(0, 0, 0, 1001);
182 for (auto i = 1; i < 1000; i++)
184 /*A*/
185 if (i%19)
186 m_pDoc->SetValue(0, i, 0, i/10 + 1000);
187 else
188 m_pDoc->SetValue(0, i, 0, 123456);
189 /*B*/ m_pDoc->SetValue(1, i, 0, i%10);
190 /*C*/ m_pDoc->SetValue(2, i, 0, i%5);
192 /*F*/ m_pDoc->SetValue(5, i, 0, i%17 + i%13);
194 /*L*/ m_pDoc->SetValue(11, i, 0, i%10);
195 /*M*/ m_pDoc->SetValue(12, i, 0, i%5);
198 for (auto i = 1; i < 1000; i++)
200 // For instance P389 will contain the formula:
201 // =SUMIFS($F$2:$F$1000; $A$2:$A$1000; A$1; $B$2:$B$1000; $L389; $C$2:$C$1000; $M389)
203 // In other words, it will sum those values in F2:1000 where the A value matches A1 (1001),
204 // the B value matches L389 and the C value matches M389. (There should be just one such
205 // value, so the formula is actually simply used to pick out that single value from the F
206 // column where A,B,C match. Silly, but that is how SUMIFS is used in some corners of the
207 // real world, apparently.)
209 /*P*/ m_pDoc->SetFormula(ScAddress(15, i, 0),
210 "=SUMIFS($F$2:$F$1000; "
211 "$A$2:$A$1000; A$1; "
212 "$B$2:$B$1000; $L" + OUString::number(i+1) + "; "
213 "$C$2:$C$1000; $M" + OUString::number(i+1) +
214 ")",
215 formula::FormulaGrammar::GRAM_NATIVE_UI);
218 m_xDocShell->DoHardRecalc();
220 #if 1
221 OUString sFormula;
223 std::cerr << "A1=" << m_pDoc->GetValue(0, 0, 0) << std::endl;
225 std::cerr << " A,B,C F L,M" << std::endl;
226 for (auto i = 1; i < 30; i++)
228 std::cerr <<
229 i+1 << ": " <<
230 m_pDoc->GetValue(0, i, 0) << "," <<
231 m_pDoc->GetValue(1, i, 0) << "," <<
232 m_pDoc->GetValue(2, i, 0) << " " <<
233 m_pDoc->GetValue(5, i, 0) << " " <<
234 m_pDoc->GetValue(11, i, 0) << "," <<
235 m_pDoc->GetValue(12, i, 0) << " \"";
236 m_pDoc->GetFormula(15, i, 0, sFormula);
237 std::cerr << sFormula << "\": \"" <<
238 m_pDoc->GetString(15, i, 0) << "\": " <<
239 m_pDoc->GetValue(15, i, 0) << std::endl;
241 #endif
243 for (auto i = 1; i < 1000; i++)
245 OString sMessage = "At row " + OString::number(i+1);
246 if ((10+i%10)%19)
247 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), m_pDoc->GetValue(5, 10+i%10, 0), m_pDoc->GetValue(15, i, 0), 1e-10);
248 else
249 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), 0, m_pDoc->GetValue(15, i, 0), 1e-10);
252 m_pDoc->DeleteTab(0);
255 void ScParallelismTest::testDivision()
257 m_pDoc->InsertTab(0, "1");
259 for (auto i = 1; i < 1000; i++)
261 /*A*/ m_pDoc->SetValue(0, i, 0, i);
262 /*B*/ m_pDoc->SetValue(1, i, 0, i%10);
263 /*C*/ m_pDoc->SetFormula(ScAddress(2, i, 0),
264 "=A" + OUString::number(i+1) + "/B" + OUString::number(i+1),
265 formula::FormulaGrammar::GRAM_NATIVE_UI);
268 m_xDocShell->DoHardRecalc();
270 for (auto i = 1; i < 1000; i++)
272 OString sMessage = "At row " + OString::number(i+1);
273 if (i%10)
274 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), static_cast<double>(i)/(i%10), m_pDoc->GetValue(2, i, 0), 1e-10);
275 else
276 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#DIV/0!"), m_pDoc->GetString(2, i, 0));
279 m_pDoc->DeleteTab(0);
282 void ScParallelismTest::testVLOOKUP()
284 m_pDoc->InsertTab(0, "1");
286 for (auto i = 1; i < 2000; i++)
288 if (i == 1042)
289 m_pDoc->SetValue(0, i, 0, 1042.42);
290 else if (i%5)
291 m_pDoc->SetValue(0, i, 0, i);
292 else
293 m_pDoc->SetValue(0, i, 0, i+0.1);
295 if (i%2)
296 m_pDoc->SetValue(1, i, 0, i*10);
297 else
298 m_pDoc->SetString(1, i, 0, "N" + OUString::number(i*10));
300 if (i < 1000)
302 m_pDoc->SetFormula(ScAddress(2, i, 0),
303 "=VLOOKUP(" + OUString::number(i) + "; "
304 "A$2:B$2000; 2; 0)",
305 formula::FormulaGrammar::GRAM_NATIVE_UI);
308 else
310 if (i == 1042)
311 m_pDoc->SetFormula(ScAddress(2, i, 0),
312 "=VLOOKUP(1042.42; "
313 "A$2:B$2000; 2; 0)",
314 formula::FormulaGrammar::GRAM_NATIVE_UI);
315 else
316 m_pDoc->SetFormula(ScAddress(2, i, 0),
317 "=VLOOKUP(1.234; "
318 "A$2:B$2000; 2; 0)",
319 formula::FormulaGrammar::GRAM_NATIVE_UI);
323 m_xDocShell->DoHardRecalc();
325 for (auto i = 1; i < 2000; i++)
327 OString sMessage = "At row " + OString::number(i+1);
328 if (i < 1000)
330 if (i%5)
332 if (i%2)
333 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), static_cast<double>(i*10), m_pDoc->GetValue(2, i, 0), 1e-10);
334 else
335 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("N" + OUString::number(i*10)), m_pDoc->GetString(2, i, 0));
337 else
339 // The corresponding value in A is i+0.1
340 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#N/A"), m_pDoc->GetString(2, i, 0));
343 else
345 if (i == 1042)
346 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("N" + OUString::number(i*10)), m_pDoc->GetString(2, i, 0));
347 else
348 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#N/A"), m_pDoc->GetString(2, i, 0));
352 m_pDoc->DeleteTab(0);
355 void ScParallelismTest::testVLOOKUPSUM()
357 m_pDoc->InsertTab(0, "1");
359 const size_t nNumRows = 2048;
360 OUString aTableRef = "$A$1:$B$" + OUString::number(nNumRows);
361 for (size_t i = 0; i < nNumRows; ++i)
363 m_pDoc->SetValue(0, i, 0, static_cast<double>(i));
364 m_pDoc->SetValue(1, i, 0, static_cast<double>(5*i + 100));
365 m_pDoc->SetValue(2, i, 0, static_cast<double>(nNumRows - i - 1));
367 for (size_t i = 0; i < nNumRows; ++i)
369 OUString aArgNum = "C" + OUString::number(i+1);
370 m_pDoc->SetFormula(ScAddress(3, i, 0),
371 "=SUM(" + aArgNum + ";VLOOKUP(" + aArgNum + ";" + aTableRef + "; 2; 0)) + SUM($A1:$A2)",
372 formula::FormulaGrammar::GRAM_NATIVE_UI);
375 m_xDocShell->DoHardRecalc();
377 for (size_t i = 0; i < nNumRows; ++i)
379 OString aMsg = "At row " + OString::number(i);
380 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 6 * (nNumRows - i - 1) + 101, static_cast<size_t>(m_pDoc->GetValue(3, i, 0)));
382 m_pDoc->DeleteTab(0);
385 void ScParallelismTest::testSingleRef()
387 m_pDoc->InsertTab(0, "1");
389 const size_t nNumRows = 200;
390 for (size_t i = 0; i < nNumRows; ++i)
392 m_pDoc->SetValue(0, i, 0, static_cast<double>(i));
393 m_pDoc->SetFormula(ScAddress(1, i, 0), "=A" + OUString::number(i+1), formula::FormulaGrammar::GRAM_NATIVE_UI);
396 m_xDocShell->DoHardRecalc();
398 for (size_t i = 0; i < nNumRows; ++i)
400 OString aMsg = "At row " + OString::number(i);
401 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), i, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
403 m_pDoc->DeleteTab(0);
406 // Common test setup steps for testSUMIFImplicitRange*()
407 static void lcl_setupCommon(ScDocument* pDoc, size_t nNumRows, size_t nConstCellValue)
409 pDoc->SetValue(3, 0, 0, static_cast<double>(nConstCellValue)); // D1
410 for (size_t i = 0; i <= (nNumRows*2); ++i)
412 pDoc->SetValue(0, i, 0, static_cast<double>(i));
413 pDoc->SetFormula(ScAddress(1, i, 0),
414 "=A" + OUString::number(i+1),
415 formula::FormulaGrammar::GRAM_NATIVE_UI);
419 void ScParallelismTest::testSUMIFImplicitRange()
421 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
422 m_pDoc->InsertTab(0, "1");
424 const size_t nNumRows = 1048;
425 const size_t nConstCellValue = 20;
426 lcl_setupCommon(m_pDoc, nNumRows, nConstCellValue);
427 OUString aSrcRange = "$A$1:$A$" + OUString::number(nNumRows);
428 OUString aFormula;
429 for (size_t i = 0; i < nNumRows; ++i)
431 aFormula = "=SUMIF(" + aSrcRange + ";$D$1;$B$1)";
432 m_pDoc->SetFormula(ScAddress(2, i, 0),
433 aFormula,
434 formula::FormulaGrammar::GRAM_NATIVE_UI);
437 ScFormulaCell* pCell = m_pDoc->GetFormulaCell(ScAddress(2, 0, 0));
438 sc::AutoCalcSwitch aACSwitch2(*m_pDoc, true);
439 pCell->InterpretFormulaGroup(); // Start calculation on the F.G at C1
441 for (size_t i = 0; i < nNumRows; ++i)
443 OString aMsg = "At row " + OString::number(i);
444 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nConstCellValue, static_cast<size_t>(m_pDoc->GetValue(2, i, 0)));
446 m_pDoc->DeleteTab(0);
449 void ScParallelismTest::testFGCycleWithPlainFormulaCell1()
451 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
452 m_pDoc->InsertTab(0, "1");
453 const size_t nNumRows = 1048;
454 // Column A contains no formula-group
455 // A1 = 100
456 m_pDoc->SetValue(0, 0, 0, 100.0);
457 // A500 = B499 + 1
458 m_pDoc->SetFormula(ScAddress(0, 499, 0),
459 "=$B499 + 1",
460 formula::FormulaGrammar::GRAM_NATIVE_UI);
461 // Column B has a formula-group referencing column A.
462 OUString aFormula;
463 for (size_t i = 0; i < nNumRows; ++i)
465 aFormula = "=$A" + OUString::number(i+1) + " + 100";
466 m_pDoc->SetFormula(ScAddress(1, i, 0),
467 aFormula,
468 formula::FormulaGrammar::GRAM_NATIVE_UI);
470 m_xDocShell->DoHardRecalc();
471 // Value at A500 must be 101
472 const size_t nVal = 100;
473 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value at A500", nVal + 1, static_cast<size_t>(m_pDoc->GetValue(0, 499, 0)));
474 for (size_t i = 0; i < nNumRows; ++i)
476 OString aMsg = "Value at cell B" + OString::number(i+1);
477 size_t nExpected = nVal;
478 if (i == 0)
479 nExpected = 200;
480 else if (i == 499)
481 nExpected = 201;
482 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
484 m_pDoc->DeleteTab(0);
487 void ScParallelismTest::testFGCycleWithPlainFormulaCell2()
489 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
490 m_pDoc->InsertTab(0, "1");
491 const size_t nNumRows = 1048;
492 // Column A
493 OUString aFormula;
494 for (size_t i = 0; i < nNumRows; ++i)
496 aFormula = "=$B" + OUString::number(i+1) + " + 1";
497 m_pDoc->SetFormula(ScAddress(0, i, 0),
498 aFormula,
499 formula::FormulaGrammar::GRAM_NATIVE_UI);
501 // Column B
502 for (size_t i = 0; i < nNumRows; ++i)
504 aFormula = "=$C" + OUString::number(i+1) + " + 1";
505 m_pDoc->SetFormula(ScAddress(1, i, 0),
506 aFormula,
507 formula::FormulaGrammar::GRAM_NATIVE_UI);
510 // Column C has no FG but a cell at C500 that references A499
511 m_pDoc->SetFormula(ScAddress(2, 499, 0), // C500
512 "=$A499 + 1",
513 formula::FormulaGrammar::GRAM_NATIVE_UI);
514 m_xDocShell->DoHardRecalc();
516 size_t nExpected = 0;
517 for (size_t i = 0; i < nNumRows; ++i)
519 OString aMsg = "Value at cell A" + OString::number(i+1);
520 nExpected = 2;
521 if (i == 499) // A500 must have value = 5
522 nExpected = 5;
523 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(0, i, 0)));
524 aMsg = "Value at cell B" + OString::number(i+1);
525 nExpected = 1;
526 if (i == 499) // B500 must have value = 4
527 nExpected = 4;
528 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
531 // C500 must have value = 3
532 nExpected = 3;
533 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value at cell C500", nExpected, static_cast<size_t>(m_pDoc->GetValue(2, 499, 0)));
534 m_pDoc->DeleteTab(0);
537 static void lcl_setupMultipleFGColumn(ScDocument* pDocument, size_t nNumRowsInBlock, size_t nNumFG, size_t nOffset)
539 OUString aFormula;
540 ScAddress aAddr(1, 0, 0);
541 // Column B with multiple FG's
542 for (size_t nFGIdx = 0; nFGIdx < nNumFG; ++nFGIdx)
544 size_t nRowStart = 2*nFGIdx*nNumRowsInBlock;
545 for (size_t nRow = nRowStart; nRow < (nRowStart + nNumRowsInBlock); ++nRow)
547 aAddr.SetRow(nRow);
548 aFormula = "=$C" + OUString::number(nRow+1) + " + 0";
549 pDocument->SetFormula(aAddr, aFormula,
550 formula::FormulaGrammar::GRAM_NATIVE_UI);
551 // Fill Column C with doubles.
552 pDocument->SetValue(2, nRow, 0, static_cast<double>(nFGIdx));
556 // Column A with a single FG that depends on Column B.
557 size_t nNumRowsInRef = nNumRowsInBlock*2;
558 size_t nColAFGLen = 2*nNumRowsInBlock*nNumFG - nNumRowsInRef + 1;
559 aAddr.SetCol(0);
560 for (size_t nRow = nOffset; nRow < nColAFGLen; ++nRow)
562 aAddr.SetRow(nRow);
563 aFormula = "=SUM($B" + OUString::number(nRow+1) + ":$B" + OUString::number(nRow+nNumRowsInRef) + ")";
564 pDocument->SetFormula(aAddr, aFormula,
565 formula::FormulaGrammar::GRAM_NATIVE_UI);
569 void ScParallelismTest::testMultipleFGColumn()
571 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
572 m_pDoc->InsertTab(0, "1");
574 constexpr size_t nNumRowsInBlock = 200;
575 constexpr size_t nNumFG = 50;
576 constexpr size_t nNumRowsInRef = nNumRowsInBlock*2;
577 constexpr size_t nColAFGLen = 2*nNumRowsInBlock*nNumFG - nNumRowsInRef + 1;
578 constexpr size_t nColAStartOffset = nNumRowsInBlock/2;
579 lcl_setupMultipleFGColumn(m_pDoc, nNumRowsInBlock, nNumFG, nColAStartOffset);
581 m_xDocShell->DoHardRecalc();
583 OString aMsg;
584 // First cell in the FG in col A references nColAStartOffset cells in second formula-group of column B each having value 1.
585 size_t nExpected = nColAStartOffset;
586 size_t nIn = 0, nOut = 0;
587 for (size_t nRow = nColAStartOffset; nRow < nColAFGLen; ++nRow)
589 aMsg = "Value at Cell A" + OString::number(nRow+1);
590 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(0, nRow, 0)));
591 nIn = static_cast<size_t>(m_pDoc->GetValue(2, nRow+nNumRowsInRef, 0));
592 nOut = static_cast<size_t>(m_pDoc->GetValue(2, nRow, 0));
593 nExpected = nExpected + nIn - nOut;
596 m_pDoc->DeleteTab(0);
599 void ScParallelismTest::testFormulaGroupSpanEval()
601 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
602 m_pDoc->InsertTab(0, "1");
604 constexpr size_t nFGLen = 2048;
605 OUString aFormula;
607 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
609 aFormula = "=$C" + OUString::number(nRow+1) + " + 0";
610 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
611 formula::FormulaGrammar::GRAM_NATIVE_UI);
612 aFormula = "=SUM($B" + OUString::number(nRow+1) + ":$B" + OUString::number(nRow+2) + ")";
613 m_pDoc->SetFormula(ScAddress(0, nRow, 0), aFormula,
614 formula::FormulaGrammar::GRAM_NATIVE_UI);
617 m_xDocShell->DoHardRecalc();
619 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
621 m_pDoc->SetValue(2, nRow, 0, 1.0);
622 ScFormulaCell* pFCell = m_pDoc->GetFormulaCell(ScAddress(1, nRow, 0));
623 pFCell->SetDirtyVar();
624 pFCell = m_pDoc->GetFormulaCell(ScAddress(0, nRow, 0));
625 pFCell->SetDirtyVar();
628 constexpr size_t nSpanStart = 100;
629 constexpr size_t nSpanLen = 1024;
630 constexpr size_t nSpanEnd = nSpanStart + nSpanLen - 1;
632 m_pDoc->SetAutoCalc(true);
634 // EnsureFormulaCellResults should only calculate the specified range along with the dependent spans recursively and nothing more.
635 // The specified range is A99:A1124, and the dependent range is B99:B1125 (since A99 = SUM(B99:B100) and A1124 = SUM(B1124:B1125) )
636 bool bAnyDirty = m_pDoc->EnsureFormulaCellResults(ScRange(0, nSpanStart, 0, 0, nSpanEnd, 0));
637 CPPUNIT_ASSERT(bAnyDirty);
638 m_pDoc->SetAutoCalc(false);
640 OString aMsg;
641 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
643 size_t nExpectedA = 0, nExpectedB = 0;
644 // For nRow from 100(nSpanStart) to 1123(nSpanEnd) column A must have the value of 2 and
645 // column B should have value 1.
647 // For nRow == 1124, column A should have value 0 and column B should have value 1.
649 // For all other rows both column A and B must have value 0.
650 if (nRow >= nSpanStart)
652 if (nRow <= nSpanEnd)
654 nExpectedA = 2;
655 nExpectedB = 1;
657 else if (nRow == nSpanEnd + 1)
658 nExpectedB = 1;
661 aMsg = "Value at Cell A" + OString::number(nRow+1);
662 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpectedA, static_cast<size_t>(m_pDoc->GetValue(0, nRow, 0)));
663 aMsg = "Value at Cell B" + OString::number(nRow+1);
664 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpectedB, static_cast<size_t>(m_pDoc->GetValue(1, nRow, 0)));
667 m_pDoc->DeleteTab(0);
670 void ScParallelismTest::testFormulaGroupSpanEvalNonGroup()
672 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
673 m_pDoc->InsertTab(0, "1");
675 constexpr size_t nFGLen = 2048;
676 OUString aFormula;
678 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
680 aFormula = "=$B" + OUString::number(nRow+1) + " + 0";
681 m_pDoc->SetFormula(ScAddress(0, nRow, 0), aFormula,
682 formula::FormulaGrammar::GRAM_NATIVE_UI);
685 m_xDocShell->DoHardRecalc();
687 constexpr size_t nNumChanges = 12;
688 constexpr size_t nChangeRows[nNumChanges] = {10, 11, 12, 101, 102, 103, 251, 252, 253, 503, 671, 1029};
689 for (size_t nIdx = 0; nIdx < nNumChanges; ++nIdx)
691 size_t nRow = nChangeRows[nIdx];
692 m_pDoc->SetValue(1, nRow, 0, 1.0);
693 ScFormulaCell* pFCell = m_pDoc->GetFormulaCell(ScAddress(0, nRow, 0));
694 pFCell->SetDirtyVar();
697 m_pDoc->SetAutoCalc(true);
698 bool bAnyDirty = m_pDoc->EnsureFormulaCellResults(ScRange(0, 9, 0, 0, 1030, 0));
699 CPPUNIT_ASSERT(bAnyDirty);
700 m_pDoc->SetAutoCalc(false);
702 OString aMsg;
703 for (size_t nRow = 0, nIdx = 0; nRow < nFGLen; ++nRow)
705 size_t nExpected = 0;
706 if (nIdx < nNumChanges && nRow == nChangeRows[nIdx])
708 nExpected = 1;
709 ++nIdx;
712 aMsg = "Value at Cell A" + OString::number(nRow+1);
713 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(0, nRow, 0)));
716 m_pDoc->DeleteTab(0);
719 void ScParallelismTest::testArrayFormulaGroup()
721 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
722 m_pDoc->InsertTab(0, "1");
724 m_pDoc->SetValue(1, 0, 0, 2.0); // B1 <== 2
725 m_pDoc->SetValue(2, 0, 0, 1.0); // C1 <== 1
726 OUString aFormula;
728 for (size_t nRow = 1; nRow < 16; ++nRow)
730 m_pDoc->SetValue(0, nRow, 0, 1.0); // A2:A16 <== 1
732 if (nRow > 10)
733 continue;
735 aFormula = "=SUMPRODUCT(($A" + OUString::number(1 + nRow) +
736 ":$A" + OUString::number(499 + nRow) + ")*B$1+C$1)";
737 // Formula-group in B2:B11 with first cell = "=SUMPRODUCT(($A2:$A500)*B$1+C$1)"
738 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
739 formula::FormulaGrammar::GRAM_NATIVE_UI);
742 m_xDocShell->DoHardRecalc();
744 size_t nExpected = 529;
745 OString aMsg;
746 for (size_t nRow = 1; nRow < 11; ++nRow)
748 aMsg = "Value at Cell B" + OString::number(nRow+1);
749 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(1, nRow, 0)));
750 nExpected -= 2;
753 m_pDoc->DeleteTab(0);
756 void ScParallelismTest::testDependentFormulaGroupCollection()
758 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
759 m_pDoc->InsertTab(0, "1");
761 OUString aFormula;
763 for (size_t nRow = 0; nRow < 16; ++nRow)
765 m_pDoc->SetValue(0, nRow, 0, 1.0); // A1:A16 <== 1
767 if (nRow > 7)
768 continue;
770 // Formula-group in B1:B8 with first cell = "=SUM($A1:$A1024)"
771 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
772 ":$A" + OUString::number(1024 + nRow) + ")";
773 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
774 formula::FormulaGrammar::GRAM_NATIVE_UI);
776 // Formula-group in C1:C8 with first cell = "=SUM($K1:$K1024)"
777 aFormula = "=SUM($K" + OUString::number(1 + nRow) +
778 ":$K" + OUString::number(1024 + nRow) + ")";
779 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
780 formula::FormulaGrammar::GRAM_NATIVE_UI);
782 // Formula-group in D1:D8 with first cell = "=SUM($A1:$A1024) - $A2"
783 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
784 ":$A" + OUString::number(1024 + nRow) + ") - $A" + OUString::number(2 + nRow);
785 m_pDoc->SetFormula(ScAddress(3, nRow, 0), aFormula,
786 formula::FormulaGrammar::GRAM_NATIVE_UI);
788 // Formula-group in K1:K8 with first cell = "=SUM($B1:$B1024)"
789 aFormula = "=SUM($B" + OUString::number(1 + nRow) +
790 ":$B" + OUString::number(1024 + nRow) + ")";
791 m_pDoc->SetFormula(ScAddress(10, nRow, 0), aFormula,
792 formula::FormulaGrammar::GRAM_NATIVE_UI);
795 m_xDocShell->DoHardRecalc();
797 size_t nExpected[8] = { 408, 308, 224, 155, 100, 58, 28, 9 };
799 OString aMsg;
800 for (size_t nRow = 0; nRow < 8; ++nRow)
802 aMsg = "Value at Cell C" + OString::number(nRow+1);
803 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected[nRow], static_cast<size_t>(m_pDoc->GetValue(2, nRow, 0)));
806 m_pDoc->DeleteTab(0);
809 void ScParallelismTest::testFormulaGroupWithForwardSelfReference()
811 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
812 m_pDoc->InsertTab(0, "1");
814 OUString aFormula;
815 m_pDoc->SetValue(2, 4, 0, 10.0); // C5 <== 10
817 for (size_t nRow = 0; nRow < 4; ++nRow)
819 // Formula-group in B1:B4 with first cell = "=SUM($A1:$A1024) + C1"
820 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
821 ":$A" + OUString::number(1024 + nRow) + ") + C" + OUString::number(nRow + 1);
822 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
823 formula::FormulaGrammar::GRAM_NATIVE_UI);
825 // Formula-group in C1:C4 with first cell = "=SUM($A1:$A1024) + C2"
826 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
827 ":$A" + OUString::number(1024 + nRow) + ") + C" + OUString::number(nRow + 2);
828 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
829 formula::FormulaGrammar::GRAM_NATIVE_UI);
832 m_xDocShell->DoHardRecalc();
834 OString aMsg;
835 for (size_t nCol = 0; nCol < 2; ++nCol)
837 for (size_t nRow = 0; nRow < 4; ++nRow)
839 aMsg = "Value at Cell (Col = " + OString::number(nCol + 1) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
840 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 10.0, m_pDoc->GetValue(1 + nCol, nRow, 0));
844 m_pDoc->DeleteTab(0);
847 void ScParallelismTest::testFormulaGroupsInCyclesAndWithSelfReference()
849 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
850 m_pDoc->InsertTab(0, "1");
852 m_pDoc->SetValue(1, 0, 0, 1.0); // B1 <== 1
853 m_pDoc->SetValue(3, 0, 0, 2.0); // D1 <== 2
854 OUString aFormula;
856 for (size_t nRow = 0; nRow < 5; ++nRow)
858 // Formula-group in C1:C5 with first cell = "=SUM($A1:$A1024) + D1"
859 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
860 ":$A" + OUString::number(1024 + nRow) + ") + D" + OUString::number(nRow + 1);
861 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
862 formula::FormulaGrammar::GRAM_NATIVE_UI);
864 if (nRow == 0)
865 continue;
867 // nRow starts from 1 till 4 (for D2 to D5).
868 // Formula-group in D2:D5 with first cell = "=SUM($A1:$A1024) + D1 + B2"
869 aFormula = "=SUM($A" + OUString::number(nRow) +
870 ":$A" + OUString::number(1023 + nRow) + ") + D" + OUString::number(nRow) +
871 " + B" + OUString::number(nRow + 1);
872 m_pDoc->SetFormula(ScAddress(3, nRow, 0), aFormula,
873 formula::FormulaGrammar::GRAM_NATIVE_UI);
875 // Formula-group in B2:B5 with first cell = "=SUM($A1:$A1024) + C1 + B1"
876 aFormula = "=SUM($A" + OUString::number(nRow) +
877 ":$A" + OUString::number(1023 + nRow) + ") + C" + OUString::number(nRow) +
878 " + B" + OUString::number(nRow);
879 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
880 formula::FormulaGrammar::GRAM_NATIVE_UI);
883 m_xDocShell->DoHardRecalc();
884 m_pDoc->SetAutoCalc(true);
886 const ScRange aChangeRange(1, 1, 0, 1, 4, 0); // B2:B5
887 ScMarkData aMark(MAXROW, MAXCOL);
888 aMark.SelectOneTable(0);
890 // Set up clip document.
891 ScDocument aClipDoc(SCDOCMODE_CLIP);
892 aClipDoc.ResetClip(m_pDoc, &aMark);
893 // Cut B1:B2 to clipboard.
894 cutToClip(*m_xDocShell, aChangeRange, &aClipDoc, false);
895 pasteFromClip(m_pDoc, aChangeRange, &aClipDoc);
897 double fExpected[3][5] = {
898 { 1, 3, 8, 21, 55 },
899 { 2, 5, 13, 34, 89 },
900 { 2, 5, 13, 34, 89 }
903 OString aMsg;
904 for (size_t nCol = 0; nCol < 3; ++nCol)
906 for (size_t nRow = 0; nRow < 5; ++nRow)
908 aMsg = "Value at Cell (Col = " + OString::number(nCol + 1) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
909 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), fExpected[nCol][nRow], m_pDoc->GetValue(1 + nCol, nRow, 0));
913 m_pDoc->DeleteTab(0);
916 void ScParallelismTest::testFormulaGroupsInCyclesAndWithSelfReference2()
918 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
919 m_pDoc->InsertTab(0, "1");
921 m_pDoc->SetValue(1, 0, 0, 1.0); // B1 <== 1
922 m_pDoc->SetValue(3, 0, 0, 2.0); // D1 <== 2
923 m_pDoc->SetValue(4, 0, 0, 1.0); // E1 <== 1
924 OUString aFormula;
926 for (size_t nRow = 0; nRow < 5; ++nRow)
928 // Formula-group in C1:C5 with first cell = "=SUM($A1:$A1024) + D1 + E1"
929 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
930 ":$A" + OUString::number(1024 + nRow) + ") + D" + OUString::number(nRow + 1) +
931 " + E" + OUString::number(nRow + 1);
932 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
933 formula::FormulaGrammar::GRAM_NATIVE_UI);
935 if (nRow == 0)
936 continue;
938 // Formula-group in B2:B5 with first cell = "=SUM($A1:$A1024) + C1 + B1"
939 aFormula = "=SUM($A" + OUString::number(nRow) +
940 ":$A" + OUString::number(1023 + nRow) + ") + C" + OUString::number(nRow) +
941 " + B" + OUString::number(nRow);
942 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
943 formula::FormulaGrammar::GRAM_NATIVE_UI);
945 // Formula-group in D2:D5 with first cell = "=SUM($A1:$A1024) + D1 + B2"
946 aFormula = "=SUM($A" + OUString::number(nRow) +
947 ":$A" + OUString::number(1023 + nRow) + ") + D" + OUString::number(nRow) +
948 " + B" + OUString::number(nRow + 1);
949 m_pDoc->SetFormula(ScAddress(3, nRow, 0), aFormula,
950 formula::FormulaGrammar::GRAM_NATIVE_UI);
952 // Formula-group in E2:E5 with first cell = "=SUM($A1:$A1024) + E1 + D2"
953 aFormula = "=SUM($A" + OUString::number(nRow) +
954 ":$A" + OUString::number(1023 + nRow) + ") + E" + OUString::number(nRow) +
955 " + D" + OUString::number(nRow + 1);
956 m_pDoc->SetFormula(ScAddress(4, nRow, 0), aFormula,
957 formula::FormulaGrammar::GRAM_NATIVE_UI);
960 m_xDocShell->DoHardRecalc();
962 double fExpected[4][5] = {
963 { 1, 4, 17, 70, 286 },
964 { 3, 13, 53, 216, 881 },
965 { 2, 6, 23, 93, 379 },
966 { 1, 7, 30, 123, 502 }
969 OString aMsg;
970 for (size_t nCol = 0; nCol < 4; ++nCol)
972 for (size_t nRow = 0; nRow < 5; ++nRow)
974 aMsg = "Value at Cell (Col = " + OString::number(nCol + 1) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
975 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), fExpected[nCol][nRow], m_pDoc->GetValue(1 + nCol, nRow, 0));
979 m_pDoc->DeleteTab(0);
982 CPPUNIT_TEST_SUITE_REGISTRATION(ScParallelismTest);
984 CPPUNIT_PLUGIN_IMPLEMENT();
986 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */