Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / qa / unit / ucalc_parallelism.cxx
blob8996fb5fdc7b35d946da987adc10d9565fd4aa5c
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 <sfx2/sfxmodelfactory.hxx>
8 #include "helper/qahelper.hxx"
10 #include <docsh.hxx>
11 #include <document.hxx>
12 #include <clipparam.hxx>
13 #include <markdata.hxx>
14 #include <undoblk.hxx>
15 #include <formulacell.hxx>
16 #include <formulagroup.hxx>
17 #include <scopetools.hxx>
19 #include <officecfg/Office/Calc.hxx>
21 using namespace css;
22 using namespace css::uno;
24 // test-suite suitable for creating documents to test parallelism in
25 // with access to internal unexported symbols
27 class ScParallelismTest : public ScUcalcTestBase
29 public:
30 virtual void setUp() override;
31 virtual void tearDown() override;
33 private:
35 bool getThreadingFlag() const;
36 void setThreadingFlag(bool bSet);
38 bool m_bThreadingFlagCfg;
41 bool ScParallelismTest::getThreadingFlag() const
43 return officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
46 void ScParallelismTest::setThreadingFlag( bool bSet )
48 std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
49 officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::set(bSet, xBatch);
50 xBatch->commit();
53 void ScParallelismTest::setUp()
55 ScUcalcTestBase::setUp();
57 sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly();
59 m_bThreadingFlagCfg = getThreadingFlag();
60 if (!m_bThreadingFlagCfg)
61 setThreadingFlag(true);
64 void ScParallelismTest::tearDown()
66 // Restore threading flag
67 if (!m_bThreadingFlagCfg)
68 setThreadingFlag(false);
70 ScUcalcTestBase::tearDown();
73 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testSUMIFS)
75 m_pDoc->InsertTab(0, "1");
77 m_pDoc->SetValue(0, 0, 0, 1001);
79 for (auto i = 1; i < 1000; i++)
81 /*A*/
82 if (i%19)
83 m_pDoc->SetValue(0, i, 0, i/10 + 1000);
84 else
85 m_pDoc->SetValue(0, i, 0, 123456);
86 /*B*/ m_pDoc->SetValue(1, i, 0, i%10);
87 /*C*/ m_pDoc->SetValue(2, i, 0, i%5);
89 /*F*/ m_pDoc->SetValue(5, i, 0, i%17 + i%13);
91 /*L*/ m_pDoc->SetValue(11, i, 0, i%10);
92 /*M*/ m_pDoc->SetValue(12, i, 0, i%5);
95 for (auto i = 1; i < 1000; i++)
97 // For instance P389 will contain the formula:
98 // =SUMIFS($F$2:$F$1000; $A$2:$A$1000; A$1; $B$2:$B$1000; $L389; $C$2:$C$1000; $M389)
100 // In other words, it will sum those values in F2:1000 where the A value matches A1 (1001),
101 // the B value matches L389 and the C value matches M389. (There should be just one such
102 // value, so the formula is actually simply used to pick out that single value from the F
103 // column where A,B,C match. Silly, but that is how SUMIFS is used in some corners of the
104 // real world, apparently.)
106 /*P*/ m_pDoc->SetFormula(ScAddress(15, i, 0),
107 "=SUMIFS($F$2:$F$1000; "
108 "$A$2:$A$1000; A$1; "
109 "$B$2:$B$1000; $L" + OUString::number(i+1) + "; "
110 "$C$2:$C$1000; $M" + OUString::number(i+1) +
111 ")",
112 formula::FormulaGrammar::GRAM_NATIVE_UI);
115 m_xDocShell->DoHardRecalc();
117 #if 1
118 OUString sFormula;
120 std::cerr << "A1=" << m_pDoc->GetValue(0, 0, 0) << std::endl;
122 std::cerr << " A,B,C F L,M" << std::endl;
123 for (auto i = 1; i < 30; i++)
125 std::cerr <<
126 i+1 << ": " <<
127 m_pDoc->GetValue(0, i, 0) << "," <<
128 m_pDoc->GetValue(1, i, 0) << "," <<
129 m_pDoc->GetValue(2, i, 0) << " " <<
130 m_pDoc->GetValue(5, i, 0) << " " <<
131 m_pDoc->GetValue(11, i, 0) << "," <<
132 m_pDoc->GetValue(12, i, 0) << " \"";
133 sFormula = m_pDoc->GetFormula(15, i, 0);
134 std::cerr << sFormula << "\": \"" <<
135 m_pDoc->GetString(15, i, 0) << "\": " <<
136 m_pDoc->GetValue(15, i, 0) << std::endl;
138 #endif
140 for (auto i = 1; i < 1000; i++)
142 OString sMessage = "At row " + OString::number(i+1);
143 if ((10+i%10)%19)
144 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), m_pDoc->GetValue(5, 10+i%10, 0), m_pDoc->GetValue(15, i, 0), 1e-10);
145 else
146 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), 0, m_pDoc->GetValue(15, i, 0), 1e-10);
149 m_pDoc->DeleteTab(0);
152 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testDivision)
154 m_pDoc->InsertTab(0, "1");
156 for (auto i = 1; i < 1000; i++)
158 /*A*/ m_pDoc->SetValue(0, i, 0, i);
159 /*B*/ m_pDoc->SetValue(1, i, 0, i%10);
160 /*C*/ m_pDoc->SetFormula(ScAddress(2, i, 0),
161 "=A" + OUString::number(i+1) + "/B" + OUString::number(i+1),
162 formula::FormulaGrammar::GRAM_NATIVE_UI);
165 m_xDocShell->DoHardRecalc();
167 for (auto i = 1; i < 1000; i++)
169 OString sMessage = "At row " + OString::number(i+1);
170 if (i%10)
171 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), static_cast<double>(i)/(i%10), m_pDoc->GetValue(2, i, 0), 1e-10);
172 else
173 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#DIV/0!"), m_pDoc->GetString(2, i, 0));
176 m_pDoc->DeleteTab(0);
179 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testVLOOKUP)
181 m_pDoc->InsertTab(0, "1");
183 for (auto i = 1; i < 2000; i++)
185 if (i == 1042)
186 m_pDoc->SetValue(0, i, 0, 1042.42);
187 else if (i%5)
188 m_pDoc->SetValue(0, i, 0, i);
189 else
190 m_pDoc->SetValue(0, i, 0, i+0.1);
192 if (i%2)
193 m_pDoc->SetValue(1, i, 0, i*10);
194 else
195 m_pDoc->SetString(1, i, 0, "N" + OUString::number(i*10));
197 if (i < 1000)
199 m_pDoc->SetFormula(ScAddress(2, i, 0),
200 "=VLOOKUP(" + OUString::number(i) + "; "
201 "A$2:B$2000; 2; 0)",
202 formula::FormulaGrammar::GRAM_NATIVE_UI);
205 else
207 if (i == 1042)
208 m_pDoc->SetFormula(ScAddress(2, i, 0),
209 "=VLOOKUP(1042.42; "
210 "A$2:B$2000; 2; 0)",
211 formula::FormulaGrammar::GRAM_NATIVE_UI);
212 else
213 m_pDoc->SetFormula(ScAddress(2, i, 0),
214 "=VLOOKUP(1.234; "
215 "A$2:B$2000; 2; 0)",
216 formula::FormulaGrammar::GRAM_NATIVE_UI);
220 m_xDocShell->DoHardRecalc();
222 for (auto i = 1; i < 2000; i++)
224 OString sMessage = "At row " + OString::number(i+1);
225 if (i < 1000)
227 if (i%5)
229 if (i%2)
230 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), static_cast<double>(i*10), m_pDoc->GetValue(2, i, 0), 1e-10);
231 else
232 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("N" + OUString::number(i*10)), m_pDoc->GetString(2, i, 0));
234 else
236 // The corresponding value in A is i+0.1
237 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#N/A"), m_pDoc->GetString(2, i, 0));
240 else
242 if (i == 1042)
243 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("N" + OUString::number(i*10)), m_pDoc->GetString(2, i, 0));
244 else
245 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#N/A"), m_pDoc->GetString(2, i, 0));
249 m_pDoc->DeleteTab(0);
252 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testVLOOKUPSUM)
254 m_pDoc->InsertTab(0, "1");
256 const size_t nNumRows = 2048;
257 OUString aTableRef = "$A$1:$B$" + OUString::number(nNumRows);
258 for (size_t i = 0; i < nNumRows; ++i)
260 m_pDoc->SetValue(0, i, 0, static_cast<double>(i));
261 m_pDoc->SetValue(1, i, 0, static_cast<double>(5*i + 100));
262 m_pDoc->SetValue(2, i, 0, static_cast<double>(nNumRows - i - 1));
264 for (size_t i = 0; i < nNumRows; ++i)
266 OUString aArgNum = "C" + OUString::number(i+1);
267 m_pDoc->SetFormula(ScAddress(3, i, 0),
268 "=SUM(" + aArgNum + ";VLOOKUP(" + aArgNum + ";" + aTableRef + "; 2; 0)) + SUM($A1:$A2)",
269 formula::FormulaGrammar::GRAM_NATIVE_UI);
272 m_xDocShell->DoHardRecalc();
274 for (size_t i = 0; i < nNumRows; ++i)
276 OString aMsg = "At row " + OString::number(i);
277 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 6 * (nNumRows - i - 1) + 101, static_cast<size_t>(m_pDoc->GetValue(3, i, 0)));
279 m_pDoc->DeleteTab(0);
282 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testSingleRef)
284 m_pDoc->InsertTab(0, "1");
286 const size_t nNumRows = 200;
287 for (size_t i = 0; i < nNumRows; ++i)
289 m_pDoc->SetValue(0, i, 0, static_cast<double>(i));
290 m_pDoc->SetFormula(ScAddress(1, i, 0), "=A" + OUString::number(i+1), formula::FormulaGrammar::GRAM_NATIVE_UI);
293 m_xDocShell->DoHardRecalc();
295 for (size_t i = 0; i < nNumRows; ++i)
297 OString aMsg = "At row " + OString::number(i);
298 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), i, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
300 m_pDoc->DeleteTab(0);
303 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testTdf147905)
305 m_pDoc->InsertTab(0, "1");
307 OUString aFormula;
308 const size_t nNumRows = 500;
309 for (size_t i = 0; i < nNumRows; ++i)
311 m_pDoc->SetString(0, i, 0, "AAAAAAAA");
312 aFormula = "=PROPER($A" + OUString::number(i+1) + ")";
313 m_pDoc->SetFormula(ScAddress(1, i, 0),
314 aFormula,
315 formula::FormulaGrammar::GRAM_NATIVE_UI);
318 m_xDocShell->DoHardRecalc();
320 for (size_t i = 0; i < nNumRows; ++i)
322 OString aMsg = "At row " + OString::number(i);
323 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), OUString("AAAAAAAA"), m_pDoc->GetString(0, i, 0));
325 // Without the fix in place, this test would have failed here
326 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), OUString("Aaaaaaaa"), m_pDoc->GetString(1, i, 0));
328 m_pDoc->DeleteTab(0);
331 // Common test setup steps for testSUMIFImplicitRange*()
332 static void lcl_setupCommon(ScDocument* pDoc, size_t nNumRows, size_t nConstCellValue)
334 pDoc->SetValue(3, 0, 0, static_cast<double>(nConstCellValue)); // D1
335 for (size_t i = 0; i <= (nNumRows*2); ++i)
337 pDoc->SetValue(0, i, 0, static_cast<double>(i));
338 pDoc->SetFormula(ScAddress(1, i, 0),
339 "=A" + OUString::number(i+1),
340 formula::FormulaGrammar::GRAM_NATIVE_UI);
344 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testSUMIFImplicitRange)
346 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
347 m_pDoc->InsertTab(0, "1");
349 const size_t nNumRows = 1048;
350 const size_t nConstCellValue = 20;
351 lcl_setupCommon(m_pDoc, nNumRows, nConstCellValue);
352 OUString aSrcRange = "$A$1:$A$" + OUString::number(nNumRows);
353 OUString aFormula;
354 for (size_t i = 0; i < nNumRows; ++i)
356 aFormula = "=SUMIF(" + aSrcRange + ";$D$1;$B$1)";
357 m_pDoc->SetFormula(ScAddress(2, i, 0),
358 aFormula,
359 formula::FormulaGrammar::GRAM_NATIVE_UI);
362 ScFormulaCell* pCell = m_pDoc->GetFormulaCell(ScAddress(2, 0, 0));
363 sc::AutoCalcSwitch aACSwitch2(*m_pDoc, true);
364 pCell->InterpretFormulaGroup(); // Start calculation on the F.G at C1
366 for (size_t i = 0; i < nNumRows; ++i)
368 OString aMsg = "At row " + OString::number(i);
369 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nConstCellValue, static_cast<size_t>(m_pDoc->GetValue(2, i, 0)));
371 m_pDoc->DeleteTab(0);
374 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFGCycleWithPlainFormulaCell1)
376 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
377 m_pDoc->InsertTab(0, "1");
378 const size_t nNumRows = 1048;
379 // Column A contains no formula-group
380 // A1 = 100
381 m_pDoc->SetValue(0, 0, 0, 100.0);
382 // A500 = B499 + 1
383 m_pDoc->SetFormula(ScAddress(0, 499, 0),
384 "=$B499 + 1",
385 formula::FormulaGrammar::GRAM_NATIVE_UI);
386 // Column B has a formula-group referencing column A.
387 OUString aFormula;
388 for (size_t i = 0; i < nNumRows; ++i)
390 aFormula = "=$A" + OUString::number(i+1) + " + 100";
391 m_pDoc->SetFormula(ScAddress(1, i, 0),
392 aFormula,
393 formula::FormulaGrammar::GRAM_NATIVE_UI);
395 m_xDocShell->DoHardRecalc();
396 // Value at A500 must be 101
397 const size_t nVal = 100;
398 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value at A500", nVal + 1, static_cast<size_t>(m_pDoc->GetValue(0, 499, 0)));
399 for (size_t i = 0; i < nNumRows; ++i)
401 OString aMsg = "Value at cell B" + OString::number(i+1);
402 size_t nExpected = nVal;
403 if (i == 0)
404 nExpected = 200;
405 else if (i == 499)
406 nExpected = 201;
407 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
409 m_pDoc->DeleteTab(0);
412 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFGCycleWithPlainFormulaCell2)
414 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
415 m_pDoc->InsertTab(0, "1");
416 const size_t nNumRows = 1048;
417 // Column A
418 OUString aFormula;
419 for (size_t i = 0; i < nNumRows; ++i)
421 aFormula = "=$B" + OUString::number(i+1) + " + 1";
422 m_pDoc->SetFormula(ScAddress(0, i, 0),
423 aFormula,
424 formula::FormulaGrammar::GRAM_NATIVE_UI);
426 // Column B
427 for (size_t i = 0; i < nNumRows; ++i)
429 aFormula = "=$C" + OUString::number(i+1) + " + 1";
430 m_pDoc->SetFormula(ScAddress(1, i, 0),
431 aFormula,
432 formula::FormulaGrammar::GRAM_NATIVE_UI);
435 // Column C has no FG but a cell at C500 that references A499
436 m_pDoc->SetFormula(ScAddress(2, 499, 0), // C500
437 "=$A499 + 1",
438 formula::FormulaGrammar::GRAM_NATIVE_UI);
439 m_xDocShell->DoHardRecalc();
441 size_t nExpected = 0;
442 for (size_t i = 0; i < nNumRows; ++i)
444 OString aMsg = "Value at cell A" + OString::number(i+1);
445 nExpected = 2;
446 if (i == 499) // A500 must have value = 5
447 nExpected = 5;
448 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(0, i, 0)));
449 aMsg = "Value at cell B" + OString::number(i+1);
450 nExpected = 1;
451 if (i == 499) // B500 must have value = 4
452 nExpected = 4;
453 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
456 // C500 must have value = 3
457 nExpected = 3;
458 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value at cell C500", nExpected, static_cast<size_t>(m_pDoc->GetValue(2, 499, 0)));
459 m_pDoc->DeleteTab(0);
462 static void lcl_setupMultipleFGColumn(ScDocument* pDocument, size_t nNumRowsInBlock, size_t nNumFG, size_t nOffset)
464 OUString aFormula;
465 ScAddress aAddr(1, 0, 0);
466 // Column B with multiple FG's
467 for (size_t nFGIdx = 0; nFGIdx < nNumFG; ++nFGIdx)
469 size_t nRowStart = 2*nFGIdx*nNumRowsInBlock;
470 for (size_t nRow = nRowStart; nRow < (nRowStart + nNumRowsInBlock); ++nRow)
472 aAddr.SetRow(nRow);
473 aFormula = "=$C" + OUString::number(nRow+1) + " + 0";
474 pDocument->SetFormula(aAddr, aFormula,
475 formula::FormulaGrammar::GRAM_NATIVE_UI);
476 // Fill Column C with doubles.
477 pDocument->SetValue(2, nRow, 0, static_cast<double>(nFGIdx));
481 // Column A with a single FG that depends on Column B.
482 size_t nNumRowsInRef = nNumRowsInBlock*2;
483 size_t nColAFGLen = 2*nNumRowsInBlock*nNumFG - nNumRowsInRef + 1;
484 aAddr.SetCol(0);
485 for (size_t nRow = nOffset; nRow < nColAFGLen; ++nRow)
487 aAddr.SetRow(nRow);
488 aFormula = "=SUM($B" + OUString::number(nRow+1) + ":$B" + OUString::number(nRow+nNumRowsInRef) + ")";
489 pDocument->SetFormula(aAddr, aFormula,
490 formula::FormulaGrammar::GRAM_NATIVE_UI);
494 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testMultipleFGColumn)
496 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
497 m_pDoc->InsertTab(0, "1");
499 constexpr size_t nNumRowsInBlock = 200;
500 constexpr size_t nNumFG = 50;
501 constexpr size_t nNumRowsInRef = nNumRowsInBlock*2;
502 constexpr size_t nColAFGLen = 2*nNumRowsInBlock*nNumFG - nNumRowsInRef + 1;
503 constexpr size_t nColAStartOffset = nNumRowsInBlock/2;
504 lcl_setupMultipleFGColumn(m_pDoc, nNumRowsInBlock, nNumFG, nColAStartOffset);
506 m_xDocShell->DoHardRecalc();
508 OString aMsg;
509 // First cell in the FG in col A references nColAStartOffset cells in second formula-group of column B each having value 1.
510 size_t nExpected = nColAStartOffset;
511 size_t nIn = 0, nOut = 0;
512 for (size_t nRow = nColAStartOffset; nRow < nColAFGLen; ++nRow)
514 aMsg = "Value at Cell A" + OString::number(nRow+1);
515 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(0, nRow, 0)));
516 nIn = static_cast<size_t>(m_pDoc->GetValue(2, nRow+nNumRowsInRef, 0));
517 nOut = static_cast<size_t>(m_pDoc->GetValue(2, nRow, 0));
518 nExpected = nExpected + nIn - nOut;
521 m_pDoc->DeleteTab(0);
524 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFormulaGroupSpanEval)
526 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
527 m_pDoc->InsertTab(0, "1");
529 constexpr size_t nFGLen = 2048;
530 OUString aFormula;
532 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
534 aFormula = "=$C" + OUString::number(nRow+1) + " + 0";
535 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
536 formula::FormulaGrammar::GRAM_NATIVE_UI);
537 aFormula = "=SUM($B" + OUString::number(nRow+1) + ":$B" + OUString::number(nRow+2) + ")";
538 m_pDoc->SetFormula(ScAddress(0, nRow, 0), aFormula,
539 formula::FormulaGrammar::GRAM_NATIVE_UI);
542 m_xDocShell->DoHardRecalc();
544 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
546 m_pDoc->SetValue(2, nRow, 0, 1.0);
547 ScFormulaCell* pFCell = m_pDoc->GetFormulaCell(ScAddress(1, nRow, 0));
548 pFCell->SetDirtyVar();
549 pFCell = m_pDoc->GetFormulaCell(ScAddress(0, nRow, 0));
550 pFCell->SetDirtyVar();
553 constexpr size_t nSpanStart = 100;
554 constexpr size_t nSpanLen = 1024;
555 constexpr size_t nSpanEnd = nSpanStart + nSpanLen - 1;
557 m_pDoc->SetAutoCalc(true);
559 // EnsureFormulaCellResults should only calculate the specified range along with the dependent spans recursively and nothing more.
560 // The specified range is A99:A1124, and the dependent range is B99:B1125 (since A99 = SUM(B99:B100) and A1124 = SUM(B1124:B1125) )
561 bool bAnyDirty = m_pDoc->EnsureFormulaCellResults(ScRange(0, nSpanStart, 0, 0, nSpanEnd, 0));
562 CPPUNIT_ASSERT(bAnyDirty);
563 m_pDoc->SetAutoCalc(false);
565 OString aMsg;
566 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
568 size_t nExpectedA = 0, nExpectedB = 0;
569 // For nRow from 100(nSpanStart) to 1123(nSpanEnd) column A must have the value of 2 and
570 // column B should have value 1.
572 // For nRow == 1124, column A should have value 0 and column B should have value 1.
574 // For all other rows both column A and B must have value 0.
575 if (nRow >= nSpanStart)
577 if (nRow <= nSpanEnd)
579 nExpectedA = 2;
580 nExpectedB = 1;
582 else if (nRow == nSpanEnd + 1)
583 nExpectedB = 1;
586 aMsg = "Value at Cell A" + OString::number(nRow+1);
587 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpectedA, static_cast<size_t>(m_pDoc->GetValue(0, nRow, 0)));
588 aMsg = "Value at Cell B" + OString::number(nRow+1);
589 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpectedB, static_cast<size_t>(m_pDoc->GetValue(1, nRow, 0)));
592 m_pDoc->DeleteTab(0);
595 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFormulaGroupSpanEvalNonGroup)
597 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
598 m_pDoc->InsertTab(0, "1");
600 constexpr size_t nFGLen = 2048;
601 OUString aFormula;
603 for (size_t nRow = 0; nRow < nFGLen; ++nRow)
605 aFormula = "=$B" + OUString::number(nRow+1) + " + 0";
606 m_pDoc->SetFormula(ScAddress(0, nRow, 0), aFormula,
607 formula::FormulaGrammar::GRAM_NATIVE_UI);
610 m_xDocShell->DoHardRecalc();
612 constexpr size_t nNumChanges = 12;
613 constexpr size_t nChangeRows[nNumChanges] = {10, 11, 12, 101, 102, 103, 251, 252, 253, 503, 671, 1029};
614 for (size_t nIdx = 0; nIdx < nNumChanges; ++nIdx)
616 size_t nRow = nChangeRows[nIdx];
617 m_pDoc->SetValue(1, nRow, 0, 1.0);
618 ScFormulaCell* pFCell = m_pDoc->GetFormulaCell(ScAddress(0, nRow, 0));
619 pFCell->SetDirtyVar();
622 m_pDoc->SetAutoCalc(true);
623 bool bAnyDirty = m_pDoc->EnsureFormulaCellResults(ScRange(0, 9, 0, 0, 1030, 0));
624 CPPUNIT_ASSERT(bAnyDirty);
625 m_pDoc->SetAutoCalc(false);
627 OString aMsg;
628 for (size_t nRow = 0, nIdx = 0; nRow < nFGLen; ++nRow)
630 size_t nExpected = 0;
631 if (nIdx < nNumChanges && nRow == nChangeRows[nIdx])
633 nExpected = 1;
634 ++nIdx;
637 aMsg = "Value at Cell A" + OString::number(nRow+1);
638 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(0, nRow, 0)));
641 m_pDoc->DeleteTab(0);
644 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testArrayFormulaGroup)
646 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
647 m_pDoc->InsertTab(0, "1");
649 m_pDoc->SetValue(1, 0, 0, 2.0); // B1 <== 2
650 m_pDoc->SetValue(2, 0, 0, 1.0); // C1 <== 1
651 OUString aFormula;
653 for (size_t nRow = 1; nRow < 16; ++nRow)
655 m_pDoc->SetValue(0, nRow, 0, 1.0); // A2:A16 <== 1
657 if (nRow > 10)
658 continue;
660 aFormula = "=SUMPRODUCT(($A" + OUString::number(1 + nRow) +
661 ":$A" + OUString::number(499 + nRow) + ")*B$1+C$1)";
662 // Formula-group in B2:B11 with first cell = "=SUMPRODUCT(($A2:$A500)*B$1+C$1)"
663 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
664 formula::FormulaGrammar::GRAM_NATIVE_UI);
667 m_xDocShell->DoHardRecalc();
669 size_t nExpected = 529;
670 OString aMsg;
671 for (size_t nRow = 1; nRow < 11; ++nRow)
673 aMsg = "Value at Cell B" + OString::number(nRow+1);
674 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected, static_cast<size_t>(m_pDoc->GetValue(1, nRow, 0)));
675 nExpected -= 2;
678 m_pDoc->DeleteTab(0);
681 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testDependentFormulaGroupCollection)
683 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
684 m_pDoc->InsertTab(0, "1");
686 OUString aFormula;
688 for (size_t nRow = 0; nRow < 16; ++nRow)
690 m_pDoc->SetValue(0, nRow, 0, 1.0); // A1:A16 <== 1
692 if (nRow > 7)
693 continue;
695 // Formula-group in B1:B8 with first cell = "=SUM($A1:$A1024)"
696 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
697 ":$A" + OUString::number(1024 + nRow) + ")";
698 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
699 formula::FormulaGrammar::GRAM_NATIVE_UI);
701 // Formula-group in C1:C8 with first cell = "=SUM($K1:$K1024)"
702 aFormula = "=SUM($K" + OUString::number(1 + nRow) +
703 ":$K" + OUString::number(1024 + nRow) + ")";
704 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
705 formula::FormulaGrammar::GRAM_NATIVE_UI);
707 // Formula-group in D1:D8 with first cell = "=SUM($A1:$A1024) - $A2"
708 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
709 ":$A" + OUString::number(1024 + nRow) + ") - $A" + OUString::number(2 + nRow);
710 m_pDoc->SetFormula(ScAddress(3, nRow, 0), aFormula,
711 formula::FormulaGrammar::GRAM_NATIVE_UI);
713 // Formula-group in K1:K8 with first cell = "=SUM($B1:$B1024)"
714 aFormula = "=SUM($B" + OUString::number(1 + nRow) +
715 ":$B" + OUString::number(1024 + nRow) + ")";
716 m_pDoc->SetFormula(ScAddress(10, nRow, 0), aFormula,
717 formula::FormulaGrammar::GRAM_NATIVE_UI);
720 m_xDocShell->DoHardRecalc();
722 size_t nExpected[8] = { 408, 308, 224, 155, 100, 58, 28, 9 };
724 OString aMsg;
725 for (size_t nRow = 0; nRow < 8; ++nRow)
727 aMsg = "Value at Cell C" + OString::number(nRow+1);
728 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nExpected[nRow], static_cast<size_t>(m_pDoc->GetValue(2, nRow, 0)));
731 m_pDoc->DeleteTab(0);
734 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFormulaGroupWithForwardSelfReference)
736 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
737 m_pDoc->InsertTab(0, "1");
739 OUString aFormula;
740 m_pDoc->SetValue(2, 4, 0, 10.0); // C5 <== 10
742 for (size_t nRow = 0; nRow < 4; ++nRow)
744 // Formula-group in B1:B4 with first cell = "=SUM($A1:$A1024) + C1"
745 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
746 ":$A" + OUString::number(1024 + nRow) + ") + C" + OUString::number(nRow + 1);
747 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
748 formula::FormulaGrammar::GRAM_NATIVE_UI);
750 // Formula-group in C1:C4 with first cell = "=SUM($A1:$A1024) + C2"
751 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
752 ":$A" + OUString::number(1024 + nRow) + ") + C" + OUString::number(nRow + 2);
753 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
754 formula::FormulaGrammar::GRAM_NATIVE_UI);
757 m_xDocShell->DoHardRecalc();
759 OString aMsg;
760 for (size_t nCol = 0; nCol < 2; ++nCol)
762 for (size_t nRow = 0; nRow < 4; ++nRow)
764 aMsg = "Value at Cell (Col = " + OString::number(nCol + 1) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
765 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 10.0, m_pDoc->GetValue(1 + nCol, nRow, 0));
769 m_pDoc->DeleteTab(0);
772 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFormulaGroupsInCyclesAndWithSelfReference)
774 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
775 m_pDoc->InsertTab(0, "1");
777 m_pDoc->SetValue(1, 0, 0, 1.0); // B1 <== 1
778 m_pDoc->SetValue(3, 0, 0, 2.0); // D1 <== 2
779 OUString aFormula;
781 for (size_t nRow = 0; nRow < 5; ++nRow)
783 // Formula-group in C1:C5 with first cell = "=SUM($A1:$A1024) + D1"
784 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
785 ":$A" + OUString::number(1024 + nRow) + ") + D" + OUString::number(nRow + 1);
786 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
787 formula::FormulaGrammar::GRAM_NATIVE_UI);
789 if (nRow == 0)
790 continue;
792 // nRow starts from 1 till 4 (for D2 to D5).
793 // Formula-group in D2:D5 with first cell = "=SUM($A1:$A1024) + D1 + B2"
794 aFormula = "=SUM($A" + OUString::number(nRow) +
795 ":$A" + OUString::number(1023 + nRow) + ") + D" + OUString::number(nRow) +
796 " + B" + OUString::number(nRow + 1);
797 m_pDoc->SetFormula(ScAddress(3, nRow, 0), aFormula,
798 formula::FormulaGrammar::GRAM_NATIVE_UI);
800 // Formula-group in B2:B5 with first cell = "=SUM($A1:$A1024) + C1 + B1"
801 aFormula = "=SUM($A" + OUString::number(nRow) +
802 ":$A" + OUString::number(1023 + nRow) + ") + C" + OUString::number(nRow) +
803 " + B" + OUString::number(nRow);
804 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
805 formula::FormulaGrammar::GRAM_NATIVE_UI);
808 m_xDocShell->DoHardRecalc();
809 m_pDoc->SetAutoCalc(true);
811 const ScRange aChangeRange(1, 1, 0, 1, 4, 0); // B2:B5
812 ScMarkData aMark(m_pDoc->GetSheetLimits());
813 aMark.SelectOneTable(0);
815 // Set up clip document.
816 ScDocument aClipDoc(SCDOCMODE_CLIP);
817 aClipDoc.ResetClip(m_pDoc, &aMark);
818 // Cut B1:B2 to clipboard.
819 cutToClip(*m_xDocShell, aChangeRange, &aClipDoc, false);
820 pasteFromClip(m_pDoc, aChangeRange, &aClipDoc);
822 double fExpected[3][5] = {
823 { 1, 3, 8, 21, 55 },
824 { 2, 5, 13, 34, 89 },
825 { 2, 5, 13, 34, 89 }
828 OString aMsg;
829 for (size_t nCol = 0; nCol < 3; ++nCol)
831 for (size_t nRow = 0; nRow < 5; ++nRow)
833 aMsg = "Value at Cell (Col = " + OString::number(nCol + 1) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
834 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), fExpected[nCol][nRow], m_pDoc->GetValue(1 + nCol, nRow, 0));
838 m_pDoc->DeleteTab(0);
841 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFormulaGroupsInCyclesAndWithSelfReference2)
843 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
844 m_pDoc->InsertTab(0, "1");
846 m_pDoc->SetValue(1, 0, 0, 1.0); // B1 <== 1
847 m_pDoc->SetValue(3, 0, 0, 2.0); // D1 <== 2
848 m_pDoc->SetValue(4, 0, 0, 1.0); // E1 <== 1
849 OUString aFormula;
851 for (size_t nRow = 0; nRow < 5; ++nRow)
853 // Formula-group in C1:C5 with first cell = "=SUM($A1:$A1024) + D1 + E1"
854 aFormula = "=SUM($A" + OUString::number(1 + nRow) +
855 ":$A" + OUString::number(1024 + nRow) + ") + D" + OUString::number(nRow + 1) +
856 " + E" + OUString::number(nRow + 1);
857 m_pDoc->SetFormula(ScAddress(2, nRow, 0), aFormula,
858 formula::FormulaGrammar::GRAM_NATIVE_UI);
860 if (nRow == 0)
861 continue;
863 // Formula-group in B2:B5 with first cell = "=SUM($A1:$A1024) + C1 + B1"
864 aFormula = "=SUM($A" + OUString::number(nRow) +
865 ":$A" + OUString::number(1023 + nRow) + ") + C" + OUString::number(nRow) +
866 " + B" + OUString::number(nRow);
867 m_pDoc->SetFormula(ScAddress(1, nRow, 0), aFormula,
868 formula::FormulaGrammar::GRAM_NATIVE_UI);
870 // Formula-group in D2:D5 with first cell = "=SUM($A1:$A1024) + D1 + B2"
871 aFormula = "=SUM($A" + OUString::number(nRow) +
872 ":$A" + OUString::number(1023 + nRow) + ") + D" + OUString::number(nRow) +
873 " + B" + OUString::number(nRow + 1);
874 m_pDoc->SetFormula(ScAddress(3, nRow, 0), aFormula,
875 formula::FormulaGrammar::GRAM_NATIVE_UI);
877 // Formula-group in E2:E5 with first cell = "=SUM($A1:$A1024) + E1 + D2"
878 aFormula = "=SUM($A" + OUString::number(nRow) +
879 ":$A" + OUString::number(1023 + nRow) + ") + E" + OUString::number(nRow) +
880 " + D" + OUString::number(nRow + 1);
881 m_pDoc->SetFormula(ScAddress(4, nRow, 0), aFormula,
882 formula::FormulaGrammar::GRAM_NATIVE_UI);
885 m_xDocShell->DoHardRecalc();
887 double fExpected[4][5] = {
888 { 1, 4, 17, 70, 286 },
889 { 3, 13, 53, 216, 881 },
890 { 2, 6, 23, 93, 379 },
891 { 1, 7, 30, 123, 502 }
894 OString aMsg;
895 for (size_t nCol = 0; nCol < 4; ++nCol)
897 for (size_t nRow = 0; nRow < 5; ++nRow)
899 aMsg = "Value at Cell (Col = " + OString::number(nCol + 1) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
900 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), fExpected[nCol][nRow], m_pDoc->GetValue(1 + nCol, nRow, 0));
904 m_pDoc->DeleteTab(0);
907 CPPUNIT_TEST_FIXTURE(ScParallelismTest, testFormulaGroupsInCyclesAndWithSelfReference3)
909 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
910 m_pDoc->InsertTab(0, "1");
912 m_pDoc->SetValue(1, 1, 0, 2.0); // B2 <== 2
913 for (size_t nRow = 1; nRow < 105; ++nRow)
915 // Formula-group in B3:B104 with first cell "=D2+0.001"
916 if( nRow != 1 )
917 m_pDoc->SetFormula(ScAddress(1, nRow, 0), "=D" + OUString::number(nRow) + "+0.001",
918 formula::FormulaGrammar::GRAM_NATIVE_UI);
919 // Formula-group in C2:C104 with first cell "=B2*1.01011"
920 m_pDoc->SetFormula(ScAddress(2, nRow, 0), "=B" + OUString::number(nRow + 1) + "*1.01011",
921 formula::FormulaGrammar::GRAM_NATIVE_UI);
922 // Formula-group in D2:C104 with first cell "=C2*1.02"
923 m_pDoc->SetFormula(ScAddress(3, nRow, 0), "=C" + OUString::number(nRow + 1) + "*1.02",
924 formula::FormulaGrammar::GRAM_NATIVE_UI);
927 m_xDocShell->DoHardRecalc();
929 // What happens with tdf#132451 is that the copy&paste C6->C5 really just sets the dirty flag
930 // for C5 and all the cells that depend on it (D5,B6,C6,D6,B7,...), and it also resets
931 // flags marking the C formula group as disabled for parallel calculation because of the cycle.
932 m_pDoc->SetFormula(ScAddress(2, 4, 0), "=B5*1.01011", formula::FormulaGrammar::GRAM_NATIVE_UI);
933 m_pDoc->GetFormulaCell(ScAddress(2,4,0))->GetCellGroup()->mbPartOfCycle = false;
934 m_pDoc->GetFormulaCell(ScAddress(2,4,0))->GetCellGroup()->meCalcState = sc::GroupCalcEnabled;
936 m_pDoc->SetAutoCalc(true);
937 // Without the fix, getting value of C5 would try to parallel-interpret formula group in B
938 // from its first dirty cell (B6), which depends on D5, which depends on C5, where the cycle
939 // would be detected and dependency check would bail out. But the result from Interpret()-ing
940 // D5 would be used and D5's dirty flag reset, with D5 value incorrect.
941 m_pDoc->GetValue(2,4,0);
943 double fExpected[2][3] = {
944 { 2.19053373572776, 2.21268003179597, 2.25693363243189 },
945 { 2.25793363243189, 2.28076134145577, 2.32637656828489 }
947 for (size_t nCol = 1; nCol < 4; ++nCol)
949 for (size_t nRow = 4; nRow < 6; ++nRow)
951 OString aMsg = "Value at Cell (Col = " + OString::number(nCol) + ", Row = " + OString::number(nRow) + ", Tab = 0)";
952 ASSERT_DOUBLES_EQUAL_MESSAGE(aMsg.getStr(), fExpected[nRow - 4][nCol - 1], m_pDoc->GetValue(nCol, nRow, 0));
956 m_pDoc->DeleteTab(0);
959 CPPUNIT_PLUGIN_IMPLEMENT();
961 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */