1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 #include <sal/config.h>
5 #include "helper/qahelper.hxx"
8 #include <document.hxx>
9 #include <markdata.hxx>
10 #include <formulacell.hxx>
11 #include <formulagroup.hxx>
12 #include <scopetools.hxx>
14 #include <officecfg/Office/Calc.hxx>
18 // test-suite suitable for creating documents to test parallelism in
19 // with access to internal unexported symbols
21 class ScParallelismTest
: public ScUcalcTestBase
24 virtual void setUp() override
;
25 virtual void tearDown() override
;
29 bool getThreadingFlag() const;
30 void setThreadingFlag(bool bSet
);
32 bool m_bThreadingFlagCfg
;
35 bool ScParallelismTest::getThreadingFlag() const
37 return officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
40 void ScParallelismTest::setThreadingFlag( bool bSet
)
42 std::shared_ptr
<comphelper::ConfigurationChanges
> xBatch(comphelper::ConfigurationChanges::create());
43 officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::set(bSet
, xBatch
);
47 void ScParallelismTest::setUp()
49 ScUcalcTestBase::setUp();
51 sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly();
53 m_bThreadingFlagCfg
= getThreadingFlag();
54 if (!m_bThreadingFlagCfg
)
55 setThreadingFlag(true);
58 void ScParallelismTest::tearDown()
60 // Restore threading flag
61 if (!m_bThreadingFlagCfg
)
62 setThreadingFlag(false);
64 ScUcalcTestBase::tearDown();
67 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testSUMIFS
)
69 m_pDoc
->InsertTab(0, u
"1"_ustr
);
71 m_pDoc
->SetValue(0, 0, 0, 1001);
73 for (auto i
= 1; i
< 1000; i
++)
77 m_pDoc
->SetValue(0, i
, 0, i
/10 + 1000);
79 m_pDoc
->SetValue(0, i
, 0, 123456);
80 /*B*/ m_pDoc
->SetValue(1, i
, 0, i
%10);
81 /*C*/ m_pDoc
->SetValue(2, i
, 0, i
%5);
83 /*F*/ m_pDoc
->SetValue(5, i
, 0, i
%17 + i
%13);
85 /*L*/ m_pDoc
->SetValue(11, i
, 0, i
%10);
86 /*M*/ m_pDoc
->SetValue(12, i
, 0, i
%5);
89 for (auto i
= 1; i
< 1000; i
++)
91 // For instance P389 will contain the formula:
92 // =SUMIFS($F$2:$F$1000; $A$2:$A$1000; A$1; $B$2:$B$1000; $L389; $C$2:$C$1000; $M389)
94 // In other words, it will sum those values in F2:1000 where the A value matches A1 (1001),
95 // the B value matches L389 and the C value matches M389. (There should be just one such
96 // value, so the formula is actually simply used to pick out that single value from the F
97 // column where A,B,C match. Silly, but that is how SUMIFS is used in some corners of the
98 // real world, apparently.)
100 /*P*/ m_pDoc
->SetFormula(ScAddress(15, i
, 0),
101 "=SUMIFS($F$2:$F$1000; "
102 "$A$2:$A$1000; A$1; "
103 "$B$2:$B$1000; $L" + OUString::number(i
+1) + "; "
104 "$C$2:$C$1000; $M" + OUString::number(i
+1) +
106 formula::FormulaGrammar::GRAM_NATIVE_UI
);
109 m_xDocShell
->DoHardRecalc();
114 std::cerr
<< "A1=" << m_pDoc
->GetValue(0, 0, 0) << std::endl
;
116 std::cerr
<< " A,B,C F L,M" << std::endl
;
117 for (auto i
= 1; i
< 30; i
++)
121 m_pDoc
->GetValue(0, i
, 0) << "," <<
122 m_pDoc
->GetValue(1, i
, 0) << "," <<
123 m_pDoc
->GetValue(2, i
, 0) << " " <<
124 m_pDoc
->GetValue(5, i
, 0) << " " <<
125 m_pDoc
->GetValue(11, i
, 0) << "," <<
126 m_pDoc
->GetValue(12, i
, 0) << " \"";
127 sFormula
= m_pDoc
->GetFormula(15, i
, 0);
128 std::cerr
<< sFormula
<< "\": \"" <<
129 m_pDoc
->GetString(15, i
, 0) << "\": " <<
130 m_pDoc
->GetValue(15, i
, 0) << std::endl
;
134 for (auto i
= 1; i
< 1000; i
++)
136 OString sMessage
= "At row " + OString::number(i
+1);
138 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), m_pDoc
->GetValue(5, 10+i
%10, 0), m_pDoc
->GetValue(15, i
, 0), 1e-10);
140 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), 0, m_pDoc
->GetValue(15, i
, 0), 1e-10);
143 m_pDoc
->DeleteTab(0);
146 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testDivision
)
148 m_pDoc
->InsertTab(0, u
"1"_ustr
);
150 for (auto i
= 1; i
< 1000; i
++)
152 /*A*/ m_pDoc
->SetValue(0, i
, 0, i
);
153 /*B*/ m_pDoc
->SetValue(1, i
, 0, i
%10);
154 /*C*/ m_pDoc
->SetFormula(ScAddress(2, i
, 0),
155 "=A" + OUString::number(i
+1) + "/B" + OUString::number(i
+1),
156 formula::FormulaGrammar::GRAM_NATIVE_UI
);
159 m_xDocShell
->DoHardRecalc();
161 for (auto i
= 1; i
< 1000; i
++)
163 OString sMessage
= "At row " + OString::number(i
+1);
165 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), static_cast<double>(i
)/(i
%10), m_pDoc
->GetValue(2, i
, 0), 1e-10);
167 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), u
"#DIV/0!"_ustr
, m_pDoc
->GetString(2, i
, 0));
170 m_pDoc
->DeleteTab(0);
173 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testVLOOKUP
)
175 m_pDoc
->InsertTab(0, u
"1"_ustr
);
177 for (auto i
= 1; i
< 2000; i
++)
180 m_pDoc
->SetValue(0, i
, 0, 1042.42);
182 m_pDoc
->SetValue(0, i
, 0, i
);
184 m_pDoc
->SetValue(0, i
, 0, i
+0.1);
187 m_pDoc
->SetValue(1, i
, 0, i
*10);
189 m_pDoc
->SetString(1, i
, 0, "N" + OUString::number(i
*10));
193 m_pDoc
->SetFormula(ScAddress(2, i
, 0),
194 "=VLOOKUP(" + OUString::number(i
) + "; "
196 formula::FormulaGrammar::GRAM_NATIVE_UI
);
202 m_pDoc
->SetFormula(ScAddress(2, i
, 0),
203 u
"=VLOOKUP(1042.42; "
204 "A$2:B$2000; 2; 0)"_ustr
,
205 formula::FormulaGrammar::GRAM_NATIVE_UI
);
207 m_pDoc
->SetFormula(ScAddress(2, i
, 0),
209 "A$2:B$2000; 2; 0)"_ustr
,
210 formula::FormulaGrammar::GRAM_NATIVE_UI
);
214 m_xDocShell
->DoHardRecalc();
216 for (auto i
= 1; i
< 2000; i
++)
218 OString sMessage
= "At row " + OString::number(i
+1);
224 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), static_cast<double>(i
*10), m_pDoc
->GetValue(2, i
, 0), 1e-10);
226 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), OUString("N" + OUString::number(i
*10)), m_pDoc
->GetString(2, i
, 0));
230 // The corresponding value in A is i+0.1
231 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), u
"#N/A"_ustr
, m_pDoc
->GetString(2, i
, 0));
237 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), OUString("N" + OUString::number(i
*10)), m_pDoc
->GetString(2, i
, 0));
239 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), u
"#N/A"_ustr
, m_pDoc
->GetString(2, i
, 0));
243 m_pDoc
->DeleteTab(0);
246 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testVLOOKUPSUM
)
248 m_pDoc
->InsertTab(0, u
"1"_ustr
);
250 const size_t nNumRows
= 2048;
251 OUString aTableRef
= "$A$1:$B$" + OUString::number(nNumRows
);
252 for (size_t i
= 0; i
< nNumRows
; ++i
)
254 m_pDoc
->SetValue(0, i
, 0, static_cast<double>(i
));
255 m_pDoc
->SetValue(1, i
, 0, static_cast<double>(5*i
+ 100));
256 m_pDoc
->SetValue(2, i
, 0, static_cast<double>(nNumRows
- i
- 1));
258 for (size_t i
= 0; i
< nNumRows
; ++i
)
260 OUString aArgNum
= "C" + OUString::number(i
+1);
261 m_pDoc
->SetFormula(ScAddress(3, i
, 0),
262 "=SUM(" + aArgNum
+ ";VLOOKUP(" + aArgNum
+ ";" + aTableRef
+ "; 2; 0)) + SUM($A1:$A2)",
263 formula::FormulaGrammar::GRAM_NATIVE_UI
);
266 m_xDocShell
->DoHardRecalc();
268 for (size_t i
= 0; i
< nNumRows
; ++i
)
270 OString aMsg
= "At row " + OString::number(i
);
271 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), 6 * (nNumRows
- i
- 1) + 101, static_cast<size_t>(m_pDoc
->GetValue(3, i
, 0)));
273 m_pDoc
->DeleteTab(0);
276 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testSingleRef
)
278 m_pDoc
->InsertTab(0, u
"1"_ustr
);
280 const size_t nNumRows
= 200;
281 for (size_t i
= 0; i
< nNumRows
; ++i
)
283 m_pDoc
->SetValue(0, i
, 0, static_cast<double>(i
));
284 m_pDoc
->SetFormula(ScAddress(1, i
, 0), "=A" + OUString::number(i
+1), formula::FormulaGrammar::GRAM_NATIVE_UI
);
287 m_xDocShell
->DoHardRecalc();
289 for (size_t i
= 0; i
< nNumRows
; ++i
)
291 OString aMsg
= "At row " + OString::number(i
);
292 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), i
, static_cast<size_t>(m_pDoc
->GetValue(1, i
, 0)));
294 m_pDoc
->DeleteTab(0);
297 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testTdf147905
)
299 m_pDoc
->InsertTab(0, u
"1"_ustr
);
302 const size_t nNumRows
= 500;
303 for (size_t i
= 0; i
< nNumRows
; ++i
)
305 m_pDoc
->SetString(0, i
, 0, u
"AAAAAAAA"_ustr
);
306 aFormula
= "=PROPER($A" + OUString::number(i
+1) + ")";
307 m_pDoc
->SetFormula(ScAddress(1, i
, 0),
309 formula::FormulaGrammar::GRAM_NATIVE_UI
);
312 m_xDocShell
->DoHardRecalc();
314 for (size_t i
= 0; i
< nNumRows
; ++i
)
316 OString aMsg
= "At row " + OString::number(i
);
317 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), u
"AAAAAAAA"_ustr
, m_pDoc
->GetString(0, i
, 0));
319 // Without the fix in place, this test would have failed here
320 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), u
"Aaaaaaaa"_ustr
, m_pDoc
->GetString(1, i
, 0));
322 m_pDoc
->DeleteTab(0);
325 // Common test setup steps for testSUMIFImplicitRange*()
326 static void lcl_setupCommon(ScDocument
* pDoc
, size_t nNumRows
, size_t nConstCellValue
)
328 pDoc
->SetValue(3, 0, 0, static_cast<double>(nConstCellValue
)); // D1
329 for (size_t i
= 0; i
<= (nNumRows
*2); ++i
)
331 pDoc
->SetValue(0, i
, 0, static_cast<double>(i
));
332 pDoc
->SetFormula(ScAddress(1, i
, 0),
333 "=A" + OUString::number(i
+1),
334 formula::FormulaGrammar::GRAM_NATIVE_UI
);
338 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testSUMIFImplicitRange
)
340 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
341 m_pDoc
->InsertTab(0, u
"1"_ustr
);
343 const size_t nNumRows
= 1048;
344 const size_t nConstCellValue
= 20;
345 lcl_setupCommon(m_pDoc
, nNumRows
, nConstCellValue
);
346 OUString aSrcRange
= "$A$1:$A$" + OUString::number(nNumRows
);
348 for (size_t i
= 0; i
< nNumRows
; ++i
)
350 aFormula
= "=SUMIF(" + aSrcRange
+ ";$D$1;$B$1)";
351 m_pDoc
->SetFormula(ScAddress(2, i
, 0),
353 formula::FormulaGrammar::GRAM_NATIVE_UI
);
356 ScFormulaCell
* pCell
= m_pDoc
->GetFormulaCell(ScAddress(2, 0, 0));
357 sc::AutoCalcSwitch
aACSwitch2(*m_pDoc
, true);
358 pCell
->InterpretFormulaGroup(); // Start calculation on the F.G at C1
360 for (size_t i
= 0; i
< nNumRows
; ++i
)
362 OString aMsg
= "At row " + OString::number(i
);
363 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nConstCellValue
, static_cast<size_t>(m_pDoc
->GetValue(2, i
, 0)));
365 m_pDoc
->DeleteTab(0);
368 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFGCycleWithPlainFormulaCell1
)
370 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
371 m_pDoc
->InsertTab(0, u
"1"_ustr
);
372 const size_t nNumRows
= 1048;
373 // Column A contains no formula-group
375 m_pDoc
->SetValue(0, 0, 0, 100.0);
377 m_pDoc
->SetFormula(ScAddress(0, 499, 0),
379 formula::FormulaGrammar::GRAM_NATIVE_UI
);
380 // Column B has a formula-group referencing column A.
382 for (size_t i
= 0; i
< nNumRows
; ++i
)
384 aFormula
= "=$A" + OUString::number(i
+1) + " + 100";
385 m_pDoc
->SetFormula(ScAddress(1, i
, 0),
387 formula::FormulaGrammar::GRAM_NATIVE_UI
);
389 m_xDocShell
->DoHardRecalc();
390 // Value at A500 must be 101
391 const size_t nVal
= 100;
392 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value at A500", nVal
+ 1, static_cast<size_t>(m_pDoc
->GetValue(0, 499, 0)));
393 for (size_t i
= 0; i
< nNumRows
; ++i
)
395 OString aMsg
= "Value at cell B" + OString::number(i
+1);
396 size_t nExpected
= nVal
;
401 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
, static_cast<size_t>(m_pDoc
->GetValue(1, i
, 0)));
403 m_pDoc
->DeleteTab(0);
406 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFGCycleWithPlainFormulaCell2
)
408 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
409 m_pDoc
->InsertTab(0, u
"1"_ustr
);
410 const size_t nNumRows
= 1048;
413 for (size_t i
= 0; i
< nNumRows
; ++i
)
415 aFormula
= "=$B" + OUString::number(i
+1) + " + 1";
416 m_pDoc
->SetFormula(ScAddress(0, i
, 0),
418 formula::FormulaGrammar::GRAM_NATIVE_UI
);
421 for (size_t i
= 0; i
< nNumRows
; ++i
)
423 aFormula
= "=$C" + OUString::number(i
+1) + " + 1";
424 m_pDoc
->SetFormula(ScAddress(1, i
, 0),
426 formula::FormulaGrammar::GRAM_NATIVE_UI
);
429 // Column C has no FG but a cell at C500 that references A499
430 m_pDoc
->SetFormula(ScAddress(2, 499, 0), // C500
432 formula::FormulaGrammar::GRAM_NATIVE_UI
);
433 m_xDocShell
->DoHardRecalc();
435 size_t nExpected
= 0;
436 for (size_t i
= 0; i
< nNumRows
; ++i
)
438 OString aMsg
= "Value at cell A" + OString::number(i
+1);
440 if (i
== 499) // A500 must have value = 5
442 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
, static_cast<size_t>(m_pDoc
->GetValue(0, i
, 0)));
443 aMsg
= "Value at cell B" + OString::number(i
+1);
445 if (i
== 499) // B500 must have value = 4
447 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
, static_cast<size_t>(m_pDoc
->GetValue(1, i
, 0)));
450 // C500 must have value = 3
452 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value at cell C500", nExpected
, static_cast<size_t>(m_pDoc
->GetValue(2, 499, 0)));
453 m_pDoc
->DeleteTab(0);
456 static void lcl_setupMultipleFGColumn(ScDocument
* pDocument
, size_t nNumRowsInBlock
, size_t nNumFG
, size_t nOffset
)
459 ScAddress
aAddr(1, 0, 0);
460 // Column B with multiple FG's
461 for (size_t nFGIdx
= 0; nFGIdx
< nNumFG
; ++nFGIdx
)
463 size_t nRowStart
= 2*nFGIdx
*nNumRowsInBlock
;
464 for (size_t nRow
= nRowStart
; nRow
< (nRowStart
+ nNumRowsInBlock
); ++nRow
)
467 aFormula
= "=$C" + OUString::number(nRow
+1) + " + 0";
468 pDocument
->SetFormula(aAddr
, aFormula
,
469 formula::FormulaGrammar::GRAM_NATIVE_UI
);
470 // Fill Column C with doubles.
471 pDocument
->SetValue(2, nRow
, 0, static_cast<double>(nFGIdx
));
475 // Column A with a single FG that depends on Column B.
476 size_t nNumRowsInRef
= nNumRowsInBlock
*2;
477 size_t nColAFGLen
= 2*nNumRowsInBlock
*nNumFG
- nNumRowsInRef
+ 1;
479 for (size_t nRow
= nOffset
; nRow
< nColAFGLen
; ++nRow
)
482 aFormula
= "=SUM($B" + OUString::number(nRow
+1) + ":$B" + OUString::number(nRow
+nNumRowsInRef
) + ")";
483 pDocument
->SetFormula(aAddr
, aFormula
,
484 formula::FormulaGrammar::GRAM_NATIVE_UI
);
488 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testMultipleFGColumn
)
490 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
491 m_pDoc
->InsertTab(0, u
"1"_ustr
);
493 constexpr size_t nNumRowsInBlock
= 200;
494 constexpr size_t nNumFG
= 50;
495 constexpr size_t nNumRowsInRef
= nNumRowsInBlock
*2;
496 constexpr size_t nColAFGLen
= 2*nNumRowsInBlock
*nNumFG
- nNumRowsInRef
+ 1;
497 constexpr size_t nColAStartOffset
= nNumRowsInBlock
/2;
498 lcl_setupMultipleFGColumn(m_pDoc
, nNumRowsInBlock
, nNumFG
, nColAStartOffset
);
500 m_xDocShell
->DoHardRecalc();
503 // First cell in the FG in col A references nColAStartOffset cells in second formula-group of column B each having value 1.
504 size_t nExpected
= nColAStartOffset
;
505 size_t nIn
= 0, nOut
= 0;
506 for (size_t nRow
= nColAStartOffset
; nRow
< nColAFGLen
; ++nRow
)
508 aMsg
= "Value at Cell A" + OString::number(nRow
+1);
509 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
, static_cast<size_t>(m_pDoc
->GetValue(0, nRow
, 0)));
510 nIn
= static_cast<size_t>(m_pDoc
->GetValue(2, nRow
+nNumRowsInRef
, 0));
511 nOut
= static_cast<size_t>(m_pDoc
->GetValue(2, nRow
, 0));
512 nExpected
= nExpected
+ nIn
- nOut
;
515 m_pDoc
->DeleteTab(0);
518 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFormulaGroupSpanEval
)
520 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
521 m_pDoc
->InsertTab(0, u
"1"_ustr
);
523 constexpr size_t nFGLen
= 2048;
526 for (size_t nRow
= 0; nRow
< nFGLen
; ++nRow
)
528 aFormula
= "=$C" + OUString::number(nRow
+1) + " + 0";
529 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), aFormula
,
530 formula::FormulaGrammar::GRAM_NATIVE_UI
);
531 aFormula
= "=SUM($B" + OUString::number(nRow
+1) + ":$B" + OUString::number(nRow
+2) + ")";
532 m_pDoc
->SetFormula(ScAddress(0, nRow
, 0), aFormula
,
533 formula::FormulaGrammar::GRAM_NATIVE_UI
);
536 m_xDocShell
->DoHardRecalc();
538 for (size_t nRow
= 0; nRow
< nFGLen
; ++nRow
)
540 m_pDoc
->SetValue(2, nRow
, 0, 1.0);
541 ScFormulaCell
* pFCell
= m_pDoc
->GetFormulaCell(ScAddress(1, nRow
, 0));
542 pFCell
->SetDirtyVar();
543 pFCell
= m_pDoc
->GetFormulaCell(ScAddress(0, nRow
, 0));
544 pFCell
->SetDirtyVar();
547 constexpr size_t nSpanStart
= 100;
548 constexpr size_t nSpanLen
= 1024;
549 constexpr size_t nSpanEnd
= nSpanStart
+ nSpanLen
- 1;
551 m_pDoc
->SetAutoCalc(true);
553 // EnsureFormulaCellResults should only calculate the specified range along with the dependent spans recursively and nothing more.
554 // The specified range is A99:A1124, and the dependent range is B99:B1125 (since A99 = SUM(B99:B100) and A1124 = SUM(B1124:B1125) )
555 bool bAnyDirty
= m_pDoc
->EnsureFormulaCellResults(ScRange(0, nSpanStart
, 0, 0, nSpanEnd
, 0));
556 CPPUNIT_ASSERT(bAnyDirty
);
557 m_pDoc
->SetAutoCalc(false);
560 for (size_t nRow
= 0; nRow
< nFGLen
; ++nRow
)
562 size_t nExpectedA
= 0, nExpectedB
= 0;
563 // For nRow from 100(nSpanStart) to 1123(nSpanEnd) column A must have the value of 2 and
564 // column B should have value 1.
566 // For nRow == 1124, column A should have value 0 and column B should have value 1.
568 // For all other rows both column A and B must have value 0.
569 if (nRow
>= nSpanStart
)
571 if (nRow
<= nSpanEnd
)
576 else if (nRow
== nSpanEnd
+ 1)
580 aMsg
= "Value at Cell A" + OString::number(nRow
+1);
581 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpectedA
, static_cast<size_t>(m_pDoc
->GetValue(0, nRow
, 0)));
582 aMsg
= "Value at Cell B" + OString::number(nRow
+1);
583 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpectedB
, static_cast<size_t>(m_pDoc
->GetValue(1, nRow
, 0)));
586 m_pDoc
->DeleteTab(0);
589 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFormulaGroupSpanEvalNonGroup
)
591 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
592 m_pDoc
->InsertTab(0, u
"1"_ustr
);
594 constexpr size_t nFGLen
= 2048;
597 for (size_t nRow
= 0; nRow
< nFGLen
; ++nRow
)
599 aFormula
= "=$B" + OUString::number(nRow
+1) + " + 0";
600 m_pDoc
->SetFormula(ScAddress(0, nRow
, 0), aFormula
,
601 formula::FormulaGrammar::GRAM_NATIVE_UI
);
604 m_xDocShell
->DoHardRecalc();
606 constexpr size_t nNumChanges
= 12;
607 constexpr size_t nChangeRows
[nNumChanges
] = {10, 11, 12, 101, 102, 103, 251, 252, 253, 503, 671, 1029};
608 for (size_t nIdx
= 0; nIdx
< nNumChanges
; ++nIdx
)
610 size_t nRow
= nChangeRows
[nIdx
];
611 m_pDoc
->SetValue(1, nRow
, 0, 1.0);
612 ScFormulaCell
* pFCell
= m_pDoc
->GetFormulaCell(ScAddress(0, nRow
, 0));
613 pFCell
->SetDirtyVar();
616 m_pDoc
->SetAutoCalc(true);
617 bool bAnyDirty
= m_pDoc
->EnsureFormulaCellResults(ScRange(0, 9, 0, 0, 1030, 0));
618 CPPUNIT_ASSERT(bAnyDirty
);
619 m_pDoc
->SetAutoCalc(false);
622 for (size_t nRow
= 0, nIdx
= 0; nRow
< nFGLen
; ++nRow
)
624 size_t nExpected
= 0;
625 if (nIdx
< nNumChanges
&& nRow
== nChangeRows
[nIdx
])
631 aMsg
= "Value at Cell A" + OString::number(nRow
+1);
632 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
, static_cast<size_t>(m_pDoc
->GetValue(0, nRow
, 0)));
635 m_pDoc
->DeleteTab(0);
638 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testArrayFormulaGroup
)
640 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
641 m_pDoc
->InsertTab(0, u
"1"_ustr
);
643 m_pDoc
->SetValue(1, 0, 0, 2.0); // B1 <== 2
644 m_pDoc
->SetValue(2, 0, 0, 1.0); // C1 <== 1
647 for (size_t nRow
= 1; nRow
< 16; ++nRow
)
649 m_pDoc
->SetValue(0, nRow
, 0, 1.0); // A2:A16 <== 1
654 aFormula
= "=SUMPRODUCT(($A" + OUString::number(1 + nRow
) +
655 ":$A" + OUString::number(499 + nRow
) + ")*B$1+C$1)";
656 // Formula-group in B2:B11 with first cell = "=SUMPRODUCT(($A2:$A500)*B$1+C$1)"
657 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), aFormula
,
658 formula::FormulaGrammar::GRAM_NATIVE_UI
);
661 m_xDocShell
->DoHardRecalc();
663 size_t nExpected
= 529;
665 for (size_t nRow
= 1; nRow
< 11; ++nRow
)
667 aMsg
= "Value at Cell B" + OString::number(nRow
+1);
668 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
, static_cast<size_t>(m_pDoc
->GetValue(1, nRow
, 0)));
672 m_pDoc
->DeleteTab(0);
675 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testDependentFormulaGroupCollection
)
677 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
678 m_pDoc
->InsertTab(0, u
"1"_ustr
);
682 for (size_t nRow
= 0; nRow
< 16; ++nRow
)
684 m_pDoc
->SetValue(0, nRow
, 0, 1.0); // A1:A16 <== 1
689 // Formula-group in B1:B8 with first cell = "=SUM($A1:$A1024)"
690 aFormula
= "=SUM($A" + OUString::number(1 + nRow
) +
691 ":$A" + OUString::number(1024 + nRow
) + ")";
692 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), aFormula
,
693 formula::FormulaGrammar::GRAM_NATIVE_UI
);
695 // Formula-group in C1:C8 with first cell = "=SUM($K1:$K1024)"
696 aFormula
= "=SUM($K" + OUString::number(1 + nRow
) +
697 ":$K" + OUString::number(1024 + nRow
) + ")";
698 m_pDoc
->SetFormula(ScAddress(2, nRow
, 0), aFormula
,
699 formula::FormulaGrammar::GRAM_NATIVE_UI
);
701 // Formula-group in D1:D8 with first cell = "=SUM($A1:$A1024) - $A2"
702 aFormula
= "=SUM($A" + OUString::number(1 + nRow
) +
703 ":$A" + OUString::number(1024 + nRow
) + ") - $A" + OUString::number(2 + nRow
);
704 m_pDoc
->SetFormula(ScAddress(3, nRow
, 0), aFormula
,
705 formula::FormulaGrammar::GRAM_NATIVE_UI
);
707 // Formula-group in K1:K8 with first cell = "=SUM($B1:$B1024)"
708 aFormula
= "=SUM($B" + OUString::number(1 + nRow
) +
709 ":$B" + OUString::number(1024 + nRow
) + ")";
710 m_pDoc
->SetFormula(ScAddress(10, nRow
, 0), aFormula
,
711 formula::FormulaGrammar::GRAM_NATIVE_UI
);
714 m_xDocShell
->DoHardRecalc();
716 size_t nExpected
[8] = { 408, 308, 224, 155, 100, 58, 28, 9 };
719 for (size_t nRow
= 0; nRow
< 8; ++nRow
)
721 aMsg
= "Value at Cell C" + OString::number(nRow
+1);
722 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), nExpected
[nRow
], static_cast<size_t>(m_pDoc
->GetValue(2, nRow
, 0)));
725 m_pDoc
->DeleteTab(0);
728 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFormulaGroupWithForwardSelfReference
)
730 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
731 m_pDoc
->InsertTab(0, u
"1"_ustr
);
734 m_pDoc
->SetValue(2, 4, 0, 10.0); // C5 <== 10
736 for (size_t nRow
= 0; nRow
< 4; ++nRow
)
738 // Formula-group in B1:B4 with first cell = "=SUM($A1:$A1024) + C1"
739 aFormula
= "=SUM($A" + OUString::number(1 + nRow
) +
740 ":$A" + OUString::number(1024 + nRow
) + ") + C" + OUString::number(nRow
+ 1);
741 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), aFormula
,
742 formula::FormulaGrammar::GRAM_NATIVE_UI
);
744 // Formula-group in C1:C4 with first cell = "=SUM($A1:$A1024) + C2"
745 aFormula
= "=SUM($A" + OUString::number(1 + nRow
) +
746 ":$A" + OUString::number(1024 + nRow
) + ") + C" + OUString::number(nRow
+ 2);
747 m_pDoc
->SetFormula(ScAddress(2, nRow
, 0), aFormula
,
748 formula::FormulaGrammar::GRAM_NATIVE_UI
);
751 m_xDocShell
->DoHardRecalc();
754 for (size_t nCol
= 0; nCol
< 2; ++nCol
)
756 for (size_t nRow
= 0; nRow
< 4; ++nRow
)
758 aMsg
= "Value at Cell (Col = " + OString::number(nCol
+ 1) + ", Row = " + OString::number(nRow
) + ", Tab = 0)";
759 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), 10.0, m_pDoc
->GetValue(1 + nCol
, nRow
, 0));
763 m_pDoc
->DeleteTab(0);
766 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFormulaGroupsInCyclesAndWithSelfReference
)
768 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
769 m_pDoc
->InsertTab(0, u
"1"_ustr
);
771 m_pDoc
->SetValue(1, 0, 0, 1.0); // B1 <== 1
772 m_pDoc
->SetValue(3, 0, 0, 2.0); // D1 <== 2
775 for (size_t nRow
= 0; nRow
< 5; ++nRow
)
777 // Formula-group in C1:C5 with first cell = "=SUM($A1:$A1024) + D1"
778 aFormula
= "=SUM($A" + OUString::number(1 + nRow
) +
779 ":$A" + OUString::number(1024 + nRow
) + ") + D" + OUString::number(nRow
+ 1);
780 m_pDoc
->SetFormula(ScAddress(2, nRow
, 0), aFormula
,
781 formula::FormulaGrammar::GRAM_NATIVE_UI
);
786 // nRow starts from 1 till 4 (for D2 to D5).
787 // Formula-group in D2:D5 with first cell = "=SUM($A1:$A1024) + D1 + B2"
788 aFormula
= "=SUM($A" + OUString::number(nRow
) +
789 ":$A" + OUString::number(1023 + nRow
) + ") + D" + OUString::number(nRow
) +
790 " + B" + OUString::number(nRow
+ 1);
791 m_pDoc
->SetFormula(ScAddress(3, nRow
, 0), aFormula
,
792 formula::FormulaGrammar::GRAM_NATIVE_UI
);
794 // Formula-group in B2:B5 with first cell = "=SUM($A1:$A1024) + C1 + B1"
795 aFormula
= "=SUM($A" + OUString::number(nRow
) +
796 ":$A" + OUString::number(1023 + nRow
) + ") + C" + OUString::number(nRow
) +
797 " + B" + OUString::number(nRow
);
798 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), aFormula
,
799 formula::FormulaGrammar::GRAM_NATIVE_UI
);
802 m_xDocShell
->DoHardRecalc();
803 m_pDoc
->SetAutoCalc(true);
805 const ScRange
aChangeRange(1, 1, 0, 1, 4, 0); // B2:B5
806 ScMarkData
aMark(m_pDoc
->GetSheetLimits());
807 aMark
.SelectOneTable(0);
809 // Set up clip document.
810 ScDocument
aClipDoc(SCDOCMODE_CLIP
);
811 aClipDoc
.ResetClip(m_pDoc
, &aMark
);
812 // Cut B1:B2 to clipboard.
813 cutToClip(*m_xDocShell
, aChangeRange
, &aClipDoc
, false);
814 pasteFromClip(m_pDoc
, aChangeRange
, &aClipDoc
);
816 double fExpected
[3][5] = {
818 { 2, 5, 13, 34, 89 },
823 for (size_t nCol
= 0; nCol
< 3; ++nCol
)
825 for (size_t nRow
= 0; nRow
< 5; ++nRow
)
827 aMsg
= "Value at Cell (Col = " + OString::number(nCol
+ 1) + ", Row = " + OString::number(nRow
) + ", Tab = 0)";
828 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), fExpected
[nCol
][nRow
], m_pDoc
->GetValue(1 + nCol
, nRow
, 0));
832 m_pDoc
->DeleteTab(0);
835 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFormulaGroupsInCyclesAndWithSelfReference2
)
837 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
838 m_pDoc
->InsertTab(0, u
"1"_ustr
);
840 m_pDoc
->SetValue(1, 0, 0, 1.0); // B1 <== 1
841 m_pDoc
->SetValue(3, 0, 0, 2.0); // D1 <== 2
842 m_pDoc
->SetValue(4, 0, 0, 1.0); // E1 <== 1
845 for (size_t nRow
= 0; nRow
< 5; ++nRow
)
847 // Formula-group in C1:C5 with first cell = "=SUM($A1:$A1024) + D1 + E1"
848 aFormula
= "=SUM($A" + OUString::number(1 + nRow
) +
849 ":$A" + OUString::number(1024 + nRow
) + ") + D" + OUString::number(nRow
+ 1) +
850 " + E" + OUString::number(nRow
+ 1);
851 m_pDoc
->SetFormula(ScAddress(2, nRow
, 0), aFormula
,
852 formula::FormulaGrammar::GRAM_NATIVE_UI
);
857 // Formula-group in B2:B5 with first cell = "=SUM($A1:$A1024) + C1 + B1"
858 aFormula
= "=SUM($A" + OUString::number(nRow
) +
859 ":$A" + OUString::number(1023 + nRow
) + ") + C" + OUString::number(nRow
) +
860 " + B" + OUString::number(nRow
);
861 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), aFormula
,
862 formula::FormulaGrammar::GRAM_NATIVE_UI
);
864 // Formula-group in D2:D5 with first cell = "=SUM($A1:$A1024) + D1 + B2"
865 aFormula
= "=SUM($A" + OUString::number(nRow
) +
866 ":$A" + OUString::number(1023 + nRow
) + ") + D" + OUString::number(nRow
) +
867 " + B" + OUString::number(nRow
+ 1);
868 m_pDoc
->SetFormula(ScAddress(3, nRow
, 0), aFormula
,
869 formula::FormulaGrammar::GRAM_NATIVE_UI
);
871 // Formula-group in E2:E5 with first cell = "=SUM($A1:$A1024) + E1 + D2"
872 aFormula
= "=SUM($A" + OUString::number(nRow
) +
873 ":$A" + OUString::number(1023 + nRow
) + ") + E" + OUString::number(nRow
) +
874 " + D" + OUString::number(nRow
+ 1);
875 m_pDoc
->SetFormula(ScAddress(4, nRow
, 0), aFormula
,
876 formula::FormulaGrammar::GRAM_NATIVE_UI
);
879 m_xDocShell
->DoHardRecalc();
881 double fExpected
[4][5] = {
882 { 1, 4, 17, 70, 286 },
883 { 3, 13, 53, 216, 881 },
884 { 2, 6, 23, 93, 379 },
885 { 1, 7, 30, 123, 502 }
889 for (size_t nCol
= 0; nCol
< 4; ++nCol
)
891 for (size_t nRow
= 0; nRow
< 5; ++nRow
)
893 aMsg
= "Value at Cell (Col = " + OString::number(nCol
+ 1) + ", Row = " + OString::number(nRow
) + ", Tab = 0)";
894 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg
.getStr(), fExpected
[nCol
][nRow
], m_pDoc
->GetValue(1 + nCol
, nRow
, 0));
898 m_pDoc
->DeleteTab(0);
901 CPPUNIT_TEST_FIXTURE(ScParallelismTest
, testFormulaGroupsInCyclesAndWithSelfReference3
)
903 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
904 m_pDoc
->InsertTab(0, u
"1"_ustr
);
906 m_pDoc
->SetValue(1, 1, 0, 2.0); // B2 <== 2
907 for (size_t nRow
= 1; nRow
< 105; ++nRow
)
909 // Formula-group in B3:B104 with first cell "=D2+0.001"
911 m_pDoc
->SetFormula(ScAddress(1, nRow
, 0), "=D" + OUString::number(nRow
) + "+0.001",
912 formula::FormulaGrammar::GRAM_NATIVE_UI
);
913 // Formula-group in C2:C104 with first cell "=B2*1.01011"
914 m_pDoc
->SetFormula(ScAddress(2, nRow
, 0), "=B" + OUString::number(nRow
+ 1) + "*1.01011",
915 formula::FormulaGrammar::GRAM_NATIVE_UI
);
916 // Formula-group in D2:C104 with first cell "=C2*1.02"
917 m_pDoc
->SetFormula(ScAddress(3, nRow
, 0), "=C" + OUString::number(nRow
+ 1) + "*1.02",
918 formula::FormulaGrammar::GRAM_NATIVE_UI
);
921 m_xDocShell
->DoHardRecalc();
923 // What happens with tdf#132451 is that the copy&paste C6->C5 really just sets the dirty flag
924 // for C5 and all the cells that depend on it (D5,B6,C6,D6,B7,...), and it also resets
925 // flags marking the C formula group as disabled for parallel calculation because of the cycle.
926 m_pDoc
->SetFormula(ScAddress(2, 4, 0), u
"=B5*1.01011"_ustr
, formula::FormulaGrammar::GRAM_NATIVE_UI
);
927 m_pDoc
->GetFormulaCell(ScAddress(2,4,0))->GetCellGroup()->mbPartOfCycle
= false;
928 m_pDoc
->GetFormulaCell(ScAddress(2,4,0))->GetCellGroup()->meCalcState
= sc::GroupCalcEnabled
;
930 m_pDoc
->SetAutoCalc(true);
931 // Without the fix, getting value of C5 would try to parallel-interpret formula group in B
932 // from its first dirty cell (B6), which depends on D5, which depends on C5, where the cycle
933 // would be detected and dependency check would bail out. But the result from Interpret()-ing
934 // D5 would be used and D5's dirty flag reset, with D5 value incorrect.
935 m_pDoc
->GetValue(2,4,0);
937 double fExpected
[2][3] = {
938 { 2.19053373572776, 2.21268003179597, 2.25693363243189 },
939 { 2.25793363243189, 2.28076134145577, 2.32637656828489 }
941 for (size_t nCol
= 1; nCol
< 4; ++nCol
)
943 for (size_t nRow
= 4; nRow
< 6; ++nRow
)
945 OString aMsg
= "Value at Cell (Col = " + OString::number(nCol
) + ", Row = " + OString::number(nRow
) + ", Tab = 0)";
946 ASSERT_DOUBLES_EQUAL_MESSAGE(aMsg
.getStr(), fExpected
[nRow
- 4][nCol
- 1], m_pDoc
->GetValue(nCol
, nRow
, 0));
950 m_pDoc
->DeleteTab(0);
953 CPPUNIT_PLUGIN_IMPLEMENT();
955 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */