tdf#114479: compute implicit sum ranges for ocSumIf,ocAverageIf...
[LibreOffice.git] / sc / qa / unit / parallelism.cxx
blobc5c196154123f8de51fc866d69d948126a55c9ff
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>
5 #include <rtl/strbuf.hxx>
6 #include <osl/file.hxx>
8 #include <scdll.hxx>
9 #include <sfx2/app.hxx>
10 #include <sfx2/docfilt.hxx>
11 #include <sfx2/docfile.hxx>
12 #include <sfx2/sfxmodelfactory.hxx>
13 #include <svl/stritem.hxx>
15 #include "helper/qahelper.hxx"
17 #include <calcconfig.hxx>
18 #include <interpre.hxx>
20 #include <docsh.hxx>
21 #include <postit.hxx>
22 #include <patattr.hxx>
23 #include <scitems.hxx>
24 #include <document.hxx>
25 #include <cellform.hxx>
26 #include <drwlayer.hxx>
27 #include <userdat.hxx>
28 #include <formulacell.hxx>
29 #include <formulagroup.hxx>
30 #include <scopetools.hxx>
32 #include <svx/svdpage.hxx>
34 #include <officecfg/Office/Calc.hxx>
36 using namespace css;
37 using namespace css::uno;
39 class ScParallelismTest : public ScBootstrapFixture
41 public:
42 ScParallelismTest();
44 virtual void setUp() override;
45 virtual void tearDown() override;
47 void getNewDocShell(ScDocShellRef& rDocShellRef);
49 void testSUMIFS();
50 void testDivision();
51 void testVLOOKUP();
52 void testVLOOKUPSUM();
53 void testSingleRef();
54 void testSUMIFImplicitRange();
56 CPPUNIT_TEST_SUITE(ScParallelismTest);
57 CPPUNIT_TEST(testSUMIFS);
58 CPPUNIT_TEST(testDivision);
59 CPPUNIT_TEST(testVLOOKUP);
60 CPPUNIT_TEST(testVLOOKUPSUM);
61 CPPUNIT_TEST(testSingleRef);
62 CPPUNIT_TEST(testSUMIFImplicitRange);
63 CPPUNIT_TEST_SUITE_END();
65 private:
67 bool getThreadingFlag();
68 void setThreadingFlag(bool bSet);
70 ScDocument *m_pDoc;
72 ScDocShellRef m_xDocShell;
73 bool m_bThreadingFlagCfg;
76 ScParallelismTest::ScParallelismTest()
77 : ScBootstrapFixture( "sc/qa/unit/data" )
81 bool ScParallelismTest::getThreadingFlag()
83 return officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
86 void ScParallelismTest::setThreadingFlag( bool bSet )
88 std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
89 officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::set(bSet, xBatch);
90 xBatch->commit();
93 void ScParallelismTest::setUp()
95 test::BootstrapFixture::setUp();
97 ScDLL::Init();
99 getNewDocShell(m_xDocShell);
100 m_pDoc = &m_xDocShell->GetDocument();
102 sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly();
104 m_bThreadingFlagCfg = getThreadingFlag();
105 if (!m_bThreadingFlagCfg)
106 setThreadingFlag(true);
109 void ScParallelismTest::tearDown()
111 // Restore threading flag
112 if (!m_bThreadingFlagCfg)
113 setThreadingFlag(false);
115 test::BootstrapFixture::tearDown();
118 void ScParallelismTest::getNewDocShell( ScDocShellRef& rDocShellRef )
120 rDocShellRef = new ScDocShell(
121 SfxModelFlags::EMBEDDED_OBJECT |
122 SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
123 SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
126 void ScParallelismTest::testSUMIFS()
128 m_pDoc->InsertTab(0, "1");
130 m_pDoc->SetValue(0, 0, 0, 1001);
132 for (auto i = 1; i < 1000; i++)
134 /*A*/
135 if (i%19)
136 m_pDoc->SetValue(0, i, 0, i/10 + 1000);
137 else
138 m_pDoc->SetValue(0, i, 0, 123456);
139 /*B*/ m_pDoc->SetValue(1, i, 0, i%10);
140 /*C*/ m_pDoc->SetValue(2, i, 0, i%5);
142 /*F*/ m_pDoc->SetValue(5, i, 0, i%17 + i%13);
144 /*L*/ m_pDoc->SetValue(11, i, 0, i%10);
145 /*M*/ m_pDoc->SetValue(12, i, 0, i%5);
148 for (auto i = 1; i < 1000; i++)
150 // For instance P389 will contain the formula:
151 // =SUMIFS($F$2:$F$1000; $A$2:$A$1000; A$1; $B$2:$B$1000; $L389; $C$2:$C$1000; $M389)
153 // In other words, it will sum those values in F2:1000 where the A value matches A1 (1001),
154 // the B value matches L389 and the C value matches M389. (There should be just one such
155 // value, so the formula is actually simply used to pick out that single value from the F
156 // column where A,B,C match. Silly, but that is how SUMIFS is used in some corners of the
157 // real world, apparently.)
159 /*P*/ m_pDoc->SetFormula(ScAddress(15, i, 0),
160 "=SUMIFS($F$2:$F$1000; "
161 "$A$2:$A$1000; A$1; "
162 "$B$2:$B$1000; $L" + OUString::number(i+1) + "; "
163 "$C$2:$C$1000; $M" + OUString::number(i+1) +
164 ")",
165 formula::FormulaGrammar::GRAM_NATIVE_UI);
168 m_xDocShell->DoHardRecalc();
170 #if 1
171 OUString sFormula;
173 std::cerr << "A1=" << m_pDoc->GetValue(0, 0, 0) << std::endl;
175 std::cerr << " A,B,C F L,M" << std::endl;
176 for (auto i = 1; i < 30; i++)
178 std::cerr <<
179 i+1 << ": " <<
180 m_pDoc->GetValue(0, i, 0) << "," <<
181 m_pDoc->GetValue(1, i, 0) << "," <<
182 m_pDoc->GetValue(2, i, 0) << " " <<
183 m_pDoc->GetValue(5, i, 0) << " " <<
184 m_pDoc->GetValue(11, i, 0) << "," <<
185 m_pDoc->GetValue(12, i, 0) << " \"";
186 m_pDoc->GetFormula(15, i, 0, sFormula);
187 std::cerr << sFormula << "\": \"" <<
188 m_pDoc->GetString(15, i, 0) << "\": " <<
189 m_pDoc->GetValue(15, i, 0) << std::endl;
191 #endif
193 for (auto i = 1; i < 1000; i++)
195 OString sMessage = "At row " + OString::number(i+1);
196 if ((10+i%10)%19)
197 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), m_pDoc->GetValue(5, 10+i%10, 0), m_pDoc->GetValue(15, i, 0), 1e-10);
198 else
199 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), 0, m_pDoc->GetValue(15, i, 0), 1e-10);
202 m_pDoc->DeleteTab(0);
205 void ScParallelismTest::testDivision()
207 m_pDoc->InsertTab(0, "1");
209 for (auto i = 1; i < 1000; i++)
211 /*A*/ m_pDoc->SetValue(0, i, 0, i);
212 /*B*/ m_pDoc->SetValue(1, i, 0, i%10);
213 /*C*/ m_pDoc->SetFormula(ScAddress(2, i, 0),
214 "=A" + OUString::number(i+1) + "/B" + OUString::number(i+1),
215 formula::FormulaGrammar::GRAM_NATIVE_UI);
218 m_xDocShell->DoHardRecalc();
220 for (auto i = 1; i < 1000; i++)
222 OString sMessage = "At row " + OString::number(i+1);
223 if (i%10)
224 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), static_cast<double>(i)/(i%10), m_pDoc->GetValue(2, i, 0), 1e-10);
225 else
226 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#DIV/0!"), m_pDoc->GetString(2, i, 0));
229 m_pDoc->DeleteTab(0);
232 void ScParallelismTest::testVLOOKUP()
234 m_pDoc->InsertTab(0, "1");
236 for (auto i = 1; i < 2000; i++)
238 if (i == 1042)
239 m_pDoc->SetValue(0, i, 0, 1042.42);
240 else if (i%5)
241 m_pDoc->SetValue(0, i, 0, i);
242 else
243 m_pDoc->SetValue(0, i, 0, i+0.1);
245 if (i%2)
246 m_pDoc->SetValue(1, i, 0, i*10);
247 else
248 m_pDoc->SetString(1, i, 0, "N" + OUString::number(i*10));
250 if (i < 1000)
252 m_pDoc->SetFormula(ScAddress(2, i, 0),
253 "=VLOOKUP(" + OUString::number(i) + "; "
254 "A$2:B$2000; 2; 0)",
255 formula::FormulaGrammar::GRAM_NATIVE_UI);
258 else
260 if (i == 1042)
261 m_pDoc->SetFormula(ScAddress(2, i, 0),
262 "=VLOOKUP(1042.42; "
263 "A$2:B$2000; 2; 0)",
264 formula::FormulaGrammar::GRAM_NATIVE_UI);
265 else
266 m_pDoc->SetFormula(ScAddress(2, i, 0),
267 "=VLOOKUP(1.234; "
268 "A$2:B$2000; 2; 0)",
269 formula::FormulaGrammar::GRAM_NATIVE_UI);
273 m_xDocShell->DoHardRecalc();
275 for (auto i = 1; i < 2000; i++)
277 OString sMessage = "At row " + OString::number(i+1);
278 if (i < 1000)
280 if (i%5)
282 if (i%2)
283 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(sMessage.getStr(), static_cast<double>(i*10), m_pDoc->GetValue(2, i, 0), 1e-10);
284 else
285 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("N" + OUString::number(i*10)), m_pDoc->GetString(2, i, 0));
287 else
289 // The corresponding value in A is i+0.1
290 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#N/A"), m_pDoc->GetString(2, i, 0));
293 else
295 if (i == 1042)
296 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("N" + OUString::number(i*10)), m_pDoc->GetString(2, i, 0));
297 else
298 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), OUString("#N/A"), m_pDoc->GetString(2, i, 0));
302 m_pDoc->DeleteTab(0);
305 void ScParallelismTest::testVLOOKUPSUM()
307 m_pDoc->InsertTab(0, "1");
309 const size_t nNumRows = 4096*4;
310 OUString aTableRef = "$A$1:$B$" + OUString::number(nNumRows);
311 for (size_t i = 0; i < nNumRows; ++i)
313 m_pDoc->SetValue(0, i, 0, static_cast<double>(i));
314 m_pDoc->SetValue(1, i, 0, static_cast<double>(5*i + 100));
315 m_pDoc->SetValue(2, i, 0, static_cast<double>(nNumRows - i - 1));
317 for (size_t i = 0; i < nNumRows; ++i)
319 OUString aArgNum = "C" + OUString::number(i+1);
320 m_pDoc->SetFormula(ScAddress(3, i, 0),
321 "=SUM(" + aArgNum + ";VLOOKUP(" + aArgNum + ";" + aTableRef + "; 2; 0)) + SUM($A1:$A2)",
322 formula::FormulaGrammar::GRAM_NATIVE_UI);
325 m_xDocShell->DoHardRecalc();
327 for (size_t i = 0; i < nNumRows; ++i)
329 OString aMsg = "At row " + OString::number(i);
330 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), 6 * (nNumRows - i - 1) + 101, static_cast<size_t>(m_pDoc->GetValue(3, i, 0)));
332 m_pDoc->DeleteTab(0);
335 void ScParallelismTest::testSingleRef()
337 m_pDoc->InsertTab(0, "1");
339 const size_t nNumRows = 200;
340 for (size_t i = 0; i < nNumRows; ++i)
342 m_pDoc->SetValue(0, i, 0, static_cast<double>(i));
343 m_pDoc->SetFormula(ScAddress(1, i, 0), "=A" + OUString::number(i+1), formula::FormulaGrammar::GRAM_NATIVE_UI);
346 m_xDocShell->DoHardRecalc();
348 for (size_t i = 0; i < nNumRows; ++i)
350 OString aMsg = "At row " + OString::number(i);
351 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), i, static_cast<size_t>(m_pDoc->GetValue(1, i, 0)));
353 m_pDoc->DeleteTab(0);
356 // Common test setup steps for testSUMIFImplicitRange*()
357 void lcl_setupCommon(ScDocument* pDoc, size_t nNumRows, size_t nConstCellValue)
359 pDoc->SetValue(3, 0, 0, static_cast<double>(nConstCellValue)); // D1
360 for (size_t i = 0; i <= (nNumRows*2); ++i)
362 pDoc->SetValue(0, i, 0, static_cast<double>(i));
363 pDoc->SetFormula(ScAddress(1, i, 0),
364 "=A" + OUString::number(i+1),
365 formula::FormulaGrammar::GRAM_NATIVE_UI);
369 void ScParallelismTest::testSUMIFImplicitRange()
371 sc::AutoCalcSwitch aACSwitch(*m_pDoc, false);
372 m_pDoc->InsertTab(0, "1");
374 const size_t nNumRows = 1048;
375 const size_t nConstCellValue = 20;
376 lcl_setupCommon(m_pDoc, nNumRows, nConstCellValue);
377 OUString aSrcRange = "$A$1:$A$" + OUString::number(nNumRows);
378 OUString aFormula;
379 for (size_t i = 0; i < nNumRows; ++i)
381 aFormula = "=SUMIF(" + aSrcRange + ";$D$1;$B$1)";
382 m_pDoc->SetFormula(ScAddress(2, i, 0),
383 aFormula,
384 formula::FormulaGrammar::GRAM_NATIVE_UI);
387 ScFormulaCell* pCell = m_pDoc->GetFormulaCell(ScAddress(2, 0, 0));
388 sc::AutoCalcSwitch aACSwitch2(*m_pDoc, true);
389 pCell->InterpretFormulaGroup(); // Start calculation on the F.G at C1
391 for (size_t i = 0; i < nNumRows; ++i)
393 OString aMsg = "At row " + OString::number(i);
394 CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nConstCellValue, static_cast<size_t>(m_pDoc->GetValue(2, i, 0)));
396 m_pDoc->DeleteTab(0);
399 CPPUNIT_TEST_SUITE_REGISTRATION(ScParallelismTest);
401 CPPUNIT_PLUGIN_IMPLEMENT();
403 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */