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>
7 #include <sfx2/sfxmodelfactory.hxx>
9 #include "helper/qahelper.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>
23 using namespace css::uno
;
25 class ScParallelismTest
: public ScBootstrapFixture
30 virtual void setUp() override
;
31 virtual void tearDown() override
;
33 void getNewDocShell(ScDocShellRef
& rDocShellRef
);
38 void testVLOOKUPSUM();
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();
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
);
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
);
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
;
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
,
127 pSrcDoc
->DeleteSelection( InsertDeleteFlags::ALL
, aMark
);
128 aMark
.MarkToSimple();
131 return new ScUndoCut( &rDocSh
, rRange
, rRange
.aEnd
, aMark
, std::move(pUndoDoc
) );
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();
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
++)
186 m_pDoc
->SetValue(0, i
, 0, i
/10 + 1000);
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) +
215 formula::FormulaGrammar::GRAM_NATIVE_UI
);
218 m_xDocShell
->DoHardRecalc();
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
++)
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
;
243 for (auto i
= 1; i
< 1000; i
++)
245 OString sMessage
= "At row " + OString::number(i
+1);
247 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), m_pDoc
->GetValue(5, 10+i
%10, 0), m_pDoc
->GetValue(15, i
, 0), 1e-10);
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);
274 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), static_cast<double>(i
)/(i
%10), m_pDoc
->GetValue(2, i
, 0), 1e-10);
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
++)
289 m_pDoc
->SetValue(0, i
, 0, 1042.42);
291 m_pDoc
->SetValue(0, i
, 0, i
);
293 m_pDoc
->SetValue(0, i
, 0, i
+0.1);
296 m_pDoc
->SetValue(1, i
, 0, i
*10);
298 m_pDoc
->SetString(1, i
, 0, "N" + OUString::number(i
*10));
302 m_pDoc
->SetFormula(ScAddress(2, i
, 0),
303 "=VLOOKUP(" + OUString::number(i
) + "; "
305 formula::FormulaGrammar::GRAM_NATIVE_UI
);
311 m_pDoc
->SetFormula(ScAddress(2, i
, 0),
314 formula::FormulaGrammar::GRAM_NATIVE_UI
);
316 m_pDoc
->SetFormula(ScAddress(2, i
, 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);
333 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage
.getStr(), static_cast<double>(i
*10), m_pDoc
->GetValue(2, i
, 0), 1e-10);
335 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), OUString("N" + OUString::number(i
*10)), m_pDoc
->GetString(2, i
, 0));
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));
346 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), OUString("N" + OUString::number(i
*10)), m_pDoc
->GetString(2, i
, 0));
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
);
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),
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
456 m_pDoc
->SetValue(0, 0, 0, 100.0);
458 m_pDoc
->SetFormula(ScAddress(0, 499, 0),
460 formula::FormulaGrammar::GRAM_NATIVE_UI
);
461 // Column B has a formula-group referencing column A.
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),
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
;
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;
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),
499 formula::FormulaGrammar::GRAM_NATIVE_UI
);
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),
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
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);
521 if (i
== 499) // A500 must have value = 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);
526 if (i
== 499) // B500 must have value = 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
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
)
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
)
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;
560 for (size_t nRow
= nOffset
; nRow
< nColAFGLen
; ++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();
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;
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);
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
)
657 else if (nRow
== nSpanEnd
+ 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;
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);
703 for (size_t nRow
= 0, nIdx
= 0; nRow
< nFGLen
; ++nRow
)
705 size_t nExpected
= 0;
706 if (nIdx
< nNumChanges
&& nRow
== nChangeRows
[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
728 for (size_t nRow
= 1; nRow
< 16; ++nRow
)
730 m_pDoc
->SetValue(0, nRow
, 0, 1.0); // A2:A16 <== 1
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;
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)));
753 m_pDoc
->DeleteTab(0);
756 void ScParallelismTest::testDependentFormulaGroupCollection()
758 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, false);
759 m_pDoc
->InsertTab(0, "1");
763 for (size_t nRow
= 0; nRow
< 16; ++nRow
)
765 m_pDoc
->SetValue(0, nRow
, 0, 1.0); // A1:A16 <== 1
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 };
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");
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();
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
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
);
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] = {
899 { 2, 5, 13, 34, 89 },
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
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
);
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 }
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: */