1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include "helper/debughelper.hxx"
12 #include "helper/qahelper.hxx"
13 #include <markdata.hxx>
14 #include <calcconfig.hxx>
15 #include <clipparam.hxx>
16 #include <compiler.hxx>
17 #include <tokenarray.hxx>
18 #include <refdata.hxx>
19 #include <scopetools.hxx>
20 #include <formulacell.hxx>
22 #include <docfunc.hxx>
23 #include <paramisc.hxx>
24 #include <tokenstringcontext.hxx>
25 #include <refupdatecontext.hxx>
27 #include <scmatrix.hxx>
28 #include <validat.hxx>
29 #include <scitems.hxx>
30 #include <patattr.hxx>
31 #include <docpool.hxx>
32 #include <docoptio.hxx>
33 #include <formulaopt.hxx>
34 #include <externalrefmgr.hxx>
35 #include <svl/itemset.hxx>
37 #include <formula/vectortoken.hxx>
38 #include <svl/broadcast.hxx>
39 #include <svl/intitem.hxx>
40 #include <sfx2/docfile.hxx>
47 using namespace formula
;
51 ScRange
getCachedRange(const ScExternalRefCache::TableTypeRef
& pCacheTab
)
56 pCacheTab
->getAllRows(aRows
);
58 for (const SCROW nRow
: aRows
)
61 pCacheTab
->getAllCols(nRow
, aCols
);
62 for (const SCCOL nCol
: aCols
)
66 aRange
.aStart
= ScAddress(nCol
, nRow
, 0);
67 aRange
.aEnd
= aRange
.aStart
;
72 if (nCol
< aRange
.aStart
.Col())
73 aRange
.aStart
.SetCol(nCol
);
74 else if (aRange
.aEnd
.Col() < nCol
)
75 aRange
.aEnd
.SetCol(nCol
);
77 if (nRow
< aRange
.aStart
.Row())
78 aRange
.aStart
.SetRow(nRow
);
79 else if (aRange
.aEnd
.Row() < nRow
)
80 aRange
.aEnd
.SetRow(nRow
);
89 void Test::testFormulaCreateStringFromTokens()
92 OUString
const aTabName1("Test");
93 OUString
const aTabName2("Kevin's Data");
94 OUString
const aTabName3("Past Data");
95 OUString
const aTabName4("2013");
96 m_pDoc
->InsertTab(0, aTabName1
);
97 m_pDoc
->InsertTab(1, aTabName2
);
98 m_pDoc
->InsertTab(2, aTabName3
);
99 m_pDoc
->InsertTab(3, aTabName4
);
101 // Insert named ranges.
102 static const struct {
107 { true, "x", "Test.H1" },
108 { true, "y", "Test.H2" },
109 { true, "z", "Test.H3" },
111 { false, "sheetx", "Test.J1" }
114 ScRangeName
* pGlobalNames
= m_pDoc
->GetRangeName();
115 ScRangeName
* pSheetNames
= m_pDoc
->GetRangeName(0);
116 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames
);
117 CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames
);
119 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
121 ScRangeData
* pName
= new ScRangeData(
122 m_pDoc
, OUString::createFromAscii(aNames
[i
].pName
), OUString::createFromAscii(aNames
[i
].pExpr
),
123 ScAddress(0,0,0), ScRangeData::Type::Name
, formula::FormulaGrammar::GRAM_NATIVE
);
125 if (aNames
[i
].bGlobal
)
127 bool bInserted
= pGlobalNames
->insert(pName
);
128 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted
);
132 bool bInserted
= pSheetNames
->insert(pName
);
133 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted
);
138 static const struct {
146 { "Table1", 0, 0, 0, 10, 10 },
147 { "Table2", 1, 0, 0, 10, 10 },
148 { "Table3", 2, 0, 0, 10, 10 }
151 ScDBCollection
* pDBs
= m_pDoc
->GetDBCollection();
152 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs
);
154 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aDBs
); ++i
)
156 std::unique_ptr
<ScDBData
> pData( new ScDBData(
157 OUString::createFromAscii(
158 aDBs
[i
].pName
), aDBs
[i
].nTab
, aDBs
[i
].nCol1
, aDBs
[i
].nRow1
, aDBs
[i
].nCol2
,aDBs
[i
].nRow2
) );
159 bool bInserted
= pDBs
->getNamedDBs().insert(std::move(pData
));
160 CPPUNIT_ASSERT_MESSAGE(
162 OStringLiteral("Failed to insert \"") + aDBs
[i
].pName
+ "\"").getStr(),
166 const char* aTests
[] = {
168 "SUM(A1:A10;B1:B10;C5;D6)",
169 "IF(Test.B10<>10;\"Good\";\"Bad\")",
170 "AVERAGE('2013'.B10:C20)",
171 "'Kevin''s Data'.B10",
172 "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
173 "x+y*z", // named ranges
174 "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
175 "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
176 "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
177 "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
181 std::unique_ptr
<ScTokenArray
> pArray
;
183 sc::TokenStringContext
aCxt(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH
);
185 // Artificially add external reference data after the context object is
187 aCxt
.maExternalFileNames
.emplace_back("file:///path/to/fake.file");
188 std::vector
<OUString
> aExtTabNames
;
189 aExtTabNames
.emplace_back("Sheet");
190 aCxt
.maExternalCachedTabNames
.emplace(0, aExtTabNames
);
192 ScAddress
aPos(0,0,0);
194 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aTests
); ++i
)
197 OUString aFormula
= OUString::createFromAscii(aTests
[i
]);
199 ScCompiler
aComp(m_pDoc
, aPos
, FormulaGrammar::GRAM_ENGLISH
);
200 #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
201 pArray
.reset(aComp
.CompileString(aFormula
));
202 CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray
.get());
204 OUString aCheck
= pArray
->CreateString(aCxt
, aPos
);
205 CPPUNIT_ASSERT_EQUAL(aFormula
, aCheck
);
209 m_pDoc
->DeleteTab(3);
210 m_pDoc
->DeleteTab(2);
211 m_pDoc
->DeleteTab(1);
212 m_pDoc
->DeleteTab(0);
217 bool isEmpty( const formula::VectorRefArray
& rArray
, size_t nPos
)
219 if (rArray
.mpStringArray
)
221 if (rArray
.mpStringArray
[nPos
])
225 if (rArray
.mpNumericArray
)
226 return rtl::math::isNan(rArray
.mpNumericArray
[nPos
]);
231 bool equals( const formula::VectorRefArray
& rArray
, size_t nPos
, double fVal
)
233 if (rArray
.mpStringArray
&& rArray
.mpStringArray
[nPos
])
234 // This is a string cell.
237 return rArray
.mpNumericArray
&& rArray
.mpNumericArray
[nPos
] == fVal
;
240 bool equals( const formula::VectorRefArray
& rArray
, size_t nPos
, const OUString
& rVal
)
242 if (!rArray
.mpStringArray
)
245 bool bEquals
= OUString(rArray
.mpStringArray
[nPos
]).equalsIgnoreAsciiCase(rVal
);
248 cerr
<< "Expected: " << rVal
.toAsciiUpperCase() << " (upcased)" << endl
;
249 cerr
<< "Actual: " << OUString(rArray
.mpStringArray
[nPos
]) << " (upcased)" << endl
;
256 void Test::testFormulaParseReference()
258 OUString
aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
259 m_pDoc
->InsertTab(0, "Dummy"); // just to shift the sheet indices...
260 m_pDoc
->InsertTab(1, aTab1
); // name with a single quote.
261 m_pDoc
->InsertTab(2, aTab2
); // name with 2 single quotes.
262 m_pDoc
->InsertTab(3, aTab3
); // name without single quotes.
263 m_pDoc
->InsertTab(4, aTab4
); // name that doesn't require to be quoted.
266 m_pDoc
->GetName(1, aTabName
);
267 CPPUNIT_ASSERT_EQUAL(aTab1
, aTabName
);
268 m_pDoc
->GetName(2, aTabName
);
269 CPPUNIT_ASSERT_EQUAL(aTab2
, aTabName
);
270 m_pDoc
->GetName(3, aTabName
);
271 CPPUNIT_ASSERT_EQUAL(aTab3
, aTabName
);
272 m_pDoc
->GetName(4, aTabName
);
273 CPPUNIT_ASSERT_EQUAL(aTab4
, aTabName
);
275 // Make sure the formula input and output match.
277 const char* aChecks
[] = {
279 "'90''s and 70''s'.$AB$100",
280 "'All Others'.Z$100",
284 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
286 // Use the 'Dummy' sheet for this.
287 OUString aInput
= "=" + OUString::createFromAscii(aChecks
[i
]);
288 m_pDoc
->SetString(ScAddress(0,0,0), aInput
);
289 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,0,0), aChecks
[i
], "Wrong formula");
294 ScAddress::ExternalInfo aExtInfo
;
295 ScRefFlags nRes
= aPos
.Parse("'90''s Music'.D10", m_pDoc
, formula::FormulaGrammar::CONV_OOO
, &aExtInfo
);
296 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
297 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(1), aPos
.Tab());
298 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(3), aPos
.Col());
299 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(9), aPos
.Row());
300 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo
.mbExternal
);
302 nRes
= aPos
.Parse("'90''s and 70''s'.C100", m_pDoc
, formula::FormulaGrammar::CONV_OOO
, &aExtInfo
);
303 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
304 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(2), aPos
.Tab());
305 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(2), aPos
.Col());
306 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(99), aPos
.Row());
307 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo
.mbExternal
);
309 nRes
= aPos
.Parse("'All Others'.B3", m_pDoc
, formula::FormulaGrammar::CONV_OOO
, &aExtInfo
);
310 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
311 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(3), aPos
.Tab());
312 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), aPos
.Col());
313 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), aPos
.Row());
314 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo
.mbExternal
);
316 nRes
= aPos
.Parse("NoQuote.E13", m_pDoc
, formula::FormulaGrammar::CONV_OOO
, &aExtInfo
);
317 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
318 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(4), aPos
.Tab());
319 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(4), aPos
.Col());
320 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(12), aPos
.Row());
321 CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo
.mbExternal
);
325 aRange
.aStart
.SetTab(0);
326 nRes
= aRange
.Parse(":B", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
327 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
329 aRange
.aStart
.SetTab(0);
330 nRes
= aRange
.Parse("B:", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
331 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
333 aRange
.aStart
.SetTab(0);
334 nRes
= aRange
.Parse(":B2", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
335 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
337 aRange
.aStart
.SetTab(0);
338 nRes
= aRange
.Parse("B2:", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
339 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
341 aRange
.aStart
.SetTab(0);
342 nRes
= aRange
.Parse(":2", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
343 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
345 aRange
.aStart
.SetTab(0);
346 nRes
= aRange
.Parse("2:", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
347 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
349 aRange
.aStart
.SetTab(0);
350 nRes
= aRange
.Parse(":2B", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
351 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
353 aRange
.aStart
.SetTab(0);
354 nRes
= aRange
.Parse("2B:", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
355 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
357 aRange
.aStart
.SetTab(0);
358 nRes
= aRange
.Parse("abc_foo:abc_bar", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
359 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
361 aRange
.aStart
.SetTab(0);
362 nRes
= aRange
.Parse("B1:B2~C1", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
363 CPPUNIT_ASSERT_MESSAGE("Should fail to parse.", !(nRes
& ScRefFlags::VALID
));
365 aRange
.aStart
.SetTab(0);
366 nRes
= aRange
.Parse("B:B", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
367 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
368 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aStart
.Tab());
369 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), aRange
.aStart
.Col());
370 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(0), aRange
.aStart
.Row());
371 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aEnd
.Tab());
372 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), aRange
.aEnd
.Col());
373 CPPUNIT_ASSERT_EQUAL(m_pDoc
->MaxRow(), aRange
.aEnd
.Row());
374 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
375 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
),
376 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
377 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
)));
378 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ZERO
),
379 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
)));
380 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
),
381 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
)));
383 aRange
.aStart
.SetTab(0);
384 nRes
= aRange
.Parse("2:2", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
385 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
386 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aStart
.Tab());
387 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(0), aRange
.aStart
.Col());
388 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), aRange
.aStart
.Row());
389 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aEnd
.Tab());
390 CPPUNIT_ASSERT_EQUAL(m_pDoc
->MaxCol(), aRange
.aEnd
.Col());
391 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), aRange
.aEnd
.Row());
392 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
393 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
),
394 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
395 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
)));
396 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ZERO
),
397 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
)));
398 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
),
399 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
)));
401 nRes
= aRange
.Parse("NoQuote.B:C", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
402 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
403 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(4), aRange
.aStart
.Tab());
404 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), aRange
.aStart
.Col());
405 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(0), aRange
.aStart
.Row());
406 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(4), aRange
.aEnd
.Tab());
407 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(2), aRange
.aEnd
.Col());
408 CPPUNIT_ASSERT_EQUAL(m_pDoc
->MaxRow(), aRange
.aEnd
.Row());
409 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
410 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
),
411 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
412 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
)));
413 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ZERO
),
414 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
)));
415 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
),
416 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
)));
418 // Both rows at sheet bounds and relative => convert to absolute => entire column reference.
419 aRange
.aStart
.SetTab(0);
420 nRes
= aRange
.Parse("B1:B1048576", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
421 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
422 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aStart
.Tab());
423 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), aRange
.aStart
.Col());
424 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(0), aRange
.aStart
.Row());
425 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aEnd
.Tab());
426 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), aRange
.aEnd
.Col());
427 CPPUNIT_ASSERT_EQUAL(m_pDoc
->MaxRow(), aRange
.aEnd
.Row());
428 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
429 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
),
430 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
431 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
)));
432 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ZERO
),
433 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
)));
434 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
),
435 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
)));
437 // Both columns at sheet bounds and relative => convert to absolute => entire row reference.
438 aRange
.aStart
.SetTab(0);
439 nRes
= aRange
.Parse("A2:AMJ2", m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
440 CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes
& ScRefFlags::VALID
));
441 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aStart
.Tab());
442 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(0), aRange
.aStart
.Col());
443 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), aRange
.aStart
.Row());
444 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB
>(0), aRange
.aEnd
.Tab());
445 CPPUNIT_ASSERT_EQUAL(m_pDoc
->MaxCol(), aRange
.aEnd
.Col());
446 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), aRange
.aEnd
.Row());
447 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
448 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
),
449 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::TAB_VALID
|
450 ScRefFlags::COL2_VALID
| ScRefFlags::ROW2_VALID
| ScRefFlags::TAB2_VALID
)));
451 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::ZERO
),
452 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::ROW_ABS
| ScRefFlags::ROW2_ABS
)));
453 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16
>(ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
),
454 static_cast<sal_uInt16
>(nRes
& (ScRefFlags::COL_ABS
| ScRefFlags::COL2_ABS
)));
456 // Check for reference input conversion to and display string of entire column/row.
458 const char* aChecks
[][2] = {
460 { "=B1:B1048576", "B:B" },
461 { "=B1:B$1048576", "B1:B$1048576" },
462 { "=B$1:B1048576", "B$1:B1048576" },
463 { "=B$1:B$1048576", "B:B" },
465 { "=A2:AMJ2", "2:2" },
466 { "=A2:$AMJ2", "A2:$AMJ2" },
467 { "=$A2:AMJ2", "$A2:AMJ2" },
468 { "=$A2:$AMJ2", "2:2" }
471 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
473 // Use the 'Dummy' sheet for this.
474 m_pDoc
->SetString(ScAddress(0,0,0), OUString::createFromAscii(aChecks
[i
][0]));
475 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,0,0), aChecks
[i
][1], "Wrong formula");
479 m_pDoc
->DeleteTab(4);
480 m_pDoc
->DeleteTab(3);
481 m_pDoc
->DeleteTab(2);
482 m_pDoc
->DeleteTab(1);
483 m_pDoc
->DeleteTab(0);
486 void Test::testFetchVectorRefArray()
488 m_pDoc
->InsertTab(0, "Test");
490 // All numeric cells in Column A.
491 m_pDoc
->SetValue(ScAddress(0,0,0), 1);
492 m_pDoc
->SetValue(ScAddress(0,1,0), 2);
493 m_pDoc
->SetValue(ScAddress(0,2,0), 3);
494 m_pDoc
->SetValue(ScAddress(0,3,0), 4);
496 formula::VectorRefArray aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(0,0,0), 4);
497 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
498 CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray
.mpStringArray
);
499 CPPUNIT_ASSERT_EQUAL(1.0, aArray
.mpNumericArray
[0]);
500 CPPUNIT_ASSERT_EQUAL(2.0, aArray
.mpNumericArray
[1]);
501 CPPUNIT_ASSERT_EQUAL(3.0, aArray
.mpNumericArray
[2]);
502 CPPUNIT_ASSERT_EQUAL(4.0, aArray
.mpNumericArray
[3]);
504 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(0,0,0), 5);
505 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
506 CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray
.mpStringArray
);
507 CPPUNIT_ASSERT_EQUAL(1.0, aArray
.mpNumericArray
[0]);
508 CPPUNIT_ASSERT_EQUAL(2.0, aArray
.mpNumericArray
[1]);
509 CPPUNIT_ASSERT_EQUAL(3.0, aArray
.mpNumericArray
[2]);
510 CPPUNIT_ASSERT_EQUAL(4.0, aArray
.mpNumericArray
[3]);
511 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 4));
513 // All string cells in Column B. Note that the fetched string arrays are
514 // only to be compared case-insensitively. Right now, we use upper cased
515 // strings to achieve case-insensitive-ness, but that may change. So,
516 // don't count on that.
517 m_pDoc
->SetString(ScAddress(1,0,0), "Andy");
518 m_pDoc
->SetString(ScAddress(1,1,0), "Bruce");
519 m_pDoc
->SetString(ScAddress(1,2,0), "Charlie");
520 m_pDoc
->SetString(ScAddress(1,3,0), "David");
521 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(1,0,0), 5);
522 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
523 CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray
.mpNumericArray
);
524 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 0, "Andy"));
525 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 1, "Bruce"));
526 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 2, "Charlie"));
527 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 3, "David"));
528 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 4));
530 // Mixture of numeric, string, and empty cells in Column C.
531 m_pDoc
->SetString(ScAddress(2,0,0), "Header");
532 m_pDoc
->SetValue(ScAddress(2,1,0), 11);
533 m_pDoc
->SetValue(ScAddress(2,2,0), 12);
534 m_pDoc
->SetValue(ScAddress(2,3,0), 13);
535 m_pDoc
->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
538 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(2,0,0), 7);
539 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
540 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray
.mpNumericArray
&& aArray
.mpStringArray
);
541 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 0, "Header"));
542 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 1, 11));
543 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 12));
544 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 3, 13));
545 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 4));
546 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 5, 36));
547 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 6));
549 // Mixed type again in Column D, but it starts with a numeric cell.
550 m_pDoc
->SetValue(ScAddress(3,0,0), 10);
551 m_pDoc
->SetString(ScAddress(3,1,0), "Below 10");
552 // Leave 2 empty cells.
553 m_pDoc
->SetValue(ScAddress(3,4,0), 11);
554 m_pDoc
->SetString(ScAddress(3,5,0), "=12");
555 m_pDoc
->SetString(ScAddress(3,6,0), "=13");
556 m_pDoc
->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
559 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(3,0,0), 8);
560 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
561 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray
.mpNumericArray
&& aArray
.mpStringArray
);
562 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 0, 10));
563 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 1, "Below 10"));
564 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 2));
565 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 3));
566 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 4, 11));
567 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 5, 12));
568 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 6, 13));
569 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 7, "ABC"));
571 // Column E consists of formula cells whose results are all numeric.
572 for (SCROW i
= 0; i
<= 6; ++i
)
573 m_pDoc
->SetString(ScAddress(4,i
,0), "=ROW()");
576 // Leave row 7 empty.
577 m_pDoc
->SetString(ScAddress(4,8,0), "Andy");
578 m_pDoc
->SetValue(ScAddress(4,9,0), 123);
580 // This array fits within a single formula block.
581 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(4,0,0), 5);
582 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
583 CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray
.mpNumericArray
&& !aArray
.mpStringArray
);
584 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 0, 1));
585 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 1, 2));
586 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 3));
587 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 3, 4));
588 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 4, 5));
590 // This array spans over multiple blocks.
591 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(4,0,0), 11);
592 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
593 CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray
.mpNumericArray
&& aArray
.mpStringArray
);
594 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 0, 1));
595 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 1, 2));
596 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 3));
597 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 3, 4));
598 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 4, 5));
599 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 5, 6));
600 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 6, 7));
601 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 7));
602 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 8, "Andy"));
603 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 9, 123));
604 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 10));
606 // Hit the cache but at a different start row.
607 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(4,2,0), 3);
608 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
609 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray
.mpNumericArray
);
610 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 0, 3));
611 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 1, 4));
612 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 5));
614 // Column F begins with empty rows at the top.
615 m_pDoc
->SetValue(ScAddress(5,2,0), 1.1);
616 m_pDoc
->SetValue(ScAddress(5,3,0), 1.2);
617 m_pDoc
->SetString(ScAddress(5,4,0), "=2*8");
620 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(5,2,0), 4);
621 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
622 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray
.mpNumericArray
);
623 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 0, 1.1));
624 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 1, 1.2));
625 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 16));
626 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 3));
628 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(5,0,0), 3);
629 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
630 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray
.mpNumericArray
);
631 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 0));
632 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 1));
633 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 1.1));
635 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(5,0,0), 10);
636 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
637 CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray
.mpNumericArray
);
638 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 0));
639 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 1));
640 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 2, 1.1));
641 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 3, 1.2));
642 CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray
, 4, 16));
643 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 5));
644 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 6));
645 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 7));
646 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 8));
647 CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray
, 9));
649 // Get the array for F3:F4. This array should only consist of numeric array.
650 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(5,2,0), 3);
651 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
652 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
653 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
655 // Column G consists only of strings.
656 m_pDoc
->SetString(ScAddress(6,0,0), "Title");
657 m_pDoc
->SetString(ScAddress(6,1,0), "foo");
658 m_pDoc
->SetString(ScAddress(6,2,0), "bar");
659 m_pDoc
->SetString(ScAddress(6,3,0), "foo");
660 m_pDoc
->SetString(ScAddress(6,4,0), "baz");
661 m_pDoc
->SetString(ScAddress(6,5,0), "quack");
662 m_pDoc
->SetString(ScAddress(6,6,0), "beep");
663 m_pDoc
->SetString(ScAddress(6,7,0), "kerker");
665 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
666 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
667 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray
.mpNumericArray
);
668 CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray
.mpStringArray
);
669 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 0, "foo"));
670 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 1, "bar"));
671 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 2, "foo"));
672 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 3, "baz"));
674 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
675 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
676 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray
.mpNumericArray
);
677 CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray
.mpStringArray
);
678 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 0, "bar"));
679 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 1, "foo"));
680 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 2, "baz"));
681 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 3, "quack"));
683 // Column H starts with formula cells.
684 for (SCROW i
= 0; i
< 10; ++i
)
685 m_pDoc
->SetString(ScAddress(7,i
,0), "=ROW()");
688 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
689 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
690 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
691 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
692 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 0, 4.0));
693 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 1, 5.0));
694 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 2, 6.0));
696 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
697 CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray
.isValid());
698 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
699 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
700 CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray
, 0, 5.0));
702 // Clear everything and start over.
703 clearRange(m_pDoc
, ScRange(0,0,0,m_pDoc
->MaxCol(),m_pDoc
->MaxRow(),0));
704 m_pDoc
->PrepareFormulaCalc();
706 // Totally empty range in a totally empty column (Column A).
707 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
708 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
709 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
710 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[0]));
711 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[1]));
712 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[2]));
714 // Totally empty range in a non-empty column (Column B).
715 m_pDoc
->SetString(ScAddress(1,10,0), "Some text"); // B11
716 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
717 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
718 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
719 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[0]));
720 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[1]));
721 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[2]));
723 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
724 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
725 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
726 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[0]));
727 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[1]));
728 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[2]));
730 // These values come from a cache because of the call above.
731 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
732 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
733 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
734 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[0]));
735 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[1]));
736 CPPUNIT_ASSERT(rtl::math::isNan(aArray
.mpNumericArray
[2]));
738 // The column begins with a string header at row 1 (Column C).
739 m_pDoc
->SetString(ScAddress(2,0,0), "MyHeader");
740 for (SCROW i
= 1; i
<= 9; ++i
) // rows 2-10 are numeric.
741 m_pDoc
->SetValue(ScAddress(2,i
,0), i
);
743 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(2,1,0), 9); // C2:C10
744 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
745 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
746 for (size_t i
= 0; i
< 9; ++i
)
747 CPPUNIT_ASSERT_EQUAL(double(i
+1), aArray
.mpNumericArray
[i
]);
749 // The column begins with a number, followed by a string then followed by
750 // a block of numbers (Column D).
751 m_pDoc
->SetValue(ScAddress(3,0,0), 0.0);
752 m_pDoc
->SetString(ScAddress(3,1,0), "Some string");
753 for (SCROW i
= 2; i
<= 9; ++i
) // rows 3-10 are numeric.
754 m_pDoc
->SetValue(ScAddress(3,i
,0), i
);
756 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(3,2,0), 8); // D3:D10
757 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
758 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
759 for (size_t i
= 0; i
< 8; ++i
)
760 CPPUNIT_ASSERT_EQUAL(double(i
+2), aArray
.mpNumericArray
[i
]);
762 // The column begins with a formula, followed by a string then followed by
763 // a block of numbers (Column E).
764 m_pDoc
->SetString(ScAddress(4,0,0), "=1*2");
765 m_pDoc
->SetString(ScAddress(4,1,0), "Some string");
766 for (SCROW i
= 2; i
<= 9; ++i
) // rows 3-10 are numeric.
767 m_pDoc
->SetValue(ScAddress(4,i
,0), i
*2);
769 aArray
= m_pDoc
->FetchVectorRefArray(ScAddress(4,2,0), 8); // E3:E10
770 CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray
.mpNumericArray
);
771 CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray
.mpStringArray
);
772 for (size_t i
= 0; i
< 8; ++i
)
773 CPPUNIT_ASSERT_EQUAL(double((i
+2)*2), aArray
.mpNumericArray
[i
]);
775 m_pDoc
->DeleteTab(0);
778 void Test::testGroupConverter3D()
780 m_pDoc
->InsertTab(0, "Test");
781 m_pDoc
->InsertTab(1, "Test2");
783 m_pDoc
->SetValue(1, 0, 0, 1.0);
784 m_pDoc
->SetValue(1, 0, 1, 2.0);
786 for (SCROW nRow
= 0; nRow
< 200; ++nRow
)
788 OUString aFormula
= "=SUM(Test.B" + OUString::number(nRow
+1) + ":Test2.B" + OUString::number(nRow
+1) + ")";
789 m_pDoc
->SetString(0, nRow
, 0, aFormula
);
792 double nVal
= m_pDoc
->GetValue(0, 0, 0);
793 CPPUNIT_ASSERT_EQUAL(3.0, nVal
);
795 m_pDoc
->DeleteTab(1);
796 m_pDoc
->DeleteTab(0);
799 void Test::testFormulaHashAndTag()
801 m_pDoc
->InsertTab(0, "Test");
803 ScAddress
aPos1(0,0,0), aPos2(1,0,0);
805 // Test formula hashing.
807 static const struct {
808 const char* pFormula1
; const char* pFormula2
; bool bEqual
;
810 { "=1", "=2", false }, // different constants
811 { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
812 { "=C2*3", "=D2*3", true }, // relative references
813 { "=C2*3", "=D2*4", false }, // different constants
814 { "=C2*4", "=D2*4", true }, // relative references
815 { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
816 { "=$C3/2", "=$C3/2", true }, // absolute column references
817 { "=C$3/2", "=D$3/2", true }, // absolute row references
818 { "=$E$30/2", "=$E$30/2", true }, // absolute references
819 { "=X20", "=$X$20", false }, // absolute vs relative
820 { "=X20", "=X$20", false }, // absolute vs relative
821 { "=X20", "=$X20", false }, // absolute vs relative
822 { "=X$20", "=$X20", false }, // column absolute vs row absolute
823 // similar enough for merging ...
824 { "=A1", "=B1", true },
825 { "=$A$1", "=$B$1", true },
826 { "=A1", "=C2", true },
827 { "=SUM(A1)", "=SUM(B1)", true },
828 { "=A1+3", "=B1+3", true },
829 { "=A1+7", "=B1+42", false },
832 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHashTests
); ++i
)
834 m_pDoc
->SetString(aPos1
, OUString::createFromAscii(aHashTests
[i
].pFormula1
));
835 m_pDoc
->SetString(aPos2
, OUString::createFromAscii(aHashTests
[i
].pFormula2
));
836 size_t nHashVal1
= m_pDoc
->GetFormulaHash(aPos1
);
837 size_t nHashVal2
= m_pDoc
->GetFormulaHash(aPos2
);
839 std::ostringstream os
;
840 os
<< "(expr1:" << aHashTests
[i
].pFormula1
<< "; expr2:" << aHashTests
[i
].pFormula2
<< ")";
841 if (aHashTests
[i
].bEqual
)
843 os
<< " Error: these hashes should be equal." << endl
;
844 CPPUNIT_ASSERT_EQUAL_MESSAGE(os
.str(), nHashVal1
, nHashVal2
);
848 os
<< " Error: these hashes should differ." << endl
;
849 CPPUNIT_ASSERT_MESSAGE(os
.str(), nHashVal1
!= nHashVal2
);
860 // Test formula vectorization state.
862 static const struct {
863 const char* pFormula
;
864 ScFormulaVectorState
const eState
;
866 { "=SUM(1;2;3;4;5)", FormulaVectorEnabled
},
867 { "=NOW()", FormulaVectorDisabled
},
868 { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference
},
869 { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference
},
870 { "=MIN(10;11;22)", FormulaVectorEnabled
},
871 { "=H4", FormulaVectorCheckReference
},
874 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aVectorTests
); ++i
)
876 m_pDoc
->SetString(aPos1
, OUString::createFromAscii(aVectorTests
[i
].pFormula
));
877 ScFormulaVectorState eState
= m_pDoc
->GetFormulaVectorState(aPos1
);
878 ScFormulaVectorState eReferenceState
= aVectorTests
[i
].eState
;
880 if (eState
!= eReferenceState
)
882 std::ostringstream os
;
883 os
<< "Unexpected vectorization state: expr: '" << aVectorTests
[i
].pFormula
<< "'";
884 CPPUNIT_ASSERT_MESSAGE(os
.str(), false);
889 m_pDoc
->DeleteTab(0);
892 void Test::testFormulaTokenEquality()
894 struct FormulaTokenEqualityTest
896 const char* mpFormula1
;
897 const char* mpFormula2
;
901 static const FormulaTokenEqualityTest aTests
[] = {
902 { "R1C2", "R1C2", true },
903 { "R1C2", "R1C3", false },
904 { "R1C2", "R2C2", false },
905 { "RC2", "RC[1]", false },
906 { "R1C2:R10C2", "R1C2:R10C2", true },
907 { "R1C2:R10C2", "R1C2:R11C2", false },
909 { "RC[1]+1.2", "RC[1]+1.2", true },
910 { "RC[1]*0.2", "RC[1]*0.5", false },
911 { "\"Test1\"", "\"Test2\"", false },
912 { "\"Test\"", "\"Test\"", true },
913 { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
914 { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
917 formula::FormulaGrammar::Grammar eGram
= formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
;
918 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aTests
); ++i
)
920 ScFormulaCell
aCell1(m_pDoc
, ScAddress(), OUString::createFromAscii(aTests
[i
].mpFormula1
), eGram
);
921 ScFormulaCell
aCell2(m_pDoc
, ScAddress(), OUString::createFromAscii(aTests
[i
].mpFormula2
), eGram
);
923 ScFormulaCell::CompareState eComp
= aCell1
.CompareByTokenArray(aCell2
);
924 if (aTests
[i
].mbEqual
)
926 if (eComp
== ScFormulaCell::NotEqual
)
928 std::ostringstream os
;
929 os
<< "These two formulas should be evaluated equal: '"
930 << aTests
[i
].mpFormula1
<< "' vs '" << aTests
[i
].mpFormula2
<< "'" << endl
;
931 CPPUNIT_FAIL(os
.str());
936 if (eComp
!= ScFormulaCell::NotEqual
)
938 std::ostringstream os
;
939 os
<< "These two formulas should be evaluated non-equal: '"
940 << aTests
[i
].mpFormula1
<< "' vs '" << aTests
[i
].mpFormula2
<< "'" << endl
;
941 CPPUNIT_FAIL(os
.str());
947 void Test::testFormulaRefData()
949 ScAddress
aAddr(4,5,3), aPos(2,2,2);
950 ScSingleRefData aRef
;
951 aRef
.InitAddress(aAddr
);
952 CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef
.IsRowRel() && !aRef
.IsColRel() && !aRef
.IsTabRel());
953 CPPUNIT_ASSERT_EQUAL(SCCOL(4), aRef
.Col());
954 CPPUNIT_ASSERT_EQUAL(SCROW(5), aRef
.Row());
955 CPPUNIT_ASSERT_EQUAL(SCTAB(3), aRef
.Tab());
957 aRef
.SetRowRel(true);
958 aRef
.SetColRel(true);
959 aRef
.SetTabRel(true);
960 aRef
.SetAddress(aAddr
, aPos
);
961 CPPUNIT_ASSERT_EQUAL(SCCOL(2), aRef
.Col());
962 CPPUNIT_ASSERT_EQUAL(SCROW(3), aRef
.Row());
963 CPPUNIT_ASSERT_EQUAL(SCTAB(1), aRef
.Tab());
965 // Test extension of range reference.
967 ScComplexRefData aDoubleRef
;
968 aDoubleRef
.InitRange(ScRange(2,2,0,4,4,0));
970 aRef
.InitAddress(ScAddress(6,5,0));
972 aDoubleRef
.Extend(aRef
, ScAddress());
973 ScRange aTest
= aDoubleRef
.toAbs(ScAddress());
974 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(2,2,0), aTest
.aStart
);
975 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(6,5,0), aTest
.aEnd
);
977 ScComplexRefData aDoubleRef2
;
978 aDoubleRef2
.InitRangeRel(ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
979 aDoubleRef
.Extend(aDoubleRef2
, ScAddress(5,5,0));
980 aTest
= aDoubleRef
.toAbs(ScAddress(5,5,0));
982 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong start position of extended range.", ScAddress(1,2,0), aTest
.aStart
);
983 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong end position of extended range.", ScAddress(8,6,0), aTest
.aEnd
);
986 void Test::testFormulaCompiler()
988 static const struct {
989 const char* pInput
; FormulaGrammar::Grammar eInputGram
;
990 const char* pOutput
; FormulaGrammar::Grammar eOutputGram
;
992 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE
, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF
},
993 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE
, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE
},
994 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE
, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1
},
995 { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE
, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1
},
998 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aTests
); ++i
)
1000 std::unique_ptr
<ScTokenArray
> pArray
= compileFormula(m_pDoc
, OUString::createFromAscii(aTests
[i
].pInput
), aTests
[i
].eInputGram
);
1001 CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray
);
1003 OUString aFormula
= toString(*m_pDoc
, ScAddress(), *pArray
, aTests
[i
].eOutputGram
);
1004 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests
[i
].pOutput
), aFormula
);
1008 void Test::testFormulaCompilerJumpReordering()
1013 StackVar
const meType
;
1016 // Set separators first.
1017 ScFormulaOptions aOptions
;
1018 aOptions
.SetFormulaSepArg(";");
1019 aOptions
.SetFormulaSepArrayCol(";");
1020 aOptions
.SetFormulaSepArrayRow("|");
1021 getDocShell().SetFormulaOptions(aOptions
);
1024 OUString
const aInput("=IF(B1;12;\"text\")");
1026 // Compile formula string first.
1027 std::unique_ptr
<ScTokenArray
> pCode(compileFormula(m_pDoc
, aInput
));
1028 CPPUNIT_ASSERT(pCode
);
1030 // Then generate RPN tokens.
1031 ScCompiler
aCompRPN(m_pDoc
, ScAddress(), *pCode
, FormulaGrammar::GRAM_NATIVE
);
1032 aCompRPN
.CompileTokenArray();
1034 // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
1035 static const TokenCheck aCheckRPN
[] =
1037 { ocPush
, svSingleRef
},
1038 { ocIf
, svUnknown
}, // type is context dependent, don't test it
1039 { ocPush
, svDouble
},
1041 { ocPush
, svString
},
1045 sal_uInt16 nLen
= pCode
->GetCodeLen();
1046 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16
>(SAL_N_ELEMENTS(aCheckRPN
)), nLen
);
1048 FormulaToken
** ppTokens
= pCode
->GetCode();
1049 for (sal_uInt16 i
= 0; i
< nLen
; ++i
)
1051 const FormulaToken
* p
= ppTokens
[i
];
1052 CPPUNIT_ASSERT_EQUAL(aCheckRPN
[i
].meOp
, p
->GetOpCode());
1053 if (aCheckRPN
[i
].meOp
!= ocIf
)
1054 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN
[i
].meType
), static_cast<int>(p
->GetType()));
1057 // Generate RPN tokens again, but this time no jump command reordering.
1059 ScCompiler
aCompRPN2(m_pDoc
, ScAddress(), *pCode
, FormulaGrammar::GRAM_NATIVE
);
1060 aCompRPN2
.EnableJumpCommandReorder(false);
1061 aCompRPN2
.CompileTokenArray();
1063 static const TokenCheck aCheckRPN2
[] =
1065 { ocPush
, svSingleRef
},
1066 { ocPush
, svDouble
},
1067 { ocPush
, svString
},
1068 { ocIf
, svUnknown
}, // type is context dependent, don't test it
1071 nLen
= pCode
->GetCodeLen();
1072 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16
>(SAL_N_ELEMENTS(aCheckRPN2
)), nLen
);
1073 ppTokens
= pCode
->GetCode();
1074 for (sal_uInt16 i
= 0; i
< nLen
; ++i
)
1076 const FormulaToken
* p
= ppTokens
[i
];
1077 CPPUNIT_ASSERT_EQUAL(aCheckRPN2
[i
].meOp
, p
->GetOpCode());
1078 if (aCheckRPN
[i
].meOp
== ocPush
)
1079 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN2
[i
].meType
), static_cast<int>(p
->GetType()));
1084 void Test::testFormulaCompilerImplicitIntersection2Param()
1086 struct TestCaseFormula
1088 OUString
const aFormula
;
1089 ScAddress
const aCellAddress
;
1090 ScRange
const aSumRange
;
1091 bool const bStartColRel
; // SumRange-StartCol
1092 bool const bEndColRel
; // SumRange-EndCol
1095 m_pDoc
->InsertTab(0, "Formula");
1096 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1099 TestCaseFormula aTestCases
[] =
1101 // Formula, FormulaCellAddress, SumRange with Implicit Intersection
1103 // Sumrange is single cell, address is abs
1105 OUString("=SUMIF($B$2:$B$10;F2;$D$5)"),
1107 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1112 // Sumrange is single cell, address is relative
1114 OUString("=SUMIF($B$2:$B$10;F2;D5)"),
1116 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1121 // Baserange(abs,abs), Sumrange(abs,abs)
1123 OUString("=SUMIF($B$2:$B$10;F2;$D$5:$D$10)"),
1125 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1130 // Baserange(abs,rel), Sumrange(abs,abs)
1132 OUString("=SUMIF($B$2:B10;F2;$D$5:$D$10)"),
1134 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1139 // Baserange(rel,abs), Sumrange(abs,abs)
1141 OUString("=SUMIF(B2:$B$10;F2;$D$5:$D$10)"),
1143 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1148 // Baserange(rel,rel), Sumrange(abs,abs)
1150 OUString("=SUMIF(B2:B10;F2;$D$5:$D$10)"),
1152 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1157 // Baserange(abs,abs), Sumrange(abs,rel)
1159 OUString("=SUMIF($B$2:$B$10;F2;$D$5:D10)"),
1161 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1166 // Baserange(abs,abs), Sumrange(rel,abs)
1168 OUString("=SUMIF($B$2:$B$10;F2;D5:$D$10)"),
1170 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1175 // Baserange(abs,abs), Sumrange(rel,rel)
1177 OUString("=SUMIF($B$2:$B$10;F2;D5:D10)"),
1179 ScRange( ScAddress(3, 4, 0), ScAddress(3, 12, 0) ),
1185 for (const auto& rCase
: aTestCases
)
1187 m_pDoc
->SetString(rCase
.aCellAddress
, rCase
.aFormula
);
1188 const ScFormulaCell
* pCell
= m_pDoc
->GetFormulaCell(rCase
.aCellAddress
);
1189 const ScTokenArray
* pCode
= pCell
->GetCode();
1190 CPPUNIT_ASSERT(pCode
);
1192 sal_uInt16 nLen
= pCode
->GetCodeLen();
1193 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16
>(4), nLen
);
1195 FormulaToken
** ppTokens
= pCode
->GetCode();
1197 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(first argument to SUMIF)", svDoubleRef
, ppTokens
[0]->GetType());
1198 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of token(third argument to SUMIF)", svDoubleRef
, ppTokens
[2]->GetType());
1200 ScComplexRefData aSumRangeData
= *ppTokens
[2]->GetDoubleRef();
1201 ScRange aSumRange
= aSumRangeData
.toAbs(rCase
.aCellAddress
);
1202 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong sum-range in RPN array", rCase
.aSumRange
, aSumRange
);
1204 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for start column address in sum-range", rCase
.bStartColRel
, aSumRangeData
.Ref1
.IsColRel());
1205 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong IsRel type for end column address in sum-range", rCase
.bEndColRel
, aSumRangeData
.Ref2
.IsColRel());
1210 void Test::testFormulaCompilerImplicitIntersection1ParamNoChange()
1212 struct TestCaseFormulaNoChange
1214 OUString
const aFormula
;
1215 ScAddress
const aCellAddress
;
1216 bool const bMatrixFormula
;
1217 bool const bForcedArray
;
1220 m_pDoc
->InsertTab(0, "Formula");
1221 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1224 ScAddress
aStartAddr(4, 5, 0);
1225 TestCaseFormulaNoChange aCasesNoChange
[] =
1228 OUString("=COS(A$2:A$100)"), // No change because of abs col ref.
1234 OUString("=COS($A7:$A100)"), // No intersection
1240 OUString("=COS($A5:$C7)"), // No intersection 2-D range
1246 OUString("=SUMPRODUCT(COS(A6:A10))"), // COS() in forced array mode
1252 OUString("=COS(A6:A10)"), // Matrix formula
1259 for (const auto& rCase
: aCasesNoChange
)
1261 if (rCase
.bMatrixFormula
)
1263 ScMarkData
aMark(MAXROW
, MAXCOL
);
1264 aMark
.SelectOneTable(0);
1265 SCCOL nColStart
= rCase
.aCellAddress
.Col();
1266 SCROW nRowStart
= rCase
.aCellAddress
.Row();
1267 m_pDoc
->InsertMatrixFormula(nColStart
, nRowStart
, nColStart
, nRowStart
+ 4,
1268 aMark
, rCase
.aFormula
);
1271 m_pDoc
->SetString(rCase
.aCellAddress
, rCase
.aFormula
);
1273 const ScFormulaCell
* pCell
= m_pDoc
->GetFormulaCell(rCase
.aCellAddress
);
1274 const ScTokenArray
* pCode
= pCell
->GetCode();
1275 CPPUNIT_ASSERT(pCode
);
1277 sal_uInt16 nRPNLen
= pCode
->GetCodeLen();
1278 sal_uInt16 nRawLen
= pCode
->GetLen();
1279 sal_uInt16 nRawArgPos
;
1280 if (rCase
.bForcedArray
)
1283 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16
>(7), nRawLen
);
1284 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16
>(3), nRPNLen
);
1289 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong raw token count.", static_cast<sal_uInt16
>(4), nRawLen
);
1290 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16
>(2), nRPNLen
);
1293 FormulaToken
** ppRawTokens
= pCode
->GetArray();
1294 FormulaToken
** ppRPNTokens
= pCode
->GetCode();
1296 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of raw token(argument to COS)", svDoubleRef
, ppRawTokens
[nRawArgPos
]->GetType());
1297 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svDoubleRef
, ppRPNTokens
[0]->GetType());
1299 ScComplexRefData aArgRangeRaw
= *ppRawTokens
[nRawArgPos
]->GetDoubleRef();
1300 ScComplexRefData aArgRangeRPN
= *ppRPNTokens
[0]->GetDoubleRef();
1301 bool bRawMatchRPNToken(aArgRangeRaw
== aArgRangeRPN
);
1302 CPPUNIT_ASSERT_MESSAGE("raw arg token and RPN arg token contents do not match", bRawMatchRPNToken
);
1307 void Test::testFormulaCompilerImplicitIntersection1ParamWithChange()
1309 struct TestCaseFormula
1311 OUString
const aFormula
;
1312 ScAddress
const aCellAddress
;
1313 ScAddress
const aArgAddr
;
1316 m_pDoc
->InsertTab(0, "Formula");
1317 m_pDoc
->InsertTab(1, "Formula1");
1318 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1321 ScAddress
aStartAddr(10, 5, 0);
1322 TestCaseFormula aCasesWithChange
[] =
1325 OUString("=COS($A6:$A100)"), // Corner case with intersection
1330 OUString("=COS($A2:$A6)"), // Corner case with intersection
1335 OUString("=COS($A2:$A100)"), // Typical 1D case
1340 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1341 ScAddress(0, 0, 1), // Formula in sheet 1
1345 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1346 ScAddress(0, 2, 1), // Formula in sheet 1
1350 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1351 ScAddress(2, 0, 1), // Formula in sheet 1
1355 OUString("=COS($Formula.$A1:$C3)"), // 2D corner case
1356 ScAddress(2, 2, 1), // Formula in sheet 1
1360 OUString("=COS($Formula.$A1:$C3)"), // Typical 2D case
1361 ScAddress(1, 1, 1), // Formula in sheet 1
1366 for (const auto& rCase
: aCasesWithChange
)
1368 m_pDoc
->SetString(rCase
.aCellAddress
, rCase
.aFormula
);
1370 const ScFormulaCell
* pCell
= m_pDoc
->GetFormulaCell(rCase
.aCellAddress
);
1371 const ScTokenArray
* pCode
= pCell
->GetCode();
1372 CPPUNIT_ASSERT(pCode
);
1374 sal_uInt16 nRPNLen
= pCode
->GetCodeLen();
1375 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong RPN token count.", static_cast<sal_uInt16
>(2), nRPNLen
);
1377 FormulaToken
** ppRPNTokens
= pCode
->GetCode();
1379 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong type of RPN token(argument to COS)", svSingleRef
, ppRPNTokens
[0]->GetType());
1381 ScSingleRefData aArgAddrRPN
= *ppRPNTokens
[0]->GetSingleRef();
1382 ScAddress aArgAddrActual
= aArgAddrRPN
.toAbs(rCase
.aCellAddress
);
1383 CPPUNIT_ASSERT_EQUAL_MESSAGE("Computed implicit intersection singleref is wrong", rCase
.aArgAddr
, aArgAddrActual
);
1388 void Test::testFormulaCompilerImplicitIntersection1NoGroup()
1390 m_pDoc
->InsertTab(0, "Formula");
1391 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1393 m_pDoc
->SetString(ScAddress(1,2,0), "=COS(A1:A5)"); // B3
1394 m_pDoc
->SetString(ScAddress(1,3,0), "=COS(A1:A5)"); // B4
1396 // Implicit intersection optimization in ScCompiler::HandleIIOpCode() internally changes
1397 // these to "=COS(A3)" and "=COS(A4)", but these shouldn't be merged into a formula group,
1398 // otherwise B4's formula would then be "=COS(A2:A6)".
1399 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "COS(A1:A5)", "Formula in B3 has changed.");
1400 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,3,0), "COS(A1:A5)", "Formula in B4 has changed.");
1402 m_pDoc
->DeleteTab(0);
1405 void Test::testFormulaCompilerImplicitIntersectionOperators()
1409 OUString
const formula
[3];
1410 double const result
[3];
1413 m_pDoc
->InsertTab(0, "Test");
1414 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1416 m_pDoc
->SetValue(2, 0, 0, 5); // C1
1417 m_pDoc
->SetValue(2, 1, 0, 4); // C2
1418 m_pDoc
->SetValue(2, 2, 0, 3); // C3
1419 m_pDoc
->SetValue(3, 0, 0, 1); // D1
1420 m_pDoc
->SetValue(3, 1, 0, 2); // D2
1421 m_pDoc
->SetValue(3, 2, 0, 3); // D3
1425 { OUString("=C:C/D:D"), OUString("=C:C/D:D"), OUString("=C:C/D:D"), 5, 2, 1 },
1426 { OUString("=C1:C2/D1:D2"), OUString("=C2:C3/D2:D3"), OUString("=C3:C4/D3:D4"), 5, 2, 1 }
1429 for (const TestCase
& test
: tests
)
1431 for(int i
= 0; i
< 2; ++i
)
1432 m_pDoc
->SetString(ScAddress(4,i
,0), test
.formula
[i
]); // E1-3
1433 for(int i
= 0; i
< 2; ++i
)
1434 CPPUNIT_ASSERT_EQUAL_MESSAGE(OUString( test
.formula
[i
] + " result incorrect in row " + OUString::number(i
+1)).toUtf8().getStr(),
1435 test
.result
[i
], m_pDoc
->GetValue(ScAddress(4,i
,0)));
1438 m_pDoc
->DeleteTab(0);
1441 void Test::testFormulaRefUpdate()
1443 m_pDoc
->InsertTab(0, "Formula");
1445 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1447 m_pDoc
->SetValue(ScAddress(0,0,0), 2.0); // A1
1448 m_pDoc
->SetString(ScAddress(2,2,0), "=A1"); // C3
1449 m_pDoc
->SetString(ScAddress(2,3,0), "=$A$1"); // C4
1451 ScAddress
aPos(2,2,0);
1452 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in C3.");
1454 aPos
= ScAddress(2,3,0);
1455 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in C4.");
1457 // Delete row 2 to push formula cells up (to C2:C3).
1458 m_pDoc
->DeleteRow(ScRange(0,1,0,m_pDoc
->MaxCol(),1,0));
1460 aPos
= ScAddress(2,1,0);
1461 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in C2.");
1463 aPos
= ScAddress(2,2,0);
1464 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in C3.");
1466 // Insert one row at row 2 to move them back.
1467 m_pDoc
->InsertRow(ScRange(0,1,0,m_pDoc
->MaxCol(),1,0));
1469 aPos
= ScAddress(2,2,0);
1470 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in C3.");
1472 aPos
= ScAddress(2,3,0);
1473 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in C4.");
1475 // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
1476 m_pDoc
->InsertRow(ScRange(0,0,0,m_pDoc
->MaxCol(),1,0));
1478 aPos
= ScAddress(2,4,0);
1479 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A3", "Wrong formula in C5.");
1481 aPos
= ScAddress(2,5,0);
1482 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$3", "Wrong formula in C6.");
1484 // Delete 2 rows at row 1 to shift them back.
1485 m_pDoc
->DeleteRow(ScRange(0,0,0,m_pDoc
->MaxCol(),1,0));
1487 aPos
= ScAddress(2,2,0);
1488 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in C3.");
1490 aPos
= ScAddress(2,3,0);
1491 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in C4.");
1493 // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
1494 m_pDoc
->InsertCol(ScRange(1,0,0,3,m_pDoc
->MaxRow(),0));
1496 aPos
= ScAddress(5,2,0);
1497 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in F3.");
1499 aPos
= ScAddress(5,3,0);
1500 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in F4.");
1502 // Delete columns B:D to shift them back.
1503 m_pDoc
->DeleteCol(ScRange(1,0,0,3,m_pDoc
->MaxRow(),0));
1505 aPos
= ScAddress(2,2,0);
1506 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in C3.");
1508 aPos
= ScAddress(2,3,0);
1509 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in C4.");
1511 // Insert cells over A1:A3 to only shift A1 down to A4.
1512 m_pDoc
->InsertRow(ScRange(0,0,0,0,2,0));
1514 aPos
= ScAddress(2,2,0);
1515 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A4", "Wrong formula in C3.");
1517 aPos
= ScAddress(2,3,0);
1518 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$4", "Wrong formula in C4.");
1521 m_pDoc
->DeleteRow(ScRange(0,0,0,0,2,0));
1523 aPos
= ScAddress(2,2,0);
1524 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "A1", "Wrong formula in C3.");
1526 aPos
= ScAddress(2,3,0);
1527 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$A$1", "Wrong formula in C4.");
1529 // Delete row 1 which will delete the value cell (A1).
1530 m_pDoc
->DeleteRow(ScRange(0,0,0,m_pDoc
->MaxCol(),0,0));
1532 aPos
= ScAddress(2,1,0);
1533 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(aPos
);
1534 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC
);
1535 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef
), static_cast<int>(pFC
->GetErrCode()));
1536 aPos
= ScAddress(2,2,0);
1537 pFC
= m_pDoc
->GetFormulaCell(aPos
);
1538 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC
);
1539 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef
), static_cast<int>(pFC
->GetErrCode()));
1541 // Clear all and start over.
1542 clearRange(m_pDoc
, ScRange(0,0,0,10,10,0));
1544 // Test range updates
1546 // Fill B2:C3 with values.
1547 m_pDoc
->SetValue(ScAddress(1,1,0), 1);
1548 m_pDoc
->SetValue(ScAddress(1,2,0), 2);
1549 m_pDoc
->SetValue(ScAddress(2,1,0), 3);
1550 m_pDoc
->SetValue(ScAddress(2,2,0), 4);
1552 m_pDoc
->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
1553 m_pDoc
->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
1555 aPos
= ScAddress(0,5,0);
1556 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B2:C3)", "Wrong formula in A6.");
1558 aPos
= ScAddress(0,6,0);
1559 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1561 // Insert a row at row 1.
1562 m_pDoc
->InsertRow(ScRange(0,0,0,m_pDoc
->MaxCol(),0,0));
1564 aPos
= ScAddress(0,6,0);
1565 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B3:C4)", "Wrong formula in A7.");
1567 aPos
= ScAddress(0,7,0);
1568 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($B$3:$C$4)", "Wrong formula in A8.");
1571 m_pDoc
->DeleteRow(ScRange(0,0,0,m_pDoc
->MaxCol(),0,0));
1573 aPos
= ScAddress(0,5,0);
1574 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B2:C3)", "Wrong formula in A6.");
1576 aPos
= ScAddress(0,6,0);
1577 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1579 // Insert columns B:C to shift only the value range.
1580 m_pDoc
->InsertCol(ScRange(1,0,0,2,m_pDoc
->MaxRow(),0));
1582 aPos
= ScAddress(0,5,0);
1583 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(D2:E3)", "Wrong formula in A6.");
1585 aPos
= ScAddress(0,6,0);
1586 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($D$2:$E$3)", "Wrong formula in A7.");
1589 m_pDoc
->DeleteCol(ScRange(1,0,0,2,m_pDoc
->MaxRow(),0));
1591 aPos
= ScAddress(0,5,0);
1592 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B2:C3)", "Wrong formula in A6.");
1594 aPos
= ScAddress(0,6,0);
1595 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1597 // Insert rows 5:6 to shift the formula cells only.
1598 m_pDoc
->InsertRow(ScRange(0,4,0,m_pDoc
->MaxCol(),5,0));
1600 aPos
= ScAddress(0,7,0);
1601 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B2:C3)", "Wrong formula in A8.");
1603 aPos
= ScAddress(0,8,0);
1604 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($B$2:$C$3)", "Wrong formula in A9.");
1607 m_pDoc
->DeleteRow(ScRange(0,4,0,m_pDoc
->MaxCol(),5,0));
1609 aPos
= ScAddress(0,5,0);
1610 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B2:C3)", "Wrong formula in A6.");
1612 aPos
= ScAddress(0,6,0);
1613 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM($B$2:$C$3)", "Wrong formula in A7.");
1615 // Check the values of the formula cells in A6:A7.
1616 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1617 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
1619 // Insert cells over B1:B2 to partially shift value range.
1620 m_pDoc
->InsertRow(ScRange(1,0,0,1,1,0));
1622 // Check the values of the formula cells in A6:A7 again.
1623 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1624 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
1626 // ... and shift them back.
1627 m_pDoc
->DeleteRow(ScRange(1,0,0,1,1,0));
1629 // The formula cell results should be back too.
1630 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1631 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
1633 // Delete rows 2:3 to completely remove the referenced range.
1634 m_pDoc
->DeleteRow(ScRange(0,1,0,m_pDoc
->MaxCol(),2,0));
1636 // Both A4 and A5 should show #REF! errors.
1637 pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,3,0));
1638 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC
);
1639 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef
), static_cast<int>(pFC
->GetErrCode()));
1641 pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,4,0));
1642 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC
);
1643 CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef
), static_cast<int>(pFC
->GetErrCode()));
1645 m_pDoc
->DeleteTab(0);
1648 void Test::testFormulaRefUpdateRange()
1650 m_pDoc
->InsertTab(0, "Formula");
1652 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1654 setExpandRefs(false);
1656 // Set values to B2:C5.
1657 m_pDoc
->SetValue(ScAddress(1,1,0), 1);
1658 m_pDoc
->SetValue(ScAddress(1,2,0), 2);
1659 m_pDoc
->SetValue(ScAddress(1,3,0), 3);
1660 m_pDoc
->SetValue(ScAddress(1,4,0), 4);
1661 m_pDoc
->SetValue(ScAddress(2,1,0), 5);
1662 m_pDoc
->SetValue(ScAddress(2,2,0), 6);
1663 m_pDoc
->SetValue(ScAddress(2,3,0), 7);
1664 m_pDoc
->SetValue(ScAddress(2,4,0), 8);
1666 // Set formula cells to A7 and A8.
1667 m_pDoc
->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
1668 m_pDoc
->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
1670 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "SUM(B2:C5)", "Wrong formula in A7.");
1672 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "SUM($B$2:$C$5)", "Wrong formula in A8.");
1674 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
1675 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc
->GetValue(ScAddress(0,7,0)));
1677 // Delete row 3. This should shrink the range references by one row.
1678 m_pDoc
->DeleteRow(ScRange(0,2,0,m_pDoc
->MaxCol(),2,0));
1680 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM(B2:C4)", "Wrong formula in A6.");
1682 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "SUM($B$2:$C$4)", "Wrong formula in A7.");
1684 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1685 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
1687 // Delete row 4 - bottom of range
1688 m_pDoc
->DeleteRow(ScRange(0,3,0,m_pDoc
->MaxCol(),3,0));
1690 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM(B2:C3)", "Wrong formula in A5.");
1692 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM($B$2:$C$3)", "Wrong formula in A6.");
1694 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1695 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1697 // Delete row 2 - top of range
1698 m_pDoc
->DeleteRow(ScRange(0,1,0,m_pDoc
->MaxCol(),1,0));
1700 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,3,0), "SUM(B2:C2)", "Wrong formula in A4.");
1702 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM($B$2:$C$2)", "Wrong formula in A5.");
1704 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1705 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1707 // Clear the range and start over.
1708 clearRange(m_pDoc
, ScRange(0,0,0,20,20,0));
1710 // Fill C2:F3 with values.
1711 m_pDoc
->SetValue(ScAddress(2,1,0), 1);
1712 m_pDoc
->SetValue(ScAddress(3,1,0), 2);
1713 m_pDoc
->SetValue(ScAddress(4,1,0), 3);
1714 m_pDoc
->SetValue(ScAddress(5,1,0), 4);
1715 m_pDoc
->SetValue(ScAddress(2,2,0), 5);
1716 m_pDoc
->SetValue(ScAddress(3,2,0), 6);
1717 m_pDoc
->SetValue(ScAddress(4,2,0), 7);
1718 m_pDoc
->SetValue(ScAddress(5,2,0), 8);
1720 // Set formulas to A2 and A3.
1721 m_pDoc
->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
1722 m_pDoc
->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
1724 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C2:F3)", "Wrong formula in A2.");
1726 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$2:$F$3)", "Wrong formula in A3.");
1728 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1729 CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1732 m_pDoc
->DeleteCol(ScRange(3,0,0,3,m_pDoc
->MaxRow(),0));
1734 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C2:E3)", "Wrong formula in A2.");
1736 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$2:$E$3)", "Wrong formula in A3.");
1738 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1739 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1741 // Delete column E - the right edge of reference range.
1742 m_pDoc
->DeleteCol(ScRange(4,0,0,4,m_pDoc
->MaxRow(),0));
1744 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C2:D3)", "Wrong formula in A2.");
1746 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$2:$D$3)", "Wrong formula in A3.");
1748 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1749 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1751 // Delete column C - the left edge of reference range.
1752 m_pDoc
->DeleteCol(ScRange(2,0,0,2,m_pDoc
->MaxRow(),0));
1754 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C2:C3)", "Wrong formula in A2.");
1756 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$2:$C$3)", "Wrong formula in A3.");
1758 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1759 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1761 // Clear the range and start over.
1762 clearRange(m_pDoc
, ScRange(0,0,0,20,20,0));
1764 // Disable expansion of range reference on insertion in adjacent areas.
1765 setExpandRefs(false);
1767 // Fill C2:D3 with values.
1768 m_pDoc
->SetValue(ScAddress(2,1,0), 1);
1769 m_pDoc
->SetValue(ScAddress(3,1,0), 2);
1770 m_pDoc
->SetValue(ScAddress(2,2,0), 3);
1771 m_pDoc
->SetValue(ScAddress(3,2,0), 4);
1773 // Set formulas at A5 and A6.
1774 m_pDoc
->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
1775 m_pDoc
->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
1777 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
1779 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
1781 // Insert a column at column C. This should simply shift the reference without expansion.
1782 m_pDoc
->InsertCol(ScRange(2,0,0,2,m_pDoc
->MaxRow(),0));
1784 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM(D2:E3)", "Wrong formula in A5.");
1786 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM($D$2:$E$3)", "Wrong formula in A6.");
1789 m_pDoc
->DeleteCol(ScRange(2,0,0,2,m_pDoc
->MaxRow(),0));
1791 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM(C2:D3)", "Wrong formula in A5.");
1793 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM($C$2:$D$3)", "Wrong formula in A6.");
1795 // Insert at column D. This should expand the reference by one column length.
1796 m_pDoc
->InsertCol(ScRange(3,0,0,3,m_pDoc
->MaxRow(),0));
1798 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
1800 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
1802 // Insert at column F. No expansion should occur since the edge expansion is turned off.
1803 m_pDoc
->InsertCol(ScRange(5,0,0,5,m_pDoc
->MaxRow(),0));
1805 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,4,0), "SUM(C2:E3)", "Wrong formula in A5.");
1807 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM($C$2:$E$3)", "Wrong formula in A6.");
1809 // Insert at row 2. No expansion should occur with edge expansion turned off.
1810 m_pDoc
->InsertRow(ScRange(0,1,0,m_pDoc
->MaxCol(),1,0));
1812 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "SUM(C3:E4)", "Wrong formula in A6.");
1814 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "SUM($C$3:$E$4)", "Wrong formula in A7.");
1816 // Insert at row 4 to expand the reference range.
1817 m_pDoc
->InsertRow(ScRange(0,3,0,m_pDoc
->MaxCol(),3,0));
1819 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "SUM(C3:E5)", "Wrong formula in A7.");
1821 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "SUM($C$3:$E$5)", "Wrong formula in A8.");
1823 // Insert at row 6. No expansion with edge expansion turned off.
1824 m_pDoc
->InsertRow(ScRange(0,5,0,m_pDoc
->MaxCol(),5,0));
1826 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "SUM(C3:E5)", "Wrong formula in A8.");
1828 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,8,0), "SUM($C$3:$E$5)", "Wrong formula in A9.");
1830 // Clear the range and start over.
1831 clearRange(m_pDoc
, ScRange(0,0,0,20,20,0));
1833 // Turn edge expansion on.
1834 setExpandRefs(true);
1836 // Fill C6:D7 with values.
1837 m_pDoc
->SetValue(ScAddress(2,5,0), 1);
1838 m_pDoc
->SetValue(ScAddress(2,6,0), 2);
1839 m_pDoc
->SetValue(ScAddress(3,5,0), 3);
1840 m_pDoc
->SetValue(ScAddress(3,6,0), 4);
1842 // Set formulas at A2 and A3.
1843 m_pDoc
->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
1844 m_pDoc
->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
1846 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C6:D7)", "Wrong formula in A2.");
1848 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$6:$D$7)", "Wrong formula in A3.");
1850 // Insert at column E. This should expand the reference range by one column.
1851 m_pDoc
->InsertCol(ScRange(4,0,0,4,m_pDoc
->MaxRow(),0));
1853 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C6:E7)", "Wrong formula in A2.");
1855 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$6:$E$7)", "Wrong formula in A3.");
1857 // Insert at column C to edge-expand the reference range.
1858 m_pDoc
->InsertCol(ScRange(2,0,0,2,m_pDoc
->MaxRow(),0));
1860 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C6:F7)", "Wrong formula in A2.");
1862 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$6:$F$7)", "Wrong formula in A3.");
1864 // Insert at row 8 to edge-expand.
1865 m_pDoc
->InsertRow(ScRange(0,7,0,m_pDoc
->MaxCol(),7,0));
1867 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C6:F8)", "Wrong formula in A2.");
1869 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$6:$F$8)", "Wrong formula in A3.");
1871 // Insert at row 6 to edge-expand.
1872 m_pDoc
->InsertRow(ScRange(0,5,0,m_pDoc
->MaxCol(),5,0));
1874 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "SUM(C6:F9)", "Wrong formula in A2.");
1876 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,2,0), "SUM($C$6:$F$9)", "Wrong formula in A3.");
1878 m_pDoc
->InsertTab(1, "StickyRange");
1880 // A3:A18 all possible combinations of relative and absolute addressing,
1881 // leaving one row above and below unreferenced.
1882 ScAddress
aPos(0,2,1);
1883 m_pDoc
->SetString( aPos
, "=B2:B1048575");
1885 m_pDoc
->SetString( aPos
, "=B2:B$1048575");
1887 m_pDoc
->SetString( aPos
, "=B2:$B1048575");
1889 m_pDoc
->SetString( aPos
, "=B2:$B$1048575");
1891 m_pDoc
->SetString( aPos
, "=B$2:B1048575");
1893 m_pDoc
->SetString( aPos
, "=B$2:B$1048575");
1895 m_pDoc
->SetString( aPos
, "=B$2:$B1048575");
1897 m_pDoc
->SetString( aPos
, "=B$2:$B$1048575");
1899 m_pDoc
->SetString( aPos
, "=$B2:B1048575");
1901 m_pDoc
->SetString( aPos
, "=$B2:B$1048575");
1903 m_pDoc
->SetString( aPos
, "=$B2:$B1048575");
1905 m_pDoc
->SetString( aPos
, "=$B2:$B$1048575");
1907 m_pDoc
->SetString( aPos
, "=$B$2:B1048575");
1909 m_pDoc
->SetString( aPos
, "=$B$2:B$1048575");
1911 m_pDoc
->SetString( aPos
, "=$B$2:$B1048575");
1913 m_pDoc
->SetString( aPos
, "=$B$2:$B$1048575");
1915 // A19 reference to two cells on one row.
1916 m_pDoc
->SetString( aPos
, "=B1048575:C1048575");
1919 // Insert 2 rows in the middle to shift bottom reference down and make it
1921 m_pDoc
->InsertRow( ScRange( 0, aPos
.Row(), 1, m_pDoc
->MaxCol(), aPos
.Row()+1, 1));
1923 // A3:A18 must not result in #REF! anywhere.
1925 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B2:B1048576", "Wrong reference in A3 after insertion.");
1927 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B2:B$1048576", "Wrong reference in A4 after insertion.");
1929 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B2:$B1048576", "Wrong reference in A5 after insertion.");
1931 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B2:$B$1048576", "Wrong reference in A6 after insertion.");
1933 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$2:B1048576", "Wrong reference in A7 after insertion.");
1935 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$2:B$1048576", "Wrong reference in A8 after insertion.");
1937 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$2:$B1048576", "Wrong reference in A9 after insertion.");
1939 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$2:$B$1048576", "Wrong reference in A10 after insertion.");
1941 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B2:B1048576", "Wrong reference in A11 after insertion.");
1943 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B2:B$1048576", "Wrong reference in A12 after insertion.");
1945 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B2:$B1048576", "Wrong reference in A13 after insertion.");
1947 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B2:$B$1048576", "Wrong reference in A14 after insertion.");
1949 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$2:B1048576", "Wrong reference in A15 after insertion.");
1951 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$2:B$1048576", "Wrong reference in A16 after insertion.");
1953 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$2:$B1048576", "Wrong reference in A17 after insertion.");
1955 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$2:$B$1048576", "Wrong reference in A18 after insertion.");
1958 // A19 reference to one row shifted out should be #REF!
1959 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B#REF!:C#REF!", "Wrong reference in A19 after insertion.");
1960 // A19 enter reference to last row.
1961 m_pDoc
->SetString( aPos
, "=B1048576:C1048576");
1964 // Delete row 1 to shift top reference up, bottom reference stays sticky.
1965 m_pDoc
->DeleteRow(ScRange(0,0,1,m_pDoc
->MaxCol(),0,1));
1967 // Check sticky bottom references and display of entire column references,
1970 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:B", "Wrong reference in A2 after deletion.");
1972 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B1:B$1048576", "Wrong reference in A3 after deletion.");
1974 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:$B", "Wrong reference in A4 after deletion.");
1976 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
1978 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$1:B1048576", "Wrong reference in A6 after deletion.");
1980 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:B", "Wrong reference in A7 after deletion.");
1982 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
1984 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:$B", "Wrong reference in A9 after deletion.");
1986 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:B", "Wrong reference in A10 after deletion.");
1988 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
1990 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:$B", "Wrong reference in A12 after deletion.");
1992 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
1994 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
1996 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:B", "Wrong reference in A15 after deletion.");
1998 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2000 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:$B", "Wrong reference in A17 after deletion.");
2003 // A18 reference to one last row should be shifted up.
2004 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B1048575:C1048575", "Wrong reference in A18 after deletion.");
2007 // Insert 4 rows in the middle.
2008 m_pDoc
->InsertRow( ScRange( 0, aPos
.Row(), 1, m_pDoc
->MaxCol(), aPos
.Row()+3, 1));
2009 // Delete 2 rows in the middle.
2010 m_pDoc
->DeleteRow( ScRange( 0, aPos
.Row(), 1, m_pDoc
->MaxCol(), aPos
.Row()+1, 1));
2012 // References in A2:A17 must still be the same.
2014 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:B", "Wrong reference in A2 after deletion.");
2016 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B1:B$1048576", "Wrong reference in A3 after deletion.");
2018 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:$B", "Wrong reference in A4 after deletion.");
2020 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B1:$B$1048576", "Wrong reference in A5 after deletion.");
2022 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$1:B1048576", "Wrong reference in A6 after deletion.");
2024 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:B", "Wrong reference in A7 after deletion.");
2026 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B$1:$B1048576", "Wrong reference in A8 after deletion.");
2028 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "B:$B", "Wrong reference in A9 after deletion.");
2030 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:B", "Wrong reference in A10 after deletion.");
2032 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B1:B$1048576", "Wrong reference in A11 after deletion.");
2034 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:$B", "Wrong reference in A12 after deletion.");
2036 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B1:$B$1048576", "Wrong reference in A13 after deletion.");
2038 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$1:B1048576", "Wrong reference in A14 after deletion.");
2040 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:B", "Wrong reference in A15 after deletion.");
2042 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B$1:$B1048576", "Wrong reference in A16 after deletion.");
2044 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "$B:$B", "Wrong reference in A17 after deletion.");
2047 // Enter values in B1 and B1048576 (last row).
2048 m_pDoc
->SetValue( 1,0,1, 1.0);
2049 m_pDoc
->SetValue( 1,m_pDoc
->MaxRow(),1, 2.0);
2050 // Sticky reference including last row.
2051 m_pDoc
->SetString( 2,0,1, "=SUM(B:B)");
2052 // Reference to last row.
2053 CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxRow() changed, adapt unit test.", 1048575, int(m_pDoc
->MaxRow()));
2054 m_pDoc
->SetString( 2,1,1, "=SUM(B1048576:C1048576)");
2055 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 3.0, m_pDoc
->GetValue(2,0,1));
2056 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C2.", 2.0, m_pDoc
->GetValue(2,1,1));
2058 m_pDoc
->DeleteRow( ScRange( 0, m_pDoc
->MaxRow(), 1, m_pDoc
->MaxCol(), m_pDoc
->MaxRow(), 1));
2059 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C1.", 1.0, m_pDoc
->GetValue(2,0,1));
2060 CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C2 not invalidated.", OUString("#REF!"), m_pDoc
->GetString(2,1,1));
2062 // Enter values in A23 and AMJ23 (last column).
2063 m_pDoc
->SetValue( 0,22,1, 1.0);
2064 m_pDoc
->SetValue( m_pDoc
->MaxCol(),22,1, 2.0);
2065 // C3 with sticky reference including last column.
2066 m_pDoc
->SetString( 2,2,1, "=SUM(23:23)");
2067 // C4 with reference to last column.
2068 CPPUNIT_ASSERT_EQUAL_MESSAGE("m_pDoc->MaxCol() changed, adapt unit test.", 1023, int(m_pDoc
->MaxCol()));
2069 m_pDoc
->SetString( 2,3,1, "=SUM(AMJ22:AMJ23)");
2070 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 3.0, m_pDoc
->GetValue(2,2,1));
2071 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C4.", 2.0, m_pDoc
->GetValue(2,3,1));
2072 // Delete last column.
2073 m_pDoc
->DeleteCol( ScRange( m_pDoc
->MaxCol(), 0, 1, m_pDoc
->MaxCol(), m_pDoc
->MaxRow(), 1));
2074 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong result in C3.", 1.0, m_pDoc
->GetValue(2,2,1));
2075 CPPUNIT_ASSERT_EQUAL_MESSAGE("Reference in C4 not invalidated.", OUString("#REF!"), m_pDoc
->GetString(2,3,1));
2077 m_pDoc
->DeleteTab(1);
2079 m_pDoc
->DeleteTab(0);
2082 void Test::testFormulaRefUpdateSheets()
2084 m_pDoc
->InsertTab(0, "Sheet1");
2085 m_pDoc
->InsertTab(1, "Sheet2");
2088 m_pDoc
->GetName(0, aName
);
2089 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName
);
2090 m_pDoc
->GetName(1, aName
);
2091 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2093 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2095 // Set values to B2:C3 on sheet Sheet1.
2096 m_pDoc
->SetValue(ScAddress(1,1,0), 1);
2097 m_pDoc
->SetValue(ScAddress(1,2,0), 2);
2098 m_pDoc
->SetValue(ScAddress(2,1,0), 3);
2099 m_pDoc
->SetValue(ScAddress(2,2,0), 4);
2101 // Set formulas to B2 and B3 on sheet Sheet2.
2102 m_pDoc
->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
2103 m_pDoc
->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
2105 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2107 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2110 m_pDoc
->MoveTab(0, 1);
2111 m_pDoc
->GetName(0, aName
);
2112 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2113 m_pDoc
->GetName(1, aName
);
2114 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName
);
2116 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2118 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2121 m_pDoc
->MoveTab(0, 1);
2122 m_pDoc
->GetName(0, aName
);
2123 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName
);
2124 m_pDoc
->GetName(1, aName
);
2125 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2127 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2129 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2131 // Insert a new sheet between the two.
2132 m_pDoc
->InsertTab(1, "Temp");
2134 m_pDoc
->GetName(1, aName
);
2135 CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName
);
2136 m_pDoc
->GetName(2, aName
);
2137 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2139 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2141 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2143 // Move the last sheet (Sheet2) to the first position.
2144 m_pDoc
->MoveTab(2, 0);
2146 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2148 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2151 m_pDoc
->MoveTab(0, 2);
2153 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2155 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2157 // Move the "Temp" sheet to the last position.
2158 m_pDoc
->MoveTab(1, 2);
2160 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2162 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2165 m_pDoc
->MoveTab(2, 1);
2167 // Delete the temporary sheet.
2168 m_pDoc
->DeleteTab(1);
2170 m_pDoc
->GetName(1, aName
);
2171 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2173 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2175 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2177 // Insert a new sheet before the first one.
2178 m_pDoc
->InsertTab(0, "Temp");
2180 m_pDoc
->GetName(1, aName
);
2181 CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName
);
2182 m_pDoc
->GetName(2, aName
);
2183 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2185 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2187 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2189 // Delete the temporary sheet.
2190 m_pDoc
->DeleteTab(0);
2192 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2194 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2196 // Append a bunch of sheets.
2197 m_pDoc
->InsertTab(2, "Temp1");
2198 m_pDoc
->InsertTab(3, "Temp2");
2199 m_pDoc
->InsertTab(4, "Temp3");
2201 // Move these tabs around. This shouldn't affects the first 2 sheets.
2202 m_pDoc
->MoveTab(2, 4);
2203 m_pDoc
->MoveTab(3, 2);
2205 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)", "Wrong formula in Sheet2.B2.");
2207 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2209 // Delete the temp sheets.
2210 m_pDoc
->DeleteTab(4);
2211 m_pDoc
->DeleteTab(3);
2212 m_pDoc
->DeleteTab(2);
2215 m_pDoc
->DeleteTab(0);
2216 m_pDoc
->GetName(0, aName
);
2217 CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName
);
2219 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "SUM(#REF!.B2:C3)", "Wrong formula in Sheet2.B2.");
2221 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)", "Wrong formula in Sheet2.B3.");
2223 m_pDoc
->DeleteTab(0);
2226 void Test::testFormulaRefUpdateInsertRows()
2228 setExpandRefs(false);
2230 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2231 m_pDoc
->InsertTab(0, "Formula");
2233 // Insert raw values in B2:B4.
2234 m_pDoc
->SetValue(ScAddress(1,1,0), 1.0);
2235 m_pDoc
->SetValue(ScAddress(1,2,0), 2.0);
2236 m_pDoc
->SetValue(ScAddress(1,3,0), 3.0);
2238 // Insert a formula in B5 to sum up B2:B4.
2239 m_pDoc
->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
2241 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
2243 // Insert rows over rows 1:2.
2244 ScMarkData
aMark(MAXROW
, MAXCOL
);
2245 aMark
.SelectOneTable(0);
2246 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2247 rFunc
.InsertCells(ScRange(0,0,0,m_pDoc
->MaxCol(),1,0), &aMark
, INS_INSROWS_BEFORE
, false, true);
2249 // The raw data should have shifted to B4:B6.
2250 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
2251 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
2252 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(1,5,0)));
2254 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,6,0), "SUM(B4:B6)", "Wrong formula!");
2256 // Clear and start over.
2257 clearSheet(m_pDoc
, 0);
2259 // Set raw values in A4:A6.
2260 m_pDoc
->SetValue(ScAddress(0,3,0), 1.0);
2261 m_pDoc
->SetValue(ScAddress(0,4,0), 2.0);
2262 m_pDoc
->SetValue(ScAddress(0,5,0), 3.0);
2264 // Set formula in A3 to reference A4:A6.
2265 m_pDoc
->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
2267 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
2269 // Insert 3 rows over 2:4. This should push A3:A6 to A6:A9.
2270 rFunc
.InsertCells(ScRange(0,1,0,m_pDoc
->MaxCol(),3,0), &aMark
, INS_INSROWS_BEFORE
, false, true);
2271 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,5,0));
2272 CPPUNIT_ASSERT(pFC
);
2273 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula cell should not be an error.", 0, static_cast<int>(pFC
->GetErrCode()));
2274 ASSERT_DOUBLES_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
2276 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "MAX(A7:A9)", "Wrong formula!");
2278 m_pDoc
->DeleteTab(0);
2281 void Test::testFormulaRefUpdateSheetsDelete()
2283 m_pDoc
->InsertTab(0, "Sheet1");
2284 m_pDoc
->InsertTab(1, "Sheet2");
2285 m_pDoc
->InsertTab(2, "Sheet3");
2286 m_pDoc
->InsertTab(3, "Sheet4");
2288 m_pDoc
->SetString(ScAddress(4,1,0), "=SUM(Sheet2.A4:Sheet4.A4)");
2289 m_pDoc
->SetString(ScAddress(4,2,0), "=SUM($Sheet2.A4:$Sheet4.A4)");
2290 m_pDoc
->DeleteTab(1);
2292 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,1,0), "SUM(Sheet3.A4:Sheet4.A4)", "Wrong Formula");
2293 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,2,0), "SUM($Sheet3.A4:$Sheet4.A4)", "Wrong Formula");
2295 m_pDoc
->InsertTab(1, "Sheet2");
2297 m_pDoc
->SetString(ScAddress(5,1,3), "=SUM(Sheet1.A5:Sheet3.A5)");
2298 m_pDoc
->SetString(ScAddress(5,2,3), "=SUM($Sheet1.A5:$Sheet3.A5)");
2299 m_pDoc
->DeleteTab(2);
2301 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(5,1,2), "SUM(Sheet1.A5:Sheet2.A5)", "Wrong Formula");
2302 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(5,2,2), "SUM($Sheet1.A5:$Sheet2.A5)", "Wrong Formula");
2304 m_pDoc
->InsertTab(2, "Sheet3");
2306 m_pDoc
->SetString(ScAddress(6,1,3), "=SUM(Sheet1.A6:Sheet3.A6)");
2307 m_pDoc
->SetString(ScAddress(6,2,3), "=SUM($Sheet1.A6:$Sheet3.A6)");
2308 m_pDoc
->DeleteTabs(0,3);
2310 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(6,1,0), "SUM(#REF!.A6:#REF!.A6)", "Wrong Formula");
2311 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(6,2,0), "SUM($#REF!.A6:$#REF!.A6)", "Wrong Formula");
2313 m_pDoc
->InsertTab(0, "Sheet1");
2314 m_pDoc
->InsertTab(1, "Sheet2");
2315 m_pDoc
->InsertTab(2, "Sheet3");
2317 m_pDoc
->SetString(ScAddress(1,1,1), "=SUM(Sheet1.A2:Sheet3.A2");
2318 m_pDoc
->SetString(ScAddress(2,1,1), "=SUM(Sheet1.A1:Sheet2.A1");
2319 m_pDoc
->SetString(ScAddress(3,1,1), "=SUM(Sheet2.A3:Sheet4.A3");
2321 m_pDoc
->SetString(ScAddress(1,2,1), "=SUM($Sheet1.A2:$Sheet3.A2");
2322 m_pDoc
->SetString(ScAddress(2,2,1), "=SUM($Sheet1.A1:$Sheet2.A1");
2323 m_pDoc
->SetString(ScAddress(3,2,1), "=SUM($Sheet2.A3:$Sheet4.A3");
2325 m_pDoc
->DeleteTab(2);
2327 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,1), "SUM(Sheet1.A2:Sheet2.A2)", "Wrong Formula");
2329 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,1), "SUM(Sheet1.A1:Sheet2.A1)", "Wrong Formula");
2331 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,1,1), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2333 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,1), "SUM($Sheet1.A2:$Sheet2.A2)", "Wrong Formula");
2335 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,2,1), "SUM($Sheet1.A1:$Sheet2.A1)", "Wrong Formula");
2337 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,2,1), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2339 m_pDoc
->DeleteTab(0);
2341 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "SUM(Sheet2.A2:Sheet2.A2)", "Wrong Formula");
2343 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "SUM(Sheet2.A1:Sheet2.A1)", "Wrong Formula");
2345 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,1,0), "SUM(Sheet2.A3:Sheet4.A3)", "Wrong Formula");
2347 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "SUM($Sheet2.A2:$Sheet2.A2)", "Wrong Formula");
2349 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,2,0), "SUM($Sheet2.A1:$Sheet2.A1)", "Wrong Formula");
2351 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,2,0), "SUM($Sheet2.A3:$Sheet4.A3)", "Wrong Formula");
2353 m_pDoc
->DeleteTab(0);
2354 m_pDoc
->DeleteTab(0);
2357 void Test::testFormulaRefUpdateInsertColumns()
2359 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2360 setExpandRefs(false);
2362 m_pDoc
->InsertTab(0, "Formula");
2364 // Set named range for B2 with absolute column and relative same row.
2365 const ScAddress
aNamePos(0,1,0);
2366 bool bInserted
= m_pDoc
->InsertNewRangeName("RowRelativeRange", aNamePos
, "$Formula.$B2");
2367 CPPUNIT_ASSERT(bInserted
);
2369 // Set named range for entire absolute column B.
2370 bInserted
= m_pDoc
->InsertNewRangeName("EntireColumn", aNamePos
, "$B:$B");
2371 CPPUNIT_ASSERT(bInserted
);
2373 // Set named range for entire absolute row 2.
2374 bInserted
= m_pDoc
->InsertNewRangeName("EntireRow", aNamePos
, "$2:$2");
2375 CPPUNIT_ASSERT(bInserted
);
2377 // Set values in B1:B3.
2378 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0);
2379 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0);
2380 m_pDoc
->SetValue(ScAddress(1,2,0), 3.0);
2382 // Reference them in B4.
2383 m_pDoc
->SetString(ScAddress(1,3,0), "=SUM(B1:B3)");
2384 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
2386 // Use named range in C2 to reference B2.
2387 m_pDoc
->SetString(ScAddress(2,1,0), "=RowRelativeRange");
2388 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
2390 // Use named range in C3 to reference column B, values in B1,B2,B3,B4
2391 m_pDoc
->SetString(ScAddress(2,2,0), "=SUM(EntireColumn)");
2392 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(2,2,0)));
2394 // Use named range in C4 to reference row 2, values in B2 and C2.
2395 m_pDoc
->SetString(ScAddress(2,3,0), "=SUM(EntireRow)");
2396 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(2,3,0)));
2398 // Insert columns over A:B.
2399 ScMarkData
aMark(MAXROW
, MAXCOL
);
2400 aMark
.SelectOneTable(0);
2401 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2402 rFunc
.InsertCells(ScRange(0,0,0,1,m_pDoc
->MaxRow(),0), &aMark
, INS_INSCOLS_BEFORE
, false, true);
2404 // Now, the original column B has moved to column D.
2405 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,3,0), "SUM(D1:D3)", "Wrong formula in D4 after column insertion.");
2407 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
2409 // Check that the named reference points to the moved cell, now D2.
2410 ScRangeData
* pName
= m_pDoc
->GetRangeName()->findByUpperName("ROWRELATIVERANGE");
2411 CPPUNIT_ASSERT(pName
);
2413 pName
->GetSymbol(aSymbol
, aNamePos
, formula::FormulaGrammar::GRAM_ENGLISH
);
2414 CPPUNIT_ASSERT_EQUAL(OUString("$Formula.$D2"), aSymbol
);
2416 // Check that the formula using the name, now in E2, still has the same result.
2417 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,1,0), "RowRelativeRange", "Wrong formula in E2 after column insertion.");
2419 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(4,1,0)));
2421 // Check that the named column reference points to the moved column, now D.
2422 pName
= m_pDoc
->GetRangeName()->findByUpperName("ENTIRECOLUMN");
2423 CPPUNIT_ASSERT(pName
);
2424 pName
->GetSymbol(aSymbol
, aNamePos
, formula::FormulaGrammar::GRAM_ENGLISH
);
2425 CPPUNIT_ASSERT_EQUAL(OUString("$D:$D"), aSymbol
);
2427 // Check that the formula using the name, now in E3, still has the same result.
2428 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,2,0), "SUM(EntireColumn)", "Wrong formula in E3 after column insertion.");
2430 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(4,2,0)));
2432 // Check that the named row reference still points to the same entire row
2433 // and does not have a #REF! error due to inserted columns.
2434 pName
= m_pDoc
->GetRangeName()->findByUpperName("ENTIREROW");
2435 CPPUNIT_ASSERT(pName
);
2436 pName
->GetSymbol(aSymbol
, aNamePos
, formula::FormulaGrammar::GRAM_ENGLISH
);
2437 CPPUNIT_ASSERT_EQUAL(OUString("$2:$2"), aSymbol
);
2439 // Check that the formula using the name, now in E4, still has the same result.
2440 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(4,3,0), "SUM(EntireRow)", "Wrong formula in E4 after column insertion.");
2442 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(4,3,0)));
2444 m_pDoc
->DeleteTab(0);
2447 void Test::testFormulaRefUpdateMove()
2449 m_pDoc
->InsertTab(0, "Sheet1");
2451 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2453 // Set value to B4:B6.
2454 m_pDoc
->SetValue(ScAddress(1,3,0), 1);
2455 m_pDoc
->SetValue(ScAddress(1,4,0), 2);
2456 m_pDoc
->SetValue(ScAddress(1,5,0), 3);
2458 // Set formulas to A9:A12 that references B4:B6.
2459 m_pDoc
->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
2460 m_pDoc
->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
2461 m_pDoc
->SetString(ScAddress(0,10,0), "=B5");
2462 m_pDoc
->SetString(ScAddress(0,11,0), "=$B$6");
2464 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(0,8,0));
2465 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(0,9,0));
2466 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(0,10,0));
2467 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(0,11,0));
2469 // Move B4:B6 to D4 (two columns to the right).
2470 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2471 bool bMoved
= rFunc
.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
2472 CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved
);
2474 // The results of the formula cells that reference the moved range should remain the same.
2475 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(0,8,0));
2476 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(0,9,0));
2477 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(0,10,0));
2478 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(0,11,0));
2480 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,8,0), "SUM(D4:D6)", "Wrong formula.");
2481 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,9,0), "SUM($D$4:$D$6)", "Wrong formula.");
2482 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,10,0), "D5", "Wrong formula.");
2483 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,11,0), "$D$6", "Wrong formula.");
2485 // Move A9:A12 to B10:B13.
2486 bMoved
= rFunc
.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
2487 CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved
);
2489 // The results of these formula cells should still stay the same.
2490 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(1,9,0));
2491 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(1,10,0));
2492 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(1,11,0));
2493 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(1,12,0));
2495 // Displayed formulas should stay the same since the referenced range hasn't moved.
2496 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2497 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2498 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,11,0), "D5", "Wrong formula.");
2499 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2501 // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
2503 bMoved
= rFunc
.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
2504 CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved
);
2506 // Only the values of B10 and B11 should be updated.
2507 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(1,9,0));
2508 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(1,10,0));
2509 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(1,11,0));
2510 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(1,12,0));
2512 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,9,0), "SUM(D4:D6)", "Wrong formula.");
2513 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,10,0), "SUM($D$4:$D$6)", "Wrong formula.");
2514 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,11,0), "E5", "Wrong formula.");
2515 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,12,0), "$D$6", "Wrong formula.");
2517 m_pDoc
->DeleteTab(0);
2520 void Test::testFormulaRefUpdateMoveUndo()
2522 m_pDoc
->InsertTab(0, "Test");
2524 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2526 // Set values in A1:A4.
2527 m_pDoc
->SetValue(ScAddress(0,0,0), 1.0);
2528 m_pDoc
->SetValue(ScAddress(0,1,0), 2.0);
2529 m_pDoc
->SetValue(ScAddress(0,2,0), 3.0);
2530 m_pDoc
->SetValue(ScAddress(0,3,0), 4.0);
2532 // Set formulas with single cell references in A6:A8.
2533 m_pDoc
->SetString(ScAddress(0,5,0), "=A1");
2534 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
2535 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "A1", "Wrong formula.");
2537 m_pDoc
->SetString(ScAddress(0,6,0), "=A1+A2+A3");
2538 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
2539 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2541 m_pDoc
->SetString(ScAddress(0,7,0), "=A1+A3+A4");
2542 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc
->GetValue(ScAddress(0,7,0)));
2543 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2545 // Set formulas with range references in A10:A12.
2546 m_pDoc
->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
2547 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
2548 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2550 m_pDoc
->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
2551 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,10,0)));
2552 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2554 m_pDoc
->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
2555 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,11,0)));
2556 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2558 // Move A1:A3 to C1:C3. Note that A4 remains.
2559 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2560 bool bMoved
= rFunc
.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
2561 CPPUNIT_ASSERT(bMoved
);
2563 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
2564 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "C1", "Wrong formula.");
2566 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
2567 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "C1+C2+C3", "Wrong formula.");
2569 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc
->GetValue(ScAddress(0,7,0)));
2570 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "C1+C3+A4", "Wrong formula.");
2572 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
2573 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,9,0), "SUM(C1:C2)", "Wrong formula.");
2575 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,10,0)));
2576 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,10,0), "SUM(C1:C3)", "Wrong formula.");
2578 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(0,11,0)));
2579 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,11,0), "SUM(A1:A4)", "Wrong formula.");
2582 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2583 CPPUNIT_ASSERT(pUndoMgr
);
2586 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
2587 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,5,0), "A1", "Wrong formula.");
2589 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
2590 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,6,0), "A1+A2+A3", "Wrong formula.");
2592 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc
->GetValue(ScAddress(0,7,0)));
2593 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,7,0), "A1+A3+A4", "Wrong formula.");
2595 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
2596 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,9,0), "SUM(A1:A2)", "Wrong formula.");
2598 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,10,0)));
2599 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,10,0), "SUM(A1:A3)", "Wrong formula.");
2601 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,11,0)));
2602 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,11,0), "SUM(A1:A4)","Wrong formula." );
2604 // Make sure the broadcasters are still valid by changing the value of A1.
2605 m_pDoc
->SetValue(ScAddress(0,0,0), 20);
2607 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
2608 CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
2609 CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc
->GetValue(ScAddress(0,7,0)));
2611 CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
2612 CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc
->GetValue(ScAddress(0,10,0)));
2613 CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc
->GetValue(ScAddress(0,11,0)));
2615 m_pDoc
->DeleteTab(0);
2618 void Test::testFormulaRefUpdateMoveUndo2()
2620 m_pDoc
->InsertTab(0, "Test");
2622 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2624 std::vector
<std::vector
<const char*>> aData
= {
2625 { "1", "2", "=A2*10", "=SUM(A1:B1)" },
2626 { "3", "4", "=SUM(A2:B2)", "=SUM(A2:B2)" },
2630 ScRange aOutRange
= insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
);
2632 std::vector
<std::vector
<const char*>> aCheckInitial
= {
2633 { "1", "2", "30", "3" },
2634 { "3", "4", "7", "7" },
2635 { "3", nullptr, nullptr, nullptr },
2638 bool bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "initial data");
2639 CPPUNIT_ASSERT(bGood
);
2641 // D1:D2 should be grouped.
2642 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(3,0,0));
2643 CPPUNIT_ASSERT(pFC
);
2644 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC
->GetSharedLength());
2646 // Drag A1:B1 into A2:B2 thereby overwriting the old A2:B2 content.
2647 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2648 bool bMoved
= rFunc
.MoveBlock(ScRange(0,0,0,1,0,0), ScAddress(0,1,0), true, true, false, true);
2649 CPPUNIT_ASSERT(bMoved
);
2651 std::vector
<std::vector
<const char*>> aCheckAfter
= {
2652 { nullptr, nullptr, "10", "3" },
2653 { "1", "2", "3", "3" },
2654 { "3", nullptr, nullptr, nullptr },
2657 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "A1:B1 moved to A2:B2");
2658 CPPUNIT_ASSERT(bGood
);
2661 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2662 CPPUNIT_ASSERT(pUndoMgr
);
2665 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "after undo");
2666 CPPUNIT_ASSERT(bGood
);
2668 // D1:D2 should be grouped.
2669 pFC
= m_pDoc
->GetFormulaCell(ScAddress(3,0,0));
2670 CPPUNIT_ASSERT(pFC
);
2671 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC
->GetSharedLength());
2676 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "after redo");
2677 CPPUNIT_ASSERT(bGood
);
2679 m_pDoc
->DeleteTab(0);
2682 void Test::testFormulaRefUpdateMoveUndo3NonShared()
2684 m_pDoc
->InsertTab(0, "Test");
2686 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2688 std::vector
<std::vector
<const char*>> aData
= {
2689 { "10", nullptr, nullptr },
2690 { "=A1", nullptr, nullptr },
2691 { "=A2+A1", nullptr, nullptr },
2694 ScRange aOutRange
= insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
);
2696 std::vector
<std::vector
<const char*>> aCheckInitial
= {
2697 { "10", nullptr, nullptr },
2698 { "10", nullptr, nullptr },
2699 { "20", nullptr, nullptr },
2702 bool bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "initial data");
2703 CPPUNIT_ASSERT(bGood
);
2705 // Drag A2:A3 into C2:C3.
2706 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2707 bool bMoved
= rFunc
.MoveBlock(ScRange(0,1,0,0,2,0), ScAddress(2,1,0), true, true, false, true);
2708 CPPUNIT_ASSERT(bMoved
);
2710 std::vector
<std::vector
<const char*>> aCheckAfter
= {
2711 { "10", nullptr, nullptr},
2712 { nullptr, nullptr, "10" },
2713 { nullptr, nullptr, "20" },
2716 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "A2:A3 moved to C2:C3");
2717 CPPUNIT_ASSERT(bGood
);
2720 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2721 CPPUNIT_ASSERT(pUndoMgr
);
2724 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "after undo");
2725 CPPUNIT_ASSERT(bGood
);
2730 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "after redo");
2731 CPPUNIT_ASSERT(bGood
);
2733 m_pDoc
->DeleteTab(0);
2736 void Test::testFormulaRefUpdateMoveUndo3Shared()
2738 m_pDoc
->InsertTab(0, "Test");
2740 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2742 std::vector
<std::vector
<const char*>> aData
= {
2743 { "10", nullptr, nullptr },
2744 { "=A1", nullptr, nullptr },
2745 { "=A2+$A$1", nullptr, nullptr },
2746 { "=A3+$A$1", nullptr, nullptr },
2749 ScRange aOutRange
= insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
);
2751 std::vector
<std::vector
<const char*>> aCheckInitial
= {
2752 { "10", nullptr, nullptr },
2753 { "10", nullptr, nullptr },
2754 { "20", nullptr, nullptr },
2755 { "30", nullptr, nullptr },
2758 bool bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "initial data");
2759 CPPUNIT_ASSERT(bGood
);
2761 // A3:A4 should be grouped.
2762 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,2,0));
2763 CPPUNIT_ASSERT(pFC
);
2764 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC
->GetSharedLength());
2766 // Drag A2:A4 into C2:C4.
2767 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2768 bool bMoved
= rFunc
.MoveBlock(ScRange(0,1,0,0,3,0), ScAddress(2,1,0), true, true, false, true);
2769 CPPUNIT_ASSERT(bMoved
);
2771 std::vector
<std::vector
<const char*>> aCheckAfter
= {
2772 { "10", nullptr, nullptr},
2773 { nullptr, nullptr, "10" },
2774 { nullptr, nullptr, "20" },
2775 { nullptr, nullptr, "30" },
2778 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "A2:A4 moved to C2:C4");
2779 CPPUNIT_ASSERT(bGood
);
2781 // C3:C4 should be grouped.
2782 pFC
= m_pDoc
->GetFormulaCell(ScAddress(2,2,0));
2783 CPPUNIT_ASSERT(pFC
);
2784 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC
->GetSharedLength());
2787 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2788 CPPUNIT_ASSERT(pUndoMgr
);
2791 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "after undo");
2792 CPPUNIT_ASSERT(bGood
);
2794 // A3:A4 should be grouped.
2795 pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,2,0));
2796 CPPUNIT_ASSERT(pFC
);
2797 CPPUNIT_ASSERT_EQUAL(SCROW(2), pFC
->GetSharedLength());
2802 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "after redo");
2803 CPPUNIT_ASSERT(bGood
);
2805 m_pDoc
->DeleteTab(0);
2808 void Test::testFormulaRefUpdateMoveUndoDependents()
2810 m_pDoc
->InsertTab(0, "Test");
2812 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2813 std::vector
<std::vector
<const char*>> aData
= {
2823 ScRange aOutRange
= insertRangeData(m_pDoc
, ScAddress(2,0,0), aData
);
2825 std::vector
<std::vector
<const char*>> aCheckInitial
= {
2835 bool bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "initial data");
2836 CPPUNIT_ASSERT(bGood
);
2839 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2840 bool bMoved
= rFunc
.MoveBlock(ScRange(2, 1, 0, 2, 1, 0), ScAddress(3, 1, 0), true, true, false, true);
2841 CPPUNIT_ASSERT(bMoved
);
2843 std::vector
<std::vector
<const char*>> aCheckAfter
= {
2853 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "C2 moved to D2");
2854 CPPUNIT_ASSERT(bGood
);
2857 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2858 CPPUNIT_ASSERT(pUndoMgr
);
2861 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "after undo");
2862 CPPUNIT_ASSERT(bGood
);
2867 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "after redo");
2868 CPPUNIT_ASSERT(bGood
);
2870 m_pDoc
->DeleteTab(0);
2873 void Test::testFormulaRefUpdateMoveUndo4()
2875 m_pDoc
->InsertTab(0, "Test");
2877 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2878 std::vector
<std::vector
<const char*>> aData
= {
2879 { "1", nullptr, "=B1", "=A1" },
2880 { "2", nullptr, "=B2", "=A2" },
2883 ScRange aOutRange
= insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
);
2885 std::vector
<std::vector
<const char*>> aCheckInitial
= {
2886 { "1", nullptr, "0", "1" },
2887 { "2", nullptr, "0", "2" },
2890 bool bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "initial data");
2891 CPPUNIT_ASSERT(bGood
);
2893 // Drag A1:A2 into B1:B2.
2894 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2895 bool bMoved
= rFunc
.MoveBlock(ScRange(0, 0, 0, 0, 1, 0), ScAddress(1, 0, 0), true, true, false, true);
2896 CPPUNIT_ASSERT(bMoved
);
2898 std::vector
<std::vector
<const char*>> aCheckAfter
= {
2899 { nullptr, "1", "1", "1" },
2900 { nullptr, "2", "2", "2" },
2903 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "A1:A2 moved to B1:B2");
2904 CPPUNIT_ASSERT(bGood
);
2906 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
2907 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
2908 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
2909 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
2912 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2913 CPPUNIT_ASSERT(pUndoMgr
);
2916 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckInitial
, "after undo");
2917 CPPUNIT_ASSERT(bGood
);
2919 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
2920 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
2921 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,0,0), "A1", "Wrong formula"); // D1
2922 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,1,0), "A2", "Wrong formula"); // D2
2927 bGood
= checkOutput(m_pDoc
, aOutRange
, aCheckAfter
, "after redo");
2928 CPPUNIT_ASSERT(bGood
);
2930 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,0,0), "B1", "Wrong formula"); // C1
2931 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(2,1,0), "B2", "Wrong formula"); // C2
2932 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,0,0), "B1", "Wrong formula"); // D1
2933 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(3,1,0), "B2", "Wrong formula"); // D2
2935 m_pDoc
->DeleteTab(0);
2938 void Test::testFormulaRefUpdateMoveToSheet()
2940 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2942 m_pDoc
->InsertTab(0, "Sheet1");
2943 m_pDoc
->InsertTab(1, "Sheet2");
2945 // Set values to A1:A2 on Sheet1, and B1:B2 to reference them.
2946 m_pDoc
->SetValue(ScAddress(0,0,0), 11);
2947 m_pDoc
->SetValue(ScAddress(0,1,0), 12);
2948 m_pDoc
->SetString(ScAddress(1,0,0), "=A1");
2949 m_pDoc
->SetString(ScAddress(1,1,0), "=A2");
2951 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,0,0), "A1", "Wrong formula");
2952 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "A2", "Wrong formula");
2954 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
2955 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
2957 // Move A1:A2 on Sheet1 to B3:B4 on Sheet2.
2958 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2959 bool bMoved
= rFunc
.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(1,2,1), true, true, false, true);
2960 CPPUNIT_ASSERT(bMoved
);
2962 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
2963 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
2965 // Undo and check again.
2966 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
2969 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,0,0), "A1", "Wrong formula");
2970 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "A2", "Wrong formula");
2975 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,0,0), "Sheet2.B3", "Wrong formula");
2976 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "Sheet2.B4", "Wrong formula");
2978 m_pDoc
->DeleteTab(1);
2979 m_pDoc
->DeleteTab(0);
2982 void Test::testFormulaRefUpdateDeleteContent()
2984 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
2986 m_pDoc
->InsertTab(0, "Test");
2989 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0);
2990 // Set formula in C2 to reference B2.
2991 m_pDoc
->SetString(ScAddress(2,1,0), "=B2");
2993 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
2996 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
2997 ScMarkData
aMark(MAXROW
, MAXCOL
);
2998 aMark
.SetMarkArea(ScAddress(1,1,0));
2999 rFunc
.DeleteContents(aMark
, InsertDeleteFlags::CONTENTS
, true, true);
3001 CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE
, m_pDoc
->GetCellType(ScAddress(1,1,0)));
3002 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
3004 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
3005 CPPUNIT_ASSERT(pUndoMgr
);
3007 // Undo and check the result of C2.
3009 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,1,0))); // B2
3010 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,1,0))); // C2
3014 CPPUNIT_ASSERT_EQUAL_MESSAGE("B2 should be empty.", CELLTYPE_NONE
, m_pDoc
->GetCellType(ScAddress(1,1,0)));
3015 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
3017 m_pDoc
->DeleteTab(0);
3020 void Test::testFormulaRefUpdateDeleteAndShiftLeft()
3022 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3024 m_pDoc
->InsertTab(0, "Test");
3026 // Insert 1,2,3,4,5 in C1:G1.
3027 for (SCCOL i
= 0; i
<= 4; ++i
)
3028 m_pDoc
->SetValue(ScAddress(i
+2,0,0), i
+1);
3030 // Insert formula in H1.
3031 ScAddress
aPos(7,0,0);
3032 m_pDoc
->SetString(aPos
, "=SUM(C1:G1)");
3034 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3036 // Delete columns D:E (middle of the reference).
3037 ScMarkData
aMark(MAXROW
, MAXCOL
);
3038 aMark
.SelectOneTable(0);
3039 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3040 bool bDeleted
= rFunc
.DeleteCells(ScRange(3,0,0,4,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
3041 CPPUNIT_ASSERT(bDeleted
);
3044 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(aPos
));
3045 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:E1)", "Wrong formula!");
3048 SfxUndoManager
* pUndo
= m_pDoc
->GetUndoManager();
3049 CPPUNIT_ASSERT(pUndo
);
3053 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3054 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:G1)", "Wrong formula!");
3056 // Delete columns C:D (left end of the reference).
3057 bDeleted
= rFunc
.DeleteCells(ScRange(2,0,0,3,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
3058 CPPUNIT_ASSERT(bDeleted
);
3061 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(aPos
));
3062 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:E1)", "Wrong formula!");
3064 // Undo and check again.
3067 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3068 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:G1)", "Wrong formula!");
3070 // Delete columns B:E (overlaps on the left).
3071 bDeleted
= rFunc
.DeleteCells(ScRange(1,0,0,4,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
3072 CPPUNIT_ASSERT(bDeleted
);
3075 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(aPos
));
3076 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B1:C1)", "Wrong formula!");
3078 // Undo and check again.
3081 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3082 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:G1)", "Wrong formula!");
3084 // Start over with a new scenario.
3085 clearSheet(m_pDoc
, 0);
3087 // Insert 1,2,3,4,5,6 into C1:H1.
3088 for (SCCOL i
= 0; i
<= 5; ++i
)
3089 m_pDoc
->SetValue(ScAddress(i
+2,0,0), i
+1);
3091 // Set formula in B1.
3092 aPos
= ScAddress(1,0,0);
3093 m_pDoc
->SetString(aPos
, "=SUM(C1:H1)");
3094 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(aPos
));
3096 // Delete columns F:H (right end of the reference).
3097 bDeleted
= rFunc
.DeleteCells(ScRange(5,0,0,7,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
3098 CPPUNIT_ASSERT(bDeleted
);
3100 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(aPos
));
3101 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:E1)", "Wrong formula!");
3105 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(aPos
));
3106 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:H1)", "Wrong formula!");
3108 // Delete columns G:I (overlaps on the right).
3109 bDeleted
= rFunc
.DeleteCells(ScRange(6,0,0,8,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
3110 CPPUNIT_ASSERT(bDeleted
);
3112 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(aPos
));
3113 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:F1)", "Wrong formula!");
3115 // Undo and check again.
3117 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(aPos
));
3118 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(C1:H1)", "Wrong formula!");
3120 m_pDoc
->DeleteTab(0);
3123 void Test::testFormulaRefUpdateDeleteAndShiftLeft2()
3125 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3127 m_pDoc
->InsertTab(0, "Test");
3129 std::vector
<std::vector
<const char*>> aData
= {
3130 { "1", "=COUNT($A$1:$A$4)", "=COUNT(A1)" },
3131 { "2", "=COUNT($A$1:$A$4)", "=COUNT(A2)" },
3132 { "3", "=COUNT($A$1:$A$4)", "=COUNT(A3)" },
3133 { "4", "=COUNT($A$1:$A$4)", "=COUNT(A4)" },
3136 insertRangeData(m_pDoc
, ScAddress(), aData
);
3138 auto funcCheckOriginal
= [&]()
3140 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(0,0,0))); // A1
3141 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(0,1,0))); // A2
3142 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,2,0))); // A3
3143 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(0,3,0))); // A4
3145 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,0,0))); // B1
3146 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,1,0))); // B2
3147 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0))); // B3
3148 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,3,0))); // B4
3150 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(2,0,0))); // C1
3151 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(2,1,0))); // C2
3152 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(2,2,0))); // C3
3153 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(2,3,0))); // C4
3156 auto funcCheckDeleted
= [&]()
3158 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(0,0,0))); // A1
3159 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(0,1,0))); // A2
3160 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(0,2,0))); // A3
3161 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(0,3,0))); // A4
3163 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(1,0,0))); // B1
3164 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(1,1,0))); // B2
3165 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(1,2,0))); // B3
3166 CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc
->GetString(ScAddress(1,3,0))); // B4
3169 funcCheckOriginal();
3172 ScMarkData
aMark(MAXROW
, MAXCOL
);
3173 aMark
.SelectOneTable(0);
3174 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3175 bool bDeleted
= rFunc
.DeleteCells(ScRange(0,0,0,0,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
3176 CPPUNIT_ASSERT(bDeleted
);
3181 SfxUndoManager
* pUndo
= m_pDoc
->GetUndoManager();
3182 CPPUNIT_ASSERT(pUndo
);
3185 funcCheckOriginal();
3191 m_pDoc
->DeleteTab(0);
3194 void Test::testFormulaRefUpdateDeleteAndShiftUp()
3196 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3198 m_pDoc
->InsertTab(0, "Test");
3200 // Insert 1,2,3,4,5 in A3:A7.
3201 for (SCROW i
= 0; i
<= 4; ++i
)
3202 m_pDoc
->SetValue(ScAddress(0,i
+2,0), i
+1);
3204 // Insert formula in A8.
3205 ScAddress
aPos(0,7,0);
3206 m_pDoc
->SetString(aPos
, "=SUM(A3:A7)");
3208 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3210 // Delete rows 4:5 (middle of the reference).
3211 ScMarkData
aMark(MAXROW
, MAXCOL
);
3212 aMark
.SelectOneTable(0);
3213 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3214 bool bDeleted
= rFunc
.DeleteCells(ScRange(0,3,0,m_pDoc
->MaxCol(),4,0), &aMark
, DelCellCmd::CellsUp
, true);
3215 CPPUNIT_ASSERT(bDeleted
);
3218 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(aPos
));
3219 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A5)", "Wrong formula!");
3222 SfxUndoManager
* pUndo
= m_pDoc
->GetUndoManager();
3223 CPPUNIT_ASSERT(pUndo
);
3227 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3228 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A7)", "Wrong formula!");
3230 // Delete rows 3:4 (top end of the reference).
3231 bDeleted
= rFunc
.DeleteCells(ScRange(0,2,0,m_pDoc
->MaxCol(),3,0), &aMark
, DelCellCmd::CellsUp
, true);
3232 CPPUNIT_ASSERT(bDeleted
);
3235 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(aPos
));
3236 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A5)", "Wrong formula!");
3238 // Undo and check again.
3241 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3242 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A7)", "Wrong formula!");
3244 // Delete rows 2:5 (overlaps on the top).
3245 bDeleted
= rFunc
.DeleteCells(ScRange(0,1,0,m_pDoc
->MaxCol(),4,0), &aMark
, DelCellCmd::CellsUp
, true);
3246 CPPUNIT_ASSERT(bDeleted
);
3249 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(aPos
));
3250 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A2:A3)", "Wrong formula!");
3252 // Undo and check again.
3255 CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc
->GetValue(aPos
));
3256 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A7)", "Wrong formula!");
3258 // Start over with a new scenario.
3259 clearSheet(m_pDoc
, 0);
3261 // Insert 1,2,3,4,5,6 into A3:A8.
3262 for (SCROW i
= 0; i
<= 5; ++i
)
3263 m_pDoc
->SetValue(ScAddress(0,i
+2,0), i
+1);
3265 // Set formula in B1.
3266 aPos
= ScAddress(0,1,0);
3267 m_pDoc
->SetString(aPos
, "=SUM(A3:A8)");
3268 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(aPos
));
3270 // Delete rows 6:8 (bottom end of the reference).
3271 bDeleted
= rFunc
.DeleteCells(ScRange(0,5,0,m_pDoc
->MaxCol(),7,0), &aMark
, DelCellCmd::CellsUp
, true);
3272 CPPUNIT_ASSERT(bDeleted
);
3274 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(aPos
));
3275 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A5)", "Wrong formula!");
3279 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(aPos
));
3280 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A8)", "Wrong formula!");
3282 // Delete rows 7:9 (overlaps on the bottom).
3283 bDeleted
= rFunc
.DeleteCells(ScRange(0,6,0,m_pDoc
->MaxCol(),8,0), &aMark
, DelCellCmd::CellsUp
, true);
3284 CPPUNIT_ASSERT(bDeleted
);
3286 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(aPos
));
3287 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A6)", "Wrong formula!");
3289 // Undo and check again.
3291 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(aPos
));
3292 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A3:A8)", "Wrong formula!");
3294 m_pDoc
->DeleteTab(0);
3297 void Test::testFormulaRefUpdateName()
3299 m_pDoc
->InsertTab(0, "Formula");
3301 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3303 // Fill C2:C5 with values.
3304 m_pDoc
->SetValue(ScAddress(2,1,0), 1);
3305 m_pDoc
->SetValue(ScAddress(2,2,0), 2);
3306 m_pDoc
->SetValue(ScAddress(2,3,0), 3);
3307 m_pDoc
->SetValue(ScAddress(2,4,0), 4);
3309 // Add a named expression that references the immediate left cell.
3310 ScRangeName
* pGlobalNames
= m_pDoc
->GetRangeName();
3311 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames
);
3312 ScRangeData
* pName
= new ScRangeData(
3313 m_pDoc
, "ToLeft", "RC[-1]", ScAddress(2,1,0),
3314 ScRangeData::Type::Name
, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1
);
3316 bool bInserted
= pGlobalNames
->insert(pName
);
3317 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted
);
3319 // Insert formulas in D2:D5 using the named expression.
3320 m_pDoc
->SetString(ScAddress(3,1,0), "=ToLeft");
3321 m_pDoc
->SetString(ScAddress(3,2,0), "=ToLeft");
3322 m_pDoc
->SetString(ScAddress(3,3,0), "=ToLeft");
3323 m_pDoc
->SetString(ScAddress(3,4,0), "=ToLeft");
3325 // Make sure the results are correct.
3326 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(3,1,0));
3327 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(3,2,0));
3328 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(3,3,0));
3329 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(3,4,0));
3331 // Push cells in column C down by one cell.
3332 m_pDoc
->InsertRow(ScRange(2,0,0,2,0,0));
3334 // Make sure the results change accordingly.
3335 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(3,1,0));
3336 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(3,2,0));
3337 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(3,3,0));
3338 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(3,4,0));
3341 m_pDoc
->DeleteRow(ScRange(2,0,0,2,0,0));
3343 // Make sure the results are back as well.
3344 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(3,1,0));
3345 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(3,2,0));
3346 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(3,3,0));
3347 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(3,4,0));
3349 // Fill B10:B12 with values.
3350 m_pDoc
->SetValue(ScAddress(1,9,0), 10);
3351 m_pDoc
->SetValue(ScAddress(1,10,0), 11);
3352 m_pDoc
->SetValue(ScAddress(1,11,0), 12);
3354 // Insert a new named expression that references these values as absolute range.
3355 pName
= new ScRangeData(
3356 m_pDoc
, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), ScRangeData::Type::Name
, formula::FormulaGrammar::GRAM_NATIVE
);
3357 bInserted
= pGlobalNames
->insert(pName
);
3358 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted
);
3360 // Set formula at C8 that references this named expression.
3361 m_pDoc
->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
3362 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc
->GetValue(ScAddress(2,7,0)));
3364 // Shift B10:B12 to right by 2 columns.
3365 m_pDoc
->InsertCol(ScRange(1,9,0,2,11,0));
3367 // This should shift the absolute range B10:B12 that MyRange references.
3368 pName
= pGlobalNames
->findByUpperName("MYRANGE");
3369 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName
);
3371 pName
->GetSymbol(aExpr
);
3372 CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr
);
3374 // This move shouldn't affect the value of C8.
3375 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(2,7,0));
3376 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC
);
3377 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc
->GetValue(ScAddress(2,7,0)));
3379 // Update the value of D10 and make sure C8 gets updated.
3380 m_pDoc
->SetValue(ScAddress(3,9,0), 20);
3381 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc
->GetValue(ScAddress(2,7,0)));
3383 // Insert a new sheet before the current.
3384 m_pDoc
->InsertTab(0, "New");
3386 m_pDoc
->GetName(1, aName
);
3387 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName
);
3389 pName
= pGlobalNames
->findByUpperName("MYRANGE");
3390 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName
);
3392 m_pDoc
->SetValue(ScAddress(3,9,1), 10);
3393 CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc
->GetValue(ScAddress(2,7,1)));
3395 // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
3396 m_pDoc
->DeleteTab(0);
3399 m_pDoc
->GetName(0, aName
);
3400 CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName
);
3402 pName
= pGlobalNames
->findByUpperName("MYRANGE");
3403 CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName
);
3405 m_pDoc
->SetValue(ScAddress(3,9,0), 11);
3406 CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc
->GetValue(ScAddress(2,7,0)));
3408 // Clear all and start over.
3409 clearRange(m_pDoc
, ScRange(0,0,0,100,100,0));
3410 pGlobalNames
->clear();
3412 pName
= new ScRangeData(
3413 m_pDoc
, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), ScRangeData::Type::Name
, formula::FormulaGrammar::GRAM_NATIVE
);
3414 bInserted
= pGlobalNames
->insert(pName
);
3415 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted
);
3416 pName
->GetSymbol(aExpr
);
3417 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr
);
3419 // Insert range of cells to shift right. The range partially overlaps the named range.
3420 m_pDoc
->InsertCol(ScRange(2,4,0,3,8,0));
3422 // This should not alter the range.
3423 pName
->GetSymbol(aExpr
);
3424 CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr
);
3426 m_pDoc
->DeleteTab(0);
3429 void Test::testFormulaRefUpdateNameMove()
3431 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3433 m_pDoc
->InsertTab(0, "Test");
3435 // Set values to B2:B4.
3436 m_pDoc
->SetValue(ScAddress(1,1,0), 1.0);
3437 m_pDoc
->SetValue(ScAddress(1,2,0), 2.0);
3438 m_pDoc
->SetValue(ScAddress(1,3,0), 3.0);
3440 // Set named range for B2:B4.
3441 bool bInserted
= m_pDoc
->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
3442 CPPUNIT_ASSERT(bInserted
);
3444 // Set formula in A10.
3445 m_pDoc
->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
3446 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
3448 ScRangeData
* pData
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3449 CPPUNIT_ASSERT(pData
);
3451 pData
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3452 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol
);
3454 // Move B2:B4 to D3.
3455 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3456 bool bMoved
= rFunc
.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3457 CPPUNIT_ASSERT(bMoved
);
3459 // The named range should have moved as well.
3460 pData
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3461 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol
);
3463 // The value of A10 should remain unchanged.
3464 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
3466 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
3467 CPPUNIT_ASSERT(pUndoMgr
);
3472 pData
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3473 CPPUNIT_ASSERT(pData
);
3474 pData
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3475 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol
);
3476 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
3481 pData
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3482 CPPUNIT_ASSERT(pData
);
3483 pData
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3484 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol
);
3485 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
3487 // Undo again to bring it back to the initial condition, and clear the undo buffer.
3491 // Add an identical formula to A11 and make a formula group over A10:A11.
3492 m_pDoc
->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
3493 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,9,0));
3494 CPPUNIT_ASSERT(pFC
);
3495 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(9), pFC
->GetSharedTopRow());
3496 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedLength());
3498 // Move B2:B4 to D3 again.
3499 bMoved
= rFunc
.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
3500 CPPUNIT_ASSERT(bMoved
);
3502 // Values of A10 and A11 should remain the same.
3503 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,9,0)));
3504 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,10,0)));
3506 // Clear and start over.
3507 clearSheet(m_pDoc
, 0);
3508 m_pDoc
->GetRangeName()->clear();
3511 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0);
3513 // Define B2 as 'MyCell'.
3514 bInserted
= m_pDoc
->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
3515 CPPUNIT_ASSERT(bInserted
);
3517 // Set formula to B3 that references B2 via MyCell.
3518 m_pDoc
->SetString(ScAddress(1,2,0), "=MyCell*2");
3519 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
3522 bMoved
= rFunc
.MoveBlock(ScRange(1,1,0,1,1,0), ScAddress(3,1,0), true, true, false, true);
3523 CPPUNIT_ASSERT(bMoved
);
3525 // Value in B3 should remain unchanged.
3526 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
3528 m_pDoc
->DeleteTab(0);
3531 void Test::testFormulaRefUpdateNameExpandRef()
3533 setExpandRefs(true);
3535 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3537 m_pDoc
->InsertTab(0, "Test");
3539 bool bInserted
= m_pDoc
->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
3540 CPPUNIT_ASSERT(bInserted
);
3542 // Set values to A1:A3.
3543 m_pDoc
->SetValue(ScAddress(0,0,0), 1.0);
3544 m_pDoc
->SetValue(ScAddress(0,1,0), 2.0);
3545 m_pDoc
->SetValue(ScAddress(0,2,0), 3.0);
3547 m_pDoc
->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
3548 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
3550 // Insert a new row at row 4, which should expand the named range to A1:A4.
3551 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3552 ScMarkData
aMark(MAXROW
, MAXCOL
);
3553 aMark
.SelectOneTable(0);
3554 rFunc
.InsertCells(ScRange(0,3,0,m_pDoc
->MaxCol(),3,0), &aMark
, INS_INSROWS_BEFORE
, false, true);
3555 ScRangeData
* pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3556 CPPUNIT_ASSERT(pName
);
3558 pName
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3559 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol
);
3561 // Make sure the listening area has been expanded as well. Note the
3562 // formula cell has been pushed downward by one cell.
3563 m_pDoc
->SetValue(ScAddress(0,3,0), 4.0);
3564 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
3566 // Insert a new column at column 2, which should not expand the named
3567 // range as it is only one column wide.
3568 rFunc
.InsertCells(ScRange(1,0,0,1,m_pDoc
->MaxRow(),0), &aMark
, INS_INSCOLS_BEFORE
, false, true);
3569 pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3570 CPPUNIT_ASSERT(pName
);
3571 pName
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3572 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol
);
3574 // Make sure the referenced area has not changed.
3575 m_pDoc
->SetValue(ScAddress(0,3,0), 2.0);
3576 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
3577 m_pDoc
->SetValue(ScAddress(1,3,0), 2.0);
3578 CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc
->GetValue(ScAddress(0,6,0)));
3580 // Clear the document and start over.
3581 m_pDoc
->GetRangeName()->clear();
3582 clearSheet(m_pDoc
, 0);
3584 // Set values to B4:B6.
3585 m_pDoc
->SetValue(ScAddress(1,3,0), 1.0);
3586 m_pDoc
->SetValue(ScAddress(1,4,0), 2.0);
3587 m_pDoc
->SetValue(ScAddress(1,5,0), 3.0);
3589 bInserted
= m_pDoc
->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
3590 CPPUNIT_ASSERT(bInserted
);
3592 // Set formula to A1.
3593 m_pDoc
->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
3594 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(0,0,0));
3596 // Insert rows over 3:5 which should expand the range by 3 rows.
3597 rFunc
.InsertCells(ScRange(0,2,0,m_pDoc
->MaxCol(),4,0), &aMark
, INS_INSROWS_BEFORE
, false, true);
3599 pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3600 CPPUNIT_ASSERT(pName
);
3602 pName
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3603 CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol
);
3605 // Clear the document and start over.
3606 m_pDoc
->GetRangeName()->clear();
3607 clearSheet(m_pDoc
, 0);
3609 // Set values to A1:A3.
3610 m_pDoc
->SetValue(ScAddress(0,0,0), 1.0);
3611 m_pDoc
->SetValue(ScAddress(0,1,0), 2.0);
3612 m_pDoc
->SetValue(ScAddress(0,2,0), 3.0);
3614 // Name A1:A3 'MyData'.
3615 bInserted
= m_pDoc
->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
3616 CPPUNIT_ASSERT(bInserted
);
3618 // Set formulas to C1:C2 and E1.
3619 m_pDoc
->SetString(ScAddress(2,0,0), "=SUM(MyData)");
3620 m_pDoc
->SetString(ScAddress(2,1,0), "=SUM(MyData)");
3621 m_pDoc
->SetString(ScAddress(4,0,0), "=SUM(MyData)");
3623 // C1:C2 should be shared.
3624 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(2,0,0));
3625 CPPUNIT_ASSERT(pFC
);
3626 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(0), pFC
->GetSharedTopRow());
3627 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedLength());
3629 // E1 should not be shared.
3630 pFC
= m_pDoc
->GetFormulaCell(ScAddress(4,0,0));
3631 CPPUNIT_ASSERT(pFC
);
3632 CPPUNIT_ASSERT(!pFC
->IsShared());
3634 // Check the results.
3635 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
3636 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
3637 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
3639 // Insert a new row at row 3. This should expand MyData to A1:A4.
3640 rFunc
.InsertCells(ScRange(0,2,0,m_pDoc
->MaxCol(),2,0), &aMark
, INS_INSROWS_BEFORE
, false, true);
3642 // Set new value to A3.
3643 m_pDoc
->SetValue(ScAddress(0,2,0), 4.0);
3645 // Check the results again.
3646 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
3647 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
3648 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
3650 m_pDoc
->DeleteTab(0);
3653 void Test::testFormulaRefUpdateNameExpandRef2()
3655 setExpandRefs(true);
3657 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
3659 m_pDoc
->InsertTab(0, "Test");
3661 bool bInserted
= m_pDoc
->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$B$3");
3662 CPPUNIT_ASSERT(bInserted
);
3664 // Insert a new row at row 4, which should expand the named range to A1:A4.
3665 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3666 ScMarkData
aMark(MAXROW
, MAXCOL
);
3667 aMark
.SelectOneTable(0);
3669 // Insert a new column at column 3, which should expand the named
3670 rFunc
.InsertCells(ScRange(1,0,0,1,m_pDoc
->MaxRow(),0), &aMark
, INS_INSCOLS_BEFORE
, false, true);
3671 ScRangeData
* pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3672 CPPUNIT_ASSERT(pName
);
3674 pName
->GetSymbol(aSymbol
, m_pDoc
->GetGrammar());
3675 CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$C$3"), aSymbol
);
3677 m_pDoc
->DeleteTab(0);
3680 void Test::testFormulaRefUpdateNameDeleteRow()
3682 m_pDoc
->InsertTab(0, "Test");
3684 // Insert a new name 'MyRange' to reference B2:B4.
3685 bool bInserted
= m_pDoc
->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$2:$B$4");
3686 CPPUNIT_ASSERT(bInserted
);
3688 const ScRangeData
* pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3689 CPPUNIT_ASSERT(pName
);
3691 sc::TokenStringContext
aCxt(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH
);
3692 const ScTokenArray
* pCode
= pName
->GetCode();
3693 OUString aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3694 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr
);
3696 // Insert a new name 'MyAddress' to reference $B$3. Note absolute row.
3697 bInserted
= m_pDoc
->InsertNewRangeName("MyAddress", ScAddress(0,0,0), "$B$3");
3698 CPPUNIT_ASSERT(bInserted
);
3700 const ScRangeData
* pName2
= m_pDoc
->GetRangeName()->findByUpperName("MYADDRESS");
3701 CPPUNIT_ASSERT(pName2
);
3703 sc::TokenStringContext
aCxt2(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH
);
3704 const ScTokenArray
* pCode2
= pName2
->GetCode();
3705 OUString aExpr2
= pCode2
->CreateString(m_pDoc
, aCxt2
, ScAddress(0,0,0));
3706 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2
);
3708 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
3711 ScMarkData
aMark(MAXROW
, MAXCOL
);
3712 aMark
.SelectOneTable(0);
3713 rFunc
.DeleteCells(ScRange(0,2,0,m_pDoc
->MaxCol(),2,0), &aMark
, DelCellCmd::CellsUp
, true);
3715 // The reference in the 'MyRange' name should get updated to B2:B3.
3716 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3717 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr
);
3719 // The reference in the 'MyAddress' name should get updated to $B$#REF!.
3720 aExpr2
= pCode2
->CreateString(m_pDoc
, aCxt2
, ScAddress(0,0,0));
3721 CPPUNIT_ASSERT_EQUAL(OUString("$B$#REF!"), aExpr2
);
3723 // Delete row 3 again.
3724 rFunc
.DeleteCells(ScRange(0,2,0,m_pDoc
->MaxCol(),2,0), &aMark
, DelCellCmd::CellsUp
, true);
3725 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3726 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr
);
3729 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
3730 CPPUNIT_ASSERT(pUndoMgr
);
3734 pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3735 CPPUNIT_ASSERT(pName
);
3736 pCode
= pName
->GetCode();
3738 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3739 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr
);
3741 // Undo again and check.
3744 pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3745 CPPUNIT_ASSERT(pName
);
3746 pCode
= pName
->GetCode();
3748 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3749 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr
);
3752 rFunc
.DeleteCells(ScRange(0,1,0,m_pDoc
->MaxCol(),2,0), &aMark
, DelCellCmd::CellsUp
, true);
3754 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3755 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr
);
3760 pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3761 CPPUNIT_ASSERT(pName
);
3762 pCode
= pName
->GetCode();
3764 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3765 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr
);
3767 pName2
= m_pDoc
->GetRangeName()->findByUpperName("MYADDRESS");
3768 CPPUNIT_ASSERT(pName2
);
3769 pCode2
= pName2
->GetCode();
3771 aExpr2
= pCode2
->CreateString(m_pDoc
, aCxt2
, ScAddress(0,0,0));
3772 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2
);
3774 m_pDoc
->InsertTab(1, "test2");
3776 ScMarkData
aMark2(MAXROW
, MAXCOL
);
3777 aMark2
.SelectOneTable(1);
3778 rFunc
.DeleteCells(ScRange(0,2,1,m_pDoc
->MaxCol(),2,1), &aMark2
, DelCellCmd::CellsUp
, true);
3780 pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
3781 CPPUNIT_ASSERT(pName
);
3782 pCode
= pName
->GetCode();
3784 aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
3785 CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr
);
3787 pName2
= m_pDoc
->GetRangeName()->findByUpperName("MYADDRESS");
3788 CPPUNIT_ASSERT(pName2
);
3789 pCode2
= pName2
->GetCode();
3791 // Deleting a range the 'MyAddress' name points into due to its implicit
3792 // relative sheet reference to the sheet where used does not invalidate
3793 // the named expression because when updating the sheet reference is
3794 // relative to its base position on sheet 0 (same for the 'MyRange' range,
3795 // which is the reason why it is not updated either).
3796 // This is a tad confusing...
3797 aExpr2
= pCode2
->CreateString(m_pDoc
, aCxt2
, ScAddress(0,0,0));
3798 CPPUNIT_ASSERT_EQUAL(OUString("$B$3"), aExpr2
);
3800 m_pDoc
->DeleteTab(1);
3801 m_pDoc
->DeleteTab(0);
3804 void Test::testFormulaRefUpdateNameCopySheet()
3806 m_pDoc
->InsertTab(0, "Test");
3807 m_pDoc
->InsertTab(1, "Test2");
3809 bool bInserted
= m_pDoc
->InsertNewRangeName("RED", ScAddress(0,0,0), "$Test.$B$2");
3810 CPPUNIT_ASSERT(bInserted
);
3811 bInserted
= m_pDoc
->InsertNewRangeName("BLUE", ScAddress(0,0,0), "$Test.$B$3");
3812 CPPUNIT_ASSERT(bInserted
);
3813 m_pDoc
->SetValue(1, 1, 0, 1);
3814 m_pDoc
->SetValue(1, 2, 0, 2);
3816 // insert formula into Test2 that is =RED+BLUE
3817 m_pDoc
->SetString(ScAddress(2,2,1), "=RED+BLUE");
3819 double nVal
= m_pDoc
->GetValue(2, 2, 1);
3820 CPPUNIT_ASSERT_EQUAL(3.0, nVal
);
3821 m_pDoc
->CopyTab(1, 0);
3823 nVal
= m_pDoc
->GetValue(2, 2, 2);
3824 CPPUNIT_ASSERT_EQUAL(3.0, nVal
);
3826 nVal
= m_pDoc
->GetValue(2, 2, 0);
3827 CPPUNIT_ASSERT_EQUAL(3.0, nVal
);
3829 m_pDoc
->SetValue(1, 1, 1, 3);
3831 nVal
= m_pDoc
->GetValue(2, 2, 2);
3832 CPPUNIT_ASSERT_EQUAL(5.0, nVal
);
3834 nVal
= m_pDoc
->GetValue(2, 2, 0);
3835 CPPUNIT_ASSERT_EQUAL(5.0, nVal
);
3837 m_pDoc
->DeleteTab(2);
3838 m_pDoc
->DeleteTab(1);
3839 m_pDoc
->DeleteTab(0);
3841 m_pDoc
->InsertTab(0, "Test1");
3842 // Global name referencing sheet Test1.
3843 bInserted
= m_pDoc
->InsertNewRangeName("sheetnumber", ScAddress(0,0,0), "$Test1.$A$1");
3844 CPPUNIT_ASSERT(bInserted
);
3845 m_pDoc
->SetString(ScAddress(0,0,0), "=SHEET()");
3846 m_pDoc
->SetString(ScAddress(1,0,0), "=sheetnumber");
3847 nVal
= m_pDoc
->GetValue(1,0,0);
3848 CPPUNIT_ASSERT_EQUAL_MESSAGE("Sheet number should be 1", 1.0, nVal
);
3850 // Copy sheet after.
3851 m_pDoc
->CopyTab(0, 1);
3852 nVal
= m_pDoc
->GetValue(1,0,1);
3853 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 2", 2.0, nVal
);
3854 nVal
= m_pDoc
->GetValue(1,0,0);
3855 CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 1", 1.0, nVal
);
3856 const ScRangeData
* pName
= m_pDoc
->GetRangeName(1)->findByUpperName("SHEETNUMBER");
3857 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName
);
3859 // Copy sheet before, shifting following now two sheets.
3860 m_pDoc
->CopyTab(0, 0);
3861 nVal
= m_pDoc
->GetValue(1,0,0);
3862 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal
);
3863 pName
= m_pDoc
->GetRangeName(0)->findByUpperName("SHEETNUMBER");
3864 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName
);
3865 nVal
= m_pDoc
->GetValue(1,0,1);
3866 CPPUNIT_ASSERT_EQUAL_MESSAGE("Org sheet number should be 2", 2.0, nVal
);
3867 pName
= m_pDoc
->GetRangeName(1)->findByUpperName("SHEETNUMBER");
3868 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should not exist", !pName
);
3869 nVal
= m_pDoc
->GetValue(1,0,2);
3870 CPPUNIT_ASSERT_EQUAL_MESSAGE("Old sheet number should be 3", 3.0, nVal
);
3871 pName
= m_pDoc
->GetRangeName(2)->findByUpperName("SHEETNUMBER");
3872 CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should exist", pName
);
3874 m_pDoc
->DeleteTab(2);
3875 m_pDoc
->DeleteTab(1);
3876 m_pDoc
->DeleteTab(0);
3878 m_pDoc
->InsertTab(0, "Test2");
3879 // Local name referencing sheet Test2.
3880 bInserted
= m_pDoc
->GetRangeName(0)->insert( new ScRangeData( m_pDoc
, "localname", "$Test2.$A$1"));
3881 CPPUNIT_ASSERT(bInserted
);
3882 m_pDoc
->SetString(ScAddress(0,0,0), "=SHEET()");
3883 m_pDoc
->SetString(ScAddress(1,0,0), "=localname");
3884 nVal
= m_pDoc
->GetValue(1,0,0);
3885 CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 1", 1.0, nVal
);
3887 // Insert sheet before and shift sheet with local name.
3888 m_pDoc
->InsertTab(0, "Test1");
3889 pName
= m_pDoc
->GetRangeName(1)->findByUpperName("LOCALNAME");
3890 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName
);
3891 nVal
= m_pDoc
->GetValue(1,0,1);
3892 CPPUNIT_ASSERT_EQUAL_MESSAGE("Localname sheet number should be 2", 2.0, nVal
);
3894 // Copy sheet before, shifting following now two sheets.
3895 m_pDoc
->CopyTab(1, 0);
3896 pName
= m_pDoc
->GetRangeName(0)->findByUpperName("LOCALNAME");
3897 CPPUNIT_ASSERT_MESSAGE("New sheet-local name should exist", pName
);
3898 nVal
= m_pDoc
->GetValue(1,0,0);
3899 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 1", 1.0, nVal
);
3900 pName
= m_pDoc
->GetRangeName(1)->findByUpperName("LOCALNAME");
3901 CPPUNIT_ASSERT_MESSAGE("Old sheet-local name should not exist", !pName
);
3902 pName
= m_pDoc
->GetRangeName(2)->findByUpperName("LOCALNAME");
3903 CPPUNIT_ASSERT_MESSAGE("Org sheet-local name should exist", pName
);
3904 nVal
= m_pDoc
->GetValue(1,0,2);
3905 CPPUNIT_ASSERT_EQUAL_MESSAGE("New sheet number should be 3", 3.0, nVal
);
3907 m_pDoc
->DeleteTab(2);
3908 m_pDoc
->DeleteTab(1);
3909 m_pDoc
->DeleteTab(0);
3910 m_pDoc
->SetRangeName(nullptr);
3912 // Test nested names during copying sheet.
3914 m_pDoc
->InsertTab(0, "Test2");
3915 ScAddress
aPos(0,0,0);
3916 bInserted
= m_pDoc
->InsertNewRangeName( "global", aPos
, "$Test2.$A$1");
3917 CPPUNIT_ASSERT(bInserted
);
3918 bInserted
= m_pDoc
->InsertNewRangeName( aPos
.Tab(), "local", aPos
, "$Test2.$A$2");
3919 CPPUNIT_ASSERT(bInserted
);
3920 bInserted
= m_pDoc
->InsertNewRangeName( "global_global", aPos
, "global*100");
3921 CPPUNIT_ASSERT(bInserted
);
3922 bInserted
= m_pDoc
->InsertNewRangeName( "global_local", aPos
, "local*1000");
3923 CPPUNIT_ASSERT(bInserted
);
3924 bInserted
= m_pDoc
->InsertNewRangeName( "global_unused", aPos
, "$Test2.$A$1");
3925 CPPUNIT_ASSERT(bInserted
);
3926 bInserted
= m_pDoc
->InsertNewRangeName( "global_unused_noref", aPos
, "42");
3927 CPPUNIT_ASSERT(bInserted
);
3928 bInserted
= m_pDoc
->InsertNewRangeName( aPos
.Tab(), "local_global", aPos
, "global*10000");
3929 CPPUNIT_ASSERT(bInserted
);
3930 bInserted
= m_pDoc
->InsertNewRangeName( aPos
.Tab(), "local_local", aPos
, "local*100000");
3931 CPPUNIT_ASSERT(bInserted
);
3932 bInserted
= m_pDoc
->InsertNewRangeName( aPos
.Tab(), "local_unused", aPos
, "$Test2.$A$2");
3933 CPPUNIT_ASSERT(bInserted
);
3934 bInserted
= m_pDoc
->InsertNewRangeName( aPos
.Tab(), "local_unused_noref", aPos
, "23");
3935 CPPUNIT_ASSERT(bInserted
);
3937 m_pDoc
->SetString(aPos
, "=SHEET()");
3939 m_pDoc
->SetString(aPos
, "=A1*10+SHEET()");
3941 m_pDoc
->SetString(aPos
, "=global_global");
3943 m_pDoc
->SetString(aPos
, "=global_local");
3945 m_pDoc
->SetString(aPos
, "=local_global");
3947 m_pDoc
->SetString(aPos
, "=local_local");
3949 testFormulaRefUpdateNameCopySheetCheckTab( 0, false);
3951 // Copy sheet after.
3952 m_pDoc
->CopyTab(0, 1);
3953 testFormulaRefUpdateNameCopySheetCheckTab( 0, false);
3954 testFormulaRefUpdateNameCopySheetCheckTab( 1, true);
3956 // Copy sheet before, shifting following now two sheets.
3957 m_pDoc
->CopyTab(1, 0);
3958 testFormulaRefUpdateNameCopySheetCheckTab( 0, true);
3959 testFormulaRefUpdateNameCopySheetCheckTab( 1, false);
3960 testFormulaRefUpdateNameCopySheetCheckTab( 2, true);
3962 m_pDoc
->DeleteTab(2);
3963 m_pDoc
->DeleteTab(1);
3964 m_pDoc
->DeleteTab(0);
3967 void Test::testFormulaRefUpdateNameCopySheetCheckTab( SCTAB nTab
, bool bCheckNames
)
3971 const ScRangeData
* pName
;
3972 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("GLOBAL");
3973 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL should exist", pName
);
3974 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("LOCAL");
3975 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL should exist", pName
);
3976 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("GLOBAL_GLOBAL");
3977 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_GLOBAL should exist", pName
);
3978 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("GLOBAL_LOCAL");
3979 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_LOCAL should exist", pName
);
3980 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("GLOBAL_UNUSED");
3981 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED should exist", pName
);
3982 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("GLOBAL_UNUSED_NOREF");
3983 CPPUNIT_ASSERT_MESSAGE("Sheet-local name GLOBAL_UNUSED_NOREF should not exist", !pName
);
3984 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("LOCAL_GLOBAL");
3985 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_GLOBAL should exist", pName
);
3986 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("LOCAL_LOCAL");
3987 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_LOCAL should exist", pName
);
3988 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("LOCAL_UNUSED");
3989 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED should exist", pName
);
3990 pName
= m_pDoc
->GetRangeName(nTab
)->findByUpperName("LOCAL_UNUSED_NOREF");
3991 CPPUNIT_ASSERT_MESSAGE("Sheet-local name LOCAL_UNUSED_NOREF should exist", pName
);
3994 ScAddress
aPos(0,0,0);
3997 int nSheet
= nTab
+ 1;
3998 CPPUNIT_ASSERT_EQUAL( 1.0 * nSheet
, m_pDoc
->GetValue(aPos
));
4000 CPPUNIT_ASSERT_EQUAL( 11.0 * nSheet
, m_pDoc
->GetValue(aPos
));
4002 CPPUNIT_ASSERT_EQUAL( 100.0 * nSheet
, m_pDoc
->GetValue(aPos
));
4004 CPPUNIT_ASSERT_EQUAL( 11000.0 * nSheet
, m_pDoc
->GetValue(aPos
));
4006 CPPUNIT_ASSERT_EQUAL( 10000.0 * nSheet
, m_pDoc
->GetValue(aPos
));
4008 CPPUNIT_ASSERT_EQUAL( 1100000.0 * nSheet
, m_pDoc
->GetValue(aPos
));
4011 void Test::testFormulaRefUpdateNameDelete()
4013 m_pDoc
->InsertTab(0, "Test");
4015 // Insert a new name 'MyRange' to reference B1
4016 bool bInserted
= m_pDoc
->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$1");
4017 CPPUNIT_ASSERT(bInserted
);
4019 const ScRangeData
* pName
= m_pDoc
->GetRangeName()->findByUpperName("MYRANGE");
4020 CPPUNIT_ASSERT(pName
);
4022 m_pDoc
->DeleteCol(1, 0, 3, 0, 0, 1);
4023 const ScTokenArray
* pCode
= pName
->GetCode();
4024 sc::TokenStringContext
aCxt(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH
);
4025 OUString aExpr
= pCode
->CreateString(m_pDoc
, aCxt
, ScAddress(0,0,0));
4026 CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$1"), aExpr
);
4028 m_pDoc
->DeleteTab(0);
4031 void Test::testFormulaRefUpdateValidity()
4035 bool checkList( std::vector
<ScTypedStrData
>& rList
)
4037 double aExpected
[] = { 1.0, 2.0, 3.0 }; // must be sorted.
4038 size_t nCheckSize
= SAL_N_ELEMENTS(aExpected
);
4040 if (rList
.size() != nCheckSize
)
4042 cerr
<< "List size is not what is expected." << endl
;
4046 std::sort(rList
.begin(), rList
.end(), ScTypedStrData::LessCaseSensitive());
4048 for (size_t i
= 0; i
< nCheckSize
; ++i
)
4050 if (aExpected
[i
] != rList
[i
].GetValue())
4052 cerr
<< "Incorrect value at position " << i
4053 << ": expected=" << aExpected
[i
] << ", actual=" << rList
[i
].GetValue() << endl
;
4063 setExpandRefs(false);
4064 setCalcAsShown(m_pDoc
, true);
4066 m_pDoc
->InsertTab(0, "Formula");
4068 // Set values in C2:C4.
4069 m_pDoc
->SetValue(ScAddress(2,1,0), 1.0);
4070 m_pDoc
->SetValue(ScAddress(2,2,0), 2.0);
4071 m_pDoc
->SetValue(ScAddress(2,3,0), 3.0);
4073 // Set validity in A2.
4074 ScValidationData
aData(
4075 SC_VALID_LIST
, ScConditionMode::Equal
, "C2:C4", "", m_pDoc
, ScAddress(0,1,0), "", "",
4076 m_pDoc
->GetGrammar(), m_pDoc
->GetGrammar());
4078 sal_uLong nIndex
= m_pDoc
->AddValidationEntry(aData
);
4079 SfxUInt32Item
aItem(ATTR_VALIDDATA
, nIndex
);
4081 ScPatternAttr
aNewAttrs(
4082 std::make_unique
<SfxItemSet
>(*m_pDoc
->GetPool(), svl::Items
<ATTR_PATTERN_START
, ATTR_PATTERN_END
>{}));
4083 aNewAttrs
.GetItemSet().Put(aItem
);
4085 m_pDoc
->ApplyPattern(0, 1, 0, aNewAttrs
);
4087 const ScValidationData
* pData
= m_pDoc
->GetValidationEntry(nIndex
);
4088 CPPUNIT_ASSERT(pData
);
4090 // Make sure the list is correct.
4091 std::vector
<ScTypedStrData
> aList
;
4092 pData
->FillSelectionList(aList
, ScAddress(0,1,0));
4093 bool bGood
= aCheck
.checkList(aList
);
4094 CPPUNIT_ASSERT_MESSAGE("Initial list is incorrect.", bGood
);
4096 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
4097 ScMarkData
aMark(MAXROW
, MAXCOL
);
4098 aMark
.SelectOneTable(0);
4100 // Insert a new column at Column B, to move the list from C2:C4 to D2:D4.
4101 bool bInserted
= rFunc
.InsertCells(ScRange(1,0,0,1,m_pDoc
->MaxRow(),0), &aMark
, INS_INSCOLS_BEFORE
, true, true);
4102 CPPUNIT_ASSERT_MESSAGE("Column insertion failed.", bInserted
);
4103 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
4104 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
4105 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
4107 // Check the list values again.
4109 pData
->FillSelectionList(aList
, ScAddress(0,1,0));
4110 bGood
= aCheck
.checkList(aList
);
4111 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after column insertion.", bGood
);
4113 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
4114 CPPUNIT_ASSERT(pUndoMgr
);
4116 // Undo and check the list content again. The list moves back to C2:C4 after the undo.
4118 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
4119 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,2,0)));
4120 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(2,3,0)));
4123 pData
->FillSelectionList(aList
, ScAddress(0,1,0));
4124 bGood
= aCheck
.checkList(aList
);
4125 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of column insertion.", bGood
);
4127 // Move C2:C4 to E5:E7.
4128 bool bMoved
= rFunc
.MoveBlock(ScRange(2,1,0,2,3,0), ScAddress(4,4,0), false, true, false, true);
4129 CPPUNIT_ASSERT(bMoved
);
4130 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(4,4,0)));
4131 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(4,5,0)));
4132 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(4,6,0)));
4134 // Check the list again after the move.
4136 pData
->FillSelectionList(aList
, ScAddress(0,1,0));
4137 bGood
= aCheck
.checkList(aList
);
4138 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after moving C2:C4 to E5:E7.", bGood
);
4140 // Undo the move and check. The list should be back to C2:C4.
4142 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
4143 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,2,0)));
4144 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(2,3,0)));
4147 pData
->FillSelectionList(aList
, ScAddress(0,1,0));
4148 bGood
= aCheck
.checkList(aList
);
4149 CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of the move.", bGood
);
4151 m_pDoc
->DeleteTab(0);
4154 void Test::testTokenArrayRefUpdateMove()
4156 m_pDoc
->InsertTab(0, "Sheet1");
4157 m_pDoc
->InsertTab(1, "Sheet2");
4159 ScAddress
aPos(0,0,0); // A1
4161 sc::TokenStringContext
aCxt(m_pDoc
, m_pDoc
->GetGrammar());
4163 // Emulate cell movement from Sheet1.C3 to Sheet2.C3.
4164 sc::RefUpdateContext
aRefCxt(*m_pDoc
);
4165 aRefCxt
.meMode
= URM_MOVE
;
4166 aRefCxt
.maRange
= ScAddress(2,2,1); // C3 on Sheet2.
4167 aRefCxt
.mnTabDelta
= -1;
4169 std::vector
<OUString
> aTests
= {
4173 "SUM(Sheet1.B1:Sheet2.B1)"
4176 // Since C3 is not referenced in any of the above formulas, moving C3 from
4177 // Sheet1 to Sheet2 should NOT change the displayed formula string at all.
4179 for (const OUString
& aTest
: aTests
)
4181 ScCompiler
aComp(m_pDoc
, aPos
, m_pDoc
->GetGrammar());
4182 std::unique_ptr
<ScTokenArray
> pArray(aComp
.CompileString(aTest
));
4184 OUString aStr
= pArray
->CreateString(m_pDoc
, aCxt
, aPos
);
4186 CPPUNIT_ASSERT_EQUAL(aTest
, aStr
);
4188 // This formula cell isn't moving its position. The displayed formula
4189 // string should not change.
4190 pArray
->AdjustReferenceOnMove(aRefCxt
, aPos
, aPos
);
4192 aStr
= pArray
->CreateString(m_pDoc
, aCxt
, aPos
);
4193 CPPUNIT_ASSERT_EQUAL(aTest
, aStr
);
4196 m_pDoc
->DeleteTab(1);
4197 m_pDoc
->DeleteTab(0);
4200 void Test::testMultipleOperations()
4202 m_pDoc
->InsertTab(0, "MultiOp");
4204 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4206 // Insert the reference formula at top row.
4207 m_pDoc
->SetValue(ScAddress(0,0,0), 1);
4208 m_pDoc
->SetString(ScAddress(1,0,0), "=A1*10");
4209 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
4211 // Insert variable inputs in A3:A5.
4212 m_pDoc
->SetValue(ScAddress(0,2,0), 2);
4213 m_pDoc
->SetValue(ScAddress(0,3,0), 3);
4214 m_pDoc
->SetValue(ScAddress(0,4,0), 4);
4216 // Set multiple operations range.
4217 ScTabOpParam aParam
;
4218 aParam
.aRefFormulaCell
= ScRefAddress(1,0,0);
4219 aParam
.aRefFormulaEnd
= aParam
.aRefFormulaCell
;
4220 aParam
.aRefColCell
= ScRefAddress(0,0,0);
4221 ScMarkData
aMark(MAXROW
, MAXCOL
);
4222 aMark
.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
4223 m_pDoc
->InsertTableOp(aParam
, 0, 2, 1, 4, aMark
);
4224 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc
->GetValue(1,2,0));
4225 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc
->GetValue(1,3,0));
4226 CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc
->GetValue(1,4,0));
4229 clearRange(m_pDoc
, ScRange(0,2,0,1,4,0));
4231 // This time, use indirect reference formula cell.
4232 m_pDoc
->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
4233 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
4235 // Insert variable inputs in A3:A5.
4236 m_pDoc
->SetValue(ScAddress(0,2,0), 3);
4237 m_pDoc
->SetValue(ScAddress(0,3,0), 4);
4238 m_pDoc
->SetValue(ScAddress(0,4,0), 5);
4240 // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
4241 aParam
.aRefFormulaCell
.Set(2,0,0,false,false,false);
4242 aParam
.aRefFormulaEnd
= aParam
.aRefFormulaCell
;
4243 m_pDoc
->InsertTableOp(aParam
, 0, 2, 1, 4, aMark
);
4244 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc
->GetValue(1,2,0));
4245 CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc
->GetValue(1,3,0));
4246 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc
->GetValue(1,4,0));
4248 m_pDoc
->DeleteTab(0);
4251 void Test::testFuncCOLUMN()
4253 m_pDoc
->InsertTab(0, "Formula");
4254 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4256 m_pDoc
->SetString(ScAddress(5,10,0), "=COLUMN()");
4257 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(5,10,0)));
4259 m_pDoc
->SetString(ScAddress(0,1,0), "=F11");
4260 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4262 // Move the formula cell with COLUMN() function to change its value.
4263 m_pDoc
->InsertCol(ScRange(5,0,0,5,m_pDoc
->MaxRow(),0));
4264 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(ScAddress(6,10,0)));
4266 // The cell that references the moved cell should update its value as well.
4267 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4269 // Move the column in the other direction.
4270 m_pDoc
->DeleteCol(ScRange(5,0,0,5,m_pDoc
->MaxRow(),0));
4272 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(5,10,0)));
4274 // The cell that references the moved cell should update its value as well.
4275 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4277 m_pDoc
->DeleteTab(0);
4280 void Test::testFuncCOUNT()
4282 m_pDoc
->InsertTab(0, "Formula");
4283 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4285 m_pDoc
->SetValue(ScAddress(0,0,0), 2); // A1
4286 m_pDoc
->SetValue(ScAddress(0,1,0), 4); // A2
4287 m_pDoc
->SetValue(ScAddress(0,2,0), 6); // A3
4289 ScAddress
aPos(1,0,0);
4290 m_pDoc
->SetString(aPos
, "=COUNT(A1:A3)");
4291 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(aPos
));
4294 m_pDoc
->SetString(aPos
, "=COUNT(A1:A3;2)");
4295 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(aPos
));
4298 m_pDoc
->SetString(aPos
, "=COUNT(A1:A3;2;4)");
4299 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc
->GetValue(aPos
));
4302 m_pDoc
->SetString(aPos
, "=COUNT(A1:A3;2;4;6)");
4303 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(aPos
));
4306 ScMarkData
aMark(MAXROW
, MAXCOL
);
4307 aMark
.SelectOneTable(0);
4308 m_pDoc
->InsertMatrixFormula(2, 0, 2, 0, aMark
, "=COUNT(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4309 // Check that the #VALUE! error of "a" not found in "b" is not counted.
4310 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
4313 m_pDoc
->InsertMatrixFormula(2, 2, 2, 2, aMark
, "=COUNTA(SEARCH(\"a\";{\"a\";\"b\";\"a\"}))");
4314 // Check that the #VALUE! error of "a" not found in "b" is counted.
4315 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(2,2,0)));
4317 m_pDoc
->DeleteTab(0);
4320 void Test::testFuncCOUNTBLANK()
4322 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4323 m_pDoc
->InsertTab(0, "Formula");
4325 const char* aData
[][4] = {
4326 { "1", nullptr, "=B1", "=\"\"" },
4327 { "2", nullptr, "=B2", "=\"\"" },
4328 { "A", nullptr, "=B3", "=\"\"" },
4329 { "B", nullptr, "=B4", "=D3" },
4330 { nullptr, nullptr, "=B5", "=D4" },
4331 { "=COUNTBLANK(A1:A5)", "=COUNTBLANK(B1:B5)", "=COUNTBLANK(C1:C5)", "=COUNTBLANK(D1:D5)" }
4334 ScAddress
aPos(0,0,0);
4335 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
4336 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
4338 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
4339 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc
->GetValue(ScAddress(1,5,0)));
4340 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(2,5,0)));
4341 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
4343 // Test single cell reference cases.
4345 clearSheet(m_pDoc
, 0);
4347 const char* aData2
[][2] = {
4348 { "1", "=COUNTBLANK(A1)" },
4349 { "A", "=COUNTBLANK(A2)" },
4350 { nullptr, "=COUNTBLANK(A3)" },
4351 { "=\"\"", "=COUNTBLANK(A4)" },
4352 { "=A4" , "=COUNTBLANK(A5)" },
4355 aRange
= insertRangeData(m_pDoc
, aPos
, aData2
, SAL_N_ELEMENTS(aData2
));
4356 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
4358 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
4359 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
4360 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
4361 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
4362 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
4364 m_pDoc
->DeleteTab(0);
4367 void Test::testFuncROW()
4369 m_pDoc
->InsertTab(0, "Formula");
4370 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4372 m_pDoc
->SetString(ScAddress(5,10,0), "=ROW()");
4373 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(5,10,0)));
4375 m_pDoc
->SetString(ScAddress(0,1,0), "=F11");
4376 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4378 // Insert 2 new rows at row 4.
4379 m_pDoc
->InsertRow(ScRange(0,3,0,m_pDoc
->MaxCol(),4,0));
4380 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc
->GetValue(ScAddress(5,12,0)));
4382 // The cell that references the moved cell should update its value as well.
4383 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4385 // Delete 2 rows to move it back.
4386 m_pDoc
->DeleteRow(ScRange(0,3,0,m_pDoc
->MaxCol(),4,0));
4388 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(5,10,0)));
4390 // The cell that references the moved cell should update its value as well.
4391 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4393 // Clear sheet and start over.
4394 clearSheet(m_pDoc
, 0);
4396 m_pDoc
->SetString(ScAddress(0,1,0), "=ROW(A5)");
4397 m_pDoc
->SetString(ScAddress(1,1,0), "=ROW(B5)");
4398 m_pDoc
->SetString(ScAddress(1,2,0), "=ROW(B6)");
4399 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4400 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
4401 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
4403 // B2:B3 should be shared.
4404 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(1,1,0));
4405 CPPUNIT_ASSERT(pFC
);
4406 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), pFC
->GetSharedTopRow());
4407 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedLength());
4409 // Insert a new row at row 4.
4410 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
4411 ScMarkData
aMark(MAXROW
, MAXCOL
);
4412 aMark
.SelectOneTable(0);
4413 rFunc
.InsertCells(ScRange(0,3,0,m_pDoc
->MaxCol(),3,0), &aMark
, INS_INSROWS_BEFORE
, false, true);
4414 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(0,1,0), "ROW(A6)", "Wrong formula!");
4415 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,1,0), "ROW(B6)", "Wrong formula!");
4416 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,2,0), "ROW(B7)", "Wrong formula!");
4418 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4419 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
4420 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
4422 m_pDoc
->DeleteTab(0);
4425 void Test::testFuncSUM()
4427 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4428 m_pDoc
->InsertTab (0, "foo"));
4430 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
4432 // Single argument case.
4433 m_pDoc
->SetValue(ScAddress(0,0,0), 1);
4434 m_pDoc
->SetValue(ScAddress(0,1,0), 1);
4435 m_pDoc
->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
4437 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
4439 // Multiple argument case.
4440 m_pDoc
->SetValue(ScAddress(0,0,0), 1);
4441 m_pDoc
->SetValue(ScAddress(0,1,0), 22);
4442 m_pDoc
->SetValue(ScAddress(0,2,0), 4);
4443 m_pDoc
->SetValue(ScAddress(0,3,0), 5);
4444 m_pDoc
->SetValue(ScAddress(0,4,0), 6);
4446 m_pDoc
->SetValue(ScAddress(1,0,0), 3);
4447 m_pDoc
->SetValue(ScAddress(1,1,0), 4);
4448 m_pDoc
->SetValue(ScAddress(1,2,0), 5);
4449 m_pDoc
->SetValue(ScAddress(1,3,0), 6);
4450 m_pDoc
->SetValue(ScAddress(1,4,0), 7);
4452 m_pDoc
->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
4453 m_pDoc
->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
4454 m_pDoc
->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
4455 CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
4456 CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
4457 CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
4459 // Clear and start over.
4460 clearRange(m_pDoc
, ScRange(0,0,0,3,m_pDoc
->MaxRow(),0));
4462 // SUM needs to take the first error in case the range contains an error.
4463 m_pDoc
->SetValue(ScAddress(0,0,0), 1.0);
4464 m_pDoc
->SetValue(ScAddress(0,1,0), 10.0);
4465 m_pDoc
->SetValue(ScAddress(0,2,0), 100.0);
4466 m_pDoc
->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
4467 CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
4469 // Set #DIV/0! error to A3. A4 should also inherit this error.
4470 m_pDoc
->SetString(ScAddress(0,2,0), "=1/0");
4471 FormulaError nErr
= m_pDoc
->GetErrCode(ScAddress(0,2,0));
4472 CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell should have a division by zero error.",
4473 int(FormulaError::DivisionByZero
), static_cast<int>(nErr
));
4474 nErr
= m_pDoc
->GetErrCode(ScAddress(0,3,0));
4475 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM should have also inherited a div-by-zero error.",
4476 int(FormulaError::DivisionByZero
), static_cast<int>(nErr
));
4478 // Set #NA! to A2. A4 should now inherit this error.
4479 m_pDoc
->SetString(ScAddress(0,1,0), "=NA()");
4480 nErr
= m_pDoc
->GetErrCode(ScAddress(0,1,0));
4481 CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr
!= FormulaError::NONE
);
4482 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 should have inherited the same error as A2.",
4483 static_cast<int>(nErr
), static_cast<int>(m_pDoc
->GetErrCode(ScAddress(0,3,0))));
4485 m_pDoc
->DeleteTab(0);
4488 void Test::testFuncPRODUCT()
4490 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto recalc.
4492 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc
->InsertTab(0, "foo"));
4494 ScAddress
aPos(3, 0, 0);
4495 m_pDoc
->SetValue(0, 0, 0, 3.0); // A1
4496 m_pDoc
->SetString(aPos
, "=PRODUCT(A1)");
4497 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 3.0, m_pDoc
->GetValue(aPos
));
4498 m_pDoc
->SetValue(0, 0, 0, -3.0); // A1
4499 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -3.0, m_pDoc
->GetValue(aPos
));
4500 m_pDoc
->SetString(aPos
, "=PRODUCT(B1)");
4501 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc
->GetValue(aPos
));
4502 m_pDoc
->SetValue(1, 0, 0, 10.0); // B1
4503 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 10.0, m_pDoc
->GetValue(aPos
));
4505 m_pDoc
->SetString(aPos
, "=PRODUCT(A1:C3)");
4506 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -30.0, m_pDoc
->GetValue(aPos
));
4507 m_pDoc
->SetValue(1, 1, 0, -1.0); // B2
4508 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 30.0, m_pDoc
->GetValue(aPos
));
4509 m_pDoc
->SetValue(2, 0, 0, 4.0); // C1
4510 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", 120.0, m_pDoc
->GetValue(aPos
));
4511 m_pDoc
->SetValue(0, 1, 0, -2.0); // A2
4512 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -240.0, m_pDoc
->GetValue(aPos
));
4513 m_pDoc
->SetValue(2, 1, 0, 8.0); // C2
4514 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT failed", -1920.0, m_pDoc
->GetValue(aPos
));
4515 m_pDoc
->SetValue(0, 2, 0, 0.2); // A3
4516 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -384.0, m_pDoc
->GetValue(aPos
), 10e-4);
4517 m_pDoc
->SetValue(1, 2, 0, -0.25); // B3
4518 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 96.0, m_pDoc
->GetValue(aPos
), 10e-4);
4519 m_pDoc
->SetValue(2, 2, 0, -0.125); // C3
4520 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", -12.0, m_pDoc
->GetValue(aPos
), 10e-4);
4521 m_pDoc
->SetValue(2, 2, 0, 0.0); // C3
4522 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of PRODUCT failed", 0.0, m_pDoc
->GetValue(aPos
), 10e-4);
4524 m_pDoc
->SetString(aPos
, "=PRODUCT({2;3;4})");
4525 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 24.0, m_pDoc
->GetValue(aPos
));
4526 m_pDoc
->SetString(aPos
, "=PRODUCT({2;-2;2})");
4527 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -8.0, m_pDoc
->GetValue(aPos
));
4528 m_pDoc
->SetString(aPos
, "=PRODUCT({8;0.125;-1})");
4529 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", -1.0, m_pDoc
->GetValue(aPos
));
4531 m_pDoc
->SetString(aPos
, "=PRODUCT({2;3};{4;5})");
4532 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 120.0, m_pDoc
->GetValue(aPos
));
4533 m_pDoc
->SetString(aPos
, "=PRODUCT({10;-8};{3;-1};{15;30};{7})");
4534 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 756000.0, m_pDoc
->GetValue(aPos
));
4535 m_pDoc
->SetString(aPos
, "=PRODUCT({10;-0.1;8};{0.125;4;0.25;2};{0.5};{1};{-1})");
4536 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of PRODUCT with inline array failed", 1.0, m_pDoc
->GetValue(aPos
));
4538 m_pDoc
->DeleteTab(0);
4541 void Test::testFuncSUMPRODUCT()
4543 m_pDoc
->InsertTab(0, "Test");
4545 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto recalc.
4547 ScAddress
aPos(0,0,0);
4548 m_pDoc
->SetString(aPos
, "=SUMPRODUCT(B1:B3;C1:C3)");
4549 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(aPos
));
4550 m_pDoc
->SetValue(ScAddress(2,0,0), 1.0); // C1
4551 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(aPos
));
4552 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0); // B1
4553 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(aPos
));
4554 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0); // B2
4555 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(aPos
));
4556 m_pDoc
->SetValue(ScAddress(2,1,0), 3.0); // C2
4557 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(aPos
));
4558 m_pDoc
->SetValue(ScAddress(2,2,0), -2.0); // C3
4559 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue(aPos
));
4560 m_pDoc
->SetValue(ScAddress(1,2,0), 5.0); // B3
4561 CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc
->GetValue(aPos
));
4563 // Force an error in C2 and test ForcedArray matrix error propagation.
4564 m_pDoc
->SetString( 2, 1, 0, "=1/0");
4565 FormulaError nError
= m_pDoc
->GetErrCode(aPos
);
4566 CPPUNIT_ASSERT_MESSAGE("Formula result should be a propagated error", nError
!= FormulaError::NONE
);
4568 // Test ForceArray propagation of SUMPRODUCT parameters to ABS and + operator.
4569 // => ABS({-3,4})*({-3,4}+{-3,4}) => {3,4}*{-6,8} => {-18,32} => 14
4570 m_pDoc
->SetValue(ScAddress(4,0,0), -3.0); // E1
4571 m_pDoc
->SetValue(ScAddress(4,1,0), 4.0); // E2
4572 // Non-intersecting formula in F3.
4573 m_pDoc
->SetString(ScAddress(5,2,0), "=SUMPRODUCT(ABS(E1:E2);E1:E2+E1:E2)");
4574 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc
->GetValue(ScAddress(5,2,0)));
4576 m_pDoc
->DeleteTab(0);
4579 void Test::testFuncSUMXMY2()
4581 m_pDoc
->InsertTab(0, "Test SumXMY2");
4583 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto recalc.
4585 ScAddress
aPos(0,0,0);
4586 m_pDoc
->SetString(aPos
, "=SUMXMY2(B1:B3;C1:C3)");
4587 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(aPos
));
4588 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0); // B1
4589 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc
->GetValue(aPos
));
4590 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0); // B2
4591 CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc
->GetValue(aPos
));
4592 m_pDoc
->SetValue(ScAddress(1,2,0), 3.0); // B3
4593 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc
->GetValue(aPos
));
4594 m_pDoc
->SetValue(ScAddress(2,0,0), -1.0); // C1
4595 CPPUNIT_ASSERT_EQUAL(17.0, m_pDoc
->GetValue(aPos
));
4596 m_pDoc
->SetValue(ScAddress(2,1,0), 3.0); // C2
4597 CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc
->GetValue(aPos
));
4598 m_pDoc
->SetValue(ScAddress(2,2,0), 1.0); // C3
4599 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(aPos
));
4601 double result
= 0.0;
4602 m_pDoc
->SetString(0, 4, 0, "=SUMXMY2({2;3;4};{4;3;2})");
4603 m_pDoc
->GetValue(0, 4, 0, result
);
4604 ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of SUMXMY2 with inline arrays failed", 8.0, result
);
4606 m_pDoc
->DeleteTab(0);
4609 void Test::testFuncMIN()
4611 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto recalc.
4612 m_pDoc
->InsertTab(0, "Formula");
4615 m_pDoc
->SetString(ScAddress(0,0,0), "a");
4616 m_pDoc
->SetString(ScAddress(0,1,0), "b");
4619 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0);
4620 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0);
4623 ScMarkData
aMark(MAXROW
, MAXCOL
);
4624 aMark
.SelectOneTable(0);
4625 m_pDoc
->InsertMatrixFormula(2, 0, 2, 1, aMark
, "=MIN(IF(A1:A2=\"c\";B1:B2))");
4627 // Formula cell in C1:C2 should be a 1x2 matrix array.
4628 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(2,0,0));
4629 CPPUNIT_ASSERT(pFC
);
4630 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be an array.", ScMatrixMode::Formula
, pFC
->GetMatrixFlag());
4634 pFC
->GetMatColsRows(nCols
, nRows
);
4635 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), nCols
);
4636 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), nRows
);
4638 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C1 is invalid.", 0, static_cast<int>(m_pDoc
->GetErrCode(ScAddress(2,0,0))));
4639 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula in C2 is invalid.", 0, static_cast<int>(m_pDoc
->GetErrCode(ScAddress(2,1,0))));
4641 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
4642 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
4644 // Inline array input (A4).
4645 m_pDoc
->SetString(ScAddress(0,3,0), "=MIN({-2;4;3})");
4646 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
4648 // Add more values to B3:B4.
4649 m_pDoc
->SetValue(ScAddress(1,2,0), 20.0);
4650 m_pDoc
->SetValue(ScAddress(1,3,0), -20.0);
4652 // Get the MIN of B1:B4.
4653 m_pDoc
->SetString(ScAddress(2,4,0), "=MIN(B1:B4)");
4654 CPPUNIT_ASSERT_EQUAL(-20.0, m_pDoc
->GetValue(ScAddress(2,4,0)));
4656 m_pDoc
->DeleteTab(0);
4659 void Test::testFuncN()
4661 OUString
const aTabName("foo");
4662 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4663 m_pDoc
->InsertTab (0, aTabName
));
4667 // Clear the area first.
4668 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, 20, 0));
4670 // Put values to reference.
4672 m_pDoc
->SetValue(0, 0, 0, val
);
4673 m_pDoc
->SetString(0, 2, 0, "Text");
4675 m_pDoc
->SetValue(0, 3, 0, val
);
4677 m_pDoc
->SetValue(0, 4, 0, val
);
4679 m_pDoc
->SetValue(0, 5, 0, val
);
4680 m_pDoc
->SetString(0, 6, 0, "'12.3");
4683 m_pDoc
->SetString(1, 0, 0, "=N(A1)");
4684 m_pDoc
->SetString(1, 1, 0, "=N(A2)");
4685 m_pDoc
->SetString(1, 2, 0, "=N(A3)");
4686 m_pDoc
->SetString(1, 3, 0, "=N(A4)");
4687 m_pDoc
->SetString(1, 4, 0, "=N(A5)");
4688 m_pDoc
->SetString(1, 5, 0, "=N(A6)");
4689 m_pDoc
->SetString(1, 6, 0, "=N(A9)");
4692 m_pDoc
->SetString(1, 7, 0, "=N(0)");
4693 m_pDoc
->SetString(1, 8, 0, "=N(1)");
4694 m_pDoc
->SetString(1, 9, 0, "=N(-1)");
4695 m_pDoc
->SetString(1, 10, 0, "=N(123)");
4696 m_pDoc
->SetString(1, 11, 0, "=N(\"\")");
4697 m_pDoc
->SetString(1, 12, 0, "=N(\"12\")");
4698 m_pDoc
->SetString(1, 13, 0, "=N(\"foo\")");
4701 m_pDoc
->SetString(2, 2, 0, "=N(A1:A8)");
4702 m_pDoc
->SetString(2, 3, 0, "=N(A1:A8)");
4703 m_pDoc
->SetString(2, 4, 0, "=N(A1:A8)");
4704 m_pDoc
->SetString(2, 5, 0, "=N(A1:A8)");
4706 // Calculate and check the results.
4708 double checks1
[] = {
4709 0, 0, 0, 1, -1, 12.3, 0, // cell reference
4710 0, 1, -1, 123, 0, 0, 0 // in-line values
4712 for (size_t i
= 0; i
< SAL_N_ELEMENTS(checks1
); ++i
)
4714 m_pDoc
->GetValue(1, i
, 0, result
);
4715 bool bGood
= result
== checks1
[i
];
4718 cerr
<< "row " << (i
+1) << ": expected=" << checks1
[i
] << " actual=" << result
<< endl
;
4719 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4722 double checks2
[] = {
4723 0, 1, -1, 12.3 // range references
4725 for (size_t i
= 0; i
< SAL_N_ELEMENTS(checks2
); ++i
)
4727 m_pDoc
->GetValue(1, i
+2, 0, result
);
4728 bool bGood
= result
== checks2
[i
];
4731 cerr
<< "row " << (i
+2+1) << ": expected=" << checks2
[i
] << " actual=" << result
<< endl
;
4732 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
4736 m_pDoc
->DeleteTab(0);
4739 void Test::testFuncCOUNTIF()
4741 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4743 // COUNTIF (test case adopted from OOo i#36381)
4745 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4746 m_pDoc
->InsertTab (0, "foo"));
4748 // Empty A1:A39 first.
4749 clearRange(m_pDoc
, ScRange(0, 0, 0, 0, 40, 0));
4751 // Raw data (rows 1 through 9)
4752 const char* aData
[] = {
4764 SCROW nRows
= SAL_N_ELEMENTS(aData
);
4765 for (SCROW i
= 0; i
< nRows
; ++i
)
4766 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
]));
4768 printRange(m_pDoc
, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
4770 // formulas and results
4771 static const struct {
4772 const char* pFormula
; double fResult
;
4774 { "=COUNTIF(A1:A12;1999)", 1 },
4775 { "=COUNTIF(A1:A12;2002)", 2 },
4776 { "=COUNTIF(A1:A12;1998)", 0 },
4777 { "=COUNTIF(A1:A12;\">=1999\")", 5 },
4778 { "=COUNTIF(A1:A12;\">1999\")", 4 },
4779 { "=COUNTIF(A1:A12;\"<2001\")", 5 },
4780 { "=COUNTIF(A1:A12;\">0\")", 5 },
4781 { "=COUNTIF(A1:A12;\">=0\")", 8 },
4782 { "=COUNTIF(A1:A12;0)", 3 },
4783 { "=COUNTIF(A1:A12;\"X\")", 1 },
4784 { "=COUNTIF(A1:A12;)", 3 }
4787 nRows
= SAL_N_ELEMENTS(aChecks
);
4788 for (SCROW i
= 0; i
< nRows
; ++i
)
4790 SCROW nRow
= 20 + i
;
4791 m_pDoc
->SetString(0, nRow
, 0, OUString::createFromAscii(aChecks
[i
].pFormula
));
4794 for (SCROW i
= 0; i
< nRows
; ++i
)
4797 SCROW nRow
= 20 + i
;
4798 m_pDoc
->GetValue(0, nRow
, 0, result
);
4799 bool bGood
= result
== aChecks
[i
].fResult
;
4802 cerr
<< "row " << (nRow
+1) << ": formula" << aChecks
[i
].pFormula
4803 << " expected=" << aChecks
[i
].fResult
<< " actual=" << result
<< endl
;
4804 CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
4808 // Don't count empty strings when searching for a number.
4811 clearRange(m_pDoc
, ScRange(0, 0, 0, 0, 1, 0));
4813 m_pDoc
->SetString(0, 0, 0, "=\"\"");
4814 m_pDoc
->SetString(0, 1, 0, "=COUNTIF(A1;1)");
4816 double result
= m_pDoc
->GetValue(0, 1, 0);
4817 ASSERT_DOUBLES_EQUAL_MESSAGE("We shouldn't count empty string as valid number.", 0.0, result
);
4819 // Another test case adopted from fdo#77039.
4820 clearSheet(m_pDoc
, 0);
4822 // Set formula cells with blank results in A1:A4.
4823 for (SCROW i
= 0; i
<=3; ++i
)
4824 m_pDoc
->SetString(ScAddress(0,i
,0), "=\"\"");
4826 // Insert formula into A5 to count all cells with empty strings.
4827 m_pDoc
->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
4829 // We should correctly count with empty string key.
4830 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
4832 // Another test case adopted from tdf#99291, empty array elements should
4833 // not match empty cells, but cells with 0.
4834 clearSheet(m_pDoc
, 0);
4835 ScMarkData
aMark(MAXROW
, MAXCOL
);
4836 aMark
.SelectOneTable(0);
4837 m_pDoc
->InsertMatrixFormula(0,0, 0,1, aMark
, "=COUNTIF(B1:B5;C1:C2)");
4838 // As we will be testing for 0.0 values, check that formulas are actually present.
4840 m_pDoc
->GetFormula(0,0,0, aFormula
);
4841 CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula
);
4842 m_pDoc
->GetFormula(0,1,0, aFormula
);
4843 CPPUNIT_ASSERT_EQUAL(OUString("{=COUNTIF(B1:B5;C1:C2)}"), aFormula
);
4844 // The 0.0 results expected.
4845 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(0,0,0)));
4846 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4847 // 0.0 in B2, 1.0 in B3 and B4
4848 m_pDoc
->SetValue( ScAddress(1,1,0), 0.0);
4849 m_pDoc
->SetValue( ScAddress(1,2,0), 1.0);
4850 m_pDoc
->SetValue( ScAddress(1,3,0), 1.0);
4851 // Matched by 0.0 produced by empty cell in array, and 1.0 in C2.
4852 m_pDoc
->SetValue( ScAddress(2,1,0), 1.0);
4853 CPPUNIT_ASSERT_EQUAL_MESSAGE("One cell with 0.0", 1.0, m_pDoc
->GetValue(ScAddress(0,0,0)));
4854 CPPUNIT_ASSERT_EQUAL_MESSAGE("Two cells with 1.0", 2.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
4856 m_pDoc
->DeleteTab(0);
4859 void Test::testFuncIF()
4861 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4863 m_pDoc
->InsertTab(0, "Formula");
4865 m_pDoc
->SetString(ScAddress(0,0,0), "=IF(B1=2;\"two\";\"not two\")");
4866 CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc
->GetString(ScAddress(0,0,0)));
4867 m_pDoc
->SetValue(ScAddress(1,0,0), 2.0);
4868 CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc
->GetString(ScAddress(0,0,0)));
4869 m_pDoc
->SetValue(ScAddress(1,0,0), 3.0);
4870 CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc
->GetString(ScAddress(0,0,0)));
4872 // Test nested IF in array/matrix if the nested IF condition is a scalar.
4873 ScMarkData
aMark(MAXROW
, MAXCOL
);
4874 aMark
.SelectOneTable(0);
4875 m_pDoc
->InsertMatrixFormula(0,2, 1,2, aMark
, "=IF({1;0};IF(1;23);42)");
4876 // Results must be 23 and 42.
4877 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
4878 CPPUNIT_ASSERT_EQUAL(42.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
4880 // Test nested IF in array/matrix if nested IF conditions are range
4881 // references, data in A5:C8, matrix formula in D4 so there is no
4882 // implicit intersection between formula and ranges.
4884 const char* aData
[][3] = {
4890 ScAddress
aPos(0,4,0);
4891 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
4892 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
4894 m_pDoc
->InsertMatrixFormula(3,3, 3,3, aMark
, "=SUM(IF(A5:A8;IF(B5:B8;C5:C8;0);0))");
4895 // Result must be 16, only the first row matches all criteria.
4896 CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
4899 // Test nested IF in array/matrix if the nested IF has no Else path.
4900 m_pDoc
->InsertMatrixFormula(0,10, 1,10, aMark
, "=IF(IF({1;0};12);34;56)");
4901 // Results must be 34 and 56.
4902 CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc
->GetValue(ScAddress(0,10,0)));
4903 CPPUNIT_ASSERT_EQUAL(56.0, m_pDoc
->GetValue(ScAddress(1,10,0)));
4905 m_pDoc
->DeleteTab(0);
4908 void Test::testFuncCHOOSE()
4910 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
4912 m_pDoc
->InsertTab(0, "Formula");
4914 m_pDoc
->SetString(ScAddress(0,0,0), "=CHOOSE(B1;\"one\";\"two\";\"three\")");
4915 FormulaError nError
= m_pDoc
->GetErrCode(ScAddress(0,0,0));
4916 CPPUNIT_ASSERT_MESSAGE("Formula result should be an error since B1 is still empty.", nError
!= FormulaError::NONE
);
4917 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0);
4918 CPPUNIT_ASSERT_EQUAL(OUString("one"), m_pDoc
->GetString(ScAddress(0,0,0)));
4919 m_pDoc
->SetValue(ScAddress(1,0,0), 2.0);
4920 CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc
->GetString(ScAddress(0,0,0)));
4921 m_pDoc
->SetValue(ScAddress(1,0,0), 3.0);
4922 CPPUNIT_ASSERT_EQUAL(OUString("three"), m_pDoc
->GetString(ScAddress(0,0,0)));
4923 m_pDoc
->SetValue(ScAddress(1,0,0), 4.0);
4924 nError
= m_pDoc
->GetErrCode(ScAddress(0,0,0));
4925 CPPUNIT_ASSERT_MESSAGE("Formula result should be an error due to out-of-bound input..", nError
!= FormulaError::NONE
);
4927 m_pDoc
->DeleteTab(0);
4930 void Test::testFuncIFERROR()
4932 // IFERROR/IFNA (fdo#56124)
4934 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
4935 m_pDoc
->InsertTab (0, "foo"));
4937 // Empty A1:A39 first.
4938 clearRange(m_pDoc
, ScRange(0, 0, 0, 0, 40, 0));
4940 // Raw data (rows 1 through 12)
4941 const char* aData
[] = {
4956 SCROW nRows
= SAL_N_ELEMENTS(aData
);
4957 for (SCROW i
= 0; i
< nRows
; ++i
)
4958 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
]));
4960 printRange(m_pDoc
, ScRange(0, 0, 0, 0, nRows
-1, 0), "data range for IFERROR/IFNA");
4962 // formulas and results
4963 static const struct {
4964 const char* pFormula
; const char* pResult
;
4966 { "=IFERROR(A1;9)", "1" },
4967 { "=IFERROR(A2;9)", "e" },
4968 { "=IFERROR(A3;9)", "2" },
4969 { "=IFERROR(A4;-7)", "-7" },
4970 { "=IFERROR(A5;-7)", "-7" },
4971 { "=IFERROR(A6;-7)", "-7" },
4972 { "=IFERROR(A7;-7)", "-7" },
4973 { "=IFNA(A6;9)", "#DIV/0!" },
4974 { "=IFNA(A7;-7)", "-7" },
4975 { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)", "4" },
4976 { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
4977 { "{=IFERROR(3*A11:A12;1998)}[0]", "1998" }, // um... this is not the correct way to insert a
4978 { "{=IFERROR(3*A11:A12;1998)}[1]", "69" } // matrix formula, just a place holder, see below
4981 nRows
= SAL_N_ELEMENTS(aChecks
);
4982 for (SCROW i
= 0; i
< nRows
-2; ++i
)
4984 SCROW nRow
= 20 + i
;
4985 m_pDoc
->SetString(0, nRow
, 0, OUString::createFromAscii(aChecks
[i
].pFormula
));
4988 // Create a matrix range in last two rows of the range above, actual data
4989 // of the placeholders.
4990 ScMarkData
aMark(MAXROW
, MAXCOL
);
4991 aMark
.SelectOneTable(0);
4992 m_pDoc
->InsertMatrixFormula(0, 20 + nRows
-2, 0, 20 + nRows
-1, aMark
, "=IFERROR(3*A11:A12;1998)");
4996 for (SCROW i
= 0; i
< nRows
; ++i
)
4998 SCROW nRow
= 20 + i
;
4999 OUString aResult
= m_pDoc
->GetString(0, nRow
, 0);
5000 CPPUNIT_ASSERT_EQUAL_MESSAGE(
5001 aChecks
[i
].pFormula
, OUString::createFromAscii( aChecks
[i
].pResult
), aResult
);
5004 const SCCOL nCols
= 3;
5005 const char* aData2
[][nCols
] = {
5007 { "4", "=1/0", "6" },
5010 const char* aCheck2
[][nCols
] = {
5012 { "4", "Error","6" },
5017 ScAddress
aPos(2,0,0);
5018 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData2
, SAL_N_ELEMENTS(aData2
));
5019 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
5021 // Array formula in F4:H6
5022 const SCROW nElems2
= SAL_N_ELEMENTS(aCheck2
);
5023 const SCCOL nStartCol
= aPos
.Col() + nCols
;
5024 const SCROW nStartRow
= aPos
.Row() + nElems2
;
5025 m_pDoc
->InsertMatrixFormula( nStartCol
, nStartRow
, nStartCol
+nCols
-1, nStartRow
+nElems2
-1, aMark
,
5026 "=IFERROR(C1:E3;\"Error\")");
5030 for (SCCOL nCol
= nStartCol
; nCol
< nStartCol
+ nCols
; ++nCol
)
5032 for (SCROW nRow
= nStartRow
; nRow
< nStartRow
+ nElems2
; ++nRow
)
5034 OUString aResult
= m_pDoc
->GetString( nCol
, nRow
, 0);
5035 CPPUNIT_ASSERT_EQUAL_MESSAGE( "IFERROR array result",
5036 OUString::createFromAscii( aCheck2
[nRow
-nStartRow
][nCol
-nStartCol
]), aResult
);
5040 m_pDoc
->DeleteTab(0);
5043 void Test::testFuncSHEET()
5045 OUString
const aTabName1("test1");
5046 OUString
const aTabName2("test2");
5047 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5048 m_pDoc
->InsertTab (SC_TAB_APPEND
, aTabName1
));
5050 m_pDoc
->SetString(0, 0, 0, "=SHEETS()");
5051 m_pDoc
->CalcFormulaTree(false, false);
5053 m_pDoc
->GetValue(0, 0, 0, original
);
5055 CPPUNIT_ASSERT_EQUAL_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
5056 static_cast<SCTAB
>(original
), m_pDoc
->GetTableCount());
5058 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5059 m_pDoc
->InsertTab (SC_TAB_APPEND
, aTabName2
));
5062 m_pDoc
->GetValue(0, 0, 0, modified
);
5063 ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
5064 1.0, modified
- original
);
5066 SCTAB nTabCount
= m_pDoc
->GetTableCount();
5067 m_pDoc
->DeleteTab(--nTabCount
);
5069 m_pDoc
->GetValue(0, 0, 0, modified
);
5070 ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
5071 0.0, modified
- original
);
5073 m_pDoc
->DeleteTab(--nTabCount
);
5076 void Test::testFuncNOW()
5078 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5079 m_pDoc
->InsertTab (0, "foo"));
5082 m_pDoc
->SetValue(0, 0, 0, val
);
5083 m_pDoc
->SetString(0, 1, 0, "=IF(A1>0;NOW();0");
5085 m_pDoc
->GetValue(0, 1, 0, now1
);
5086 CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1
> 0.0);
5089 m_pDoc
->SetValue(0, 0, 0, val
);
5090 m_pDoc
->CalcFormulaTree(false, false);
5092 m_pDoc
->GetValue(0, 1, 0, zero
);
5093 ASSERT_DOUBLES_EQUAL_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", 0.0, zero
);
5096 m_pDoc
->SetValue(0, 0, 0, val
);
5097 m_pDoc
->CalcFormulaTree(false, false);
5099 m_pDoc
->GetValue(0, 1, 0, now2
);
5100 CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2
- now1
) >= 0.0);
5102 m_pDoc
->DeleteTab(0);
5105 void Test::testFuncNUMBERVALUE()
5107 // NUMBERVALUE fdo#57180
5109 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5110 m_pDoc
->InsertTab (0, "foo"));
5112 // Empty A1:A39 first.
5113 clearRange(m_pDoc
, ScRange(0, 0, 0, 0, 40, 0));
5115 // Raw data (rows 1 through 6)
5116 const char* aData
[] = {
5118 "1ag34 5g g6 78b9%%",
5125 SCROW nRows
= SAL_N_ELEMENTS(aData
);
5126 for (SCROW i
= 0; i
< nRows
; ++i
)
5127 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
]));
5129 printRange(m_pDoc
, ScRange(0, 0, 0, 0, nRows
- 1, 0), "data range for NUMBERVALUE");
5131 // formulas and results
5132 static const struct {
5133 const char* pFormula
; const char* pResult
;
5135 { "=NUMBERVALUE(A1;\"b\";\"ag\")", "199.9" },
5136 { "=NUMBERVALUE(A2;\"b\";\"ag\")", "134.56789" },
5137 { "=NUMBERVALUE(A2;\"b\";\"g\")", "#VALUE!" },
5138 { "=NUMBERVALUE(A3;\"d\")", "12.3456" },
5139 { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
5140 { "=NUMBERVALUE(A4;)", "Err:502" },
5141 { "=NUMBERVALUE(A5;)", "Err:502" },
5142 { "=NUMBERVALUE(A6;\"b\";\"a\")", "1.23" }
5145 nRows
= SAL_N_ELEMENTS(aChecks
);
5146 for (SCROW i
= 0; i
< nRows
; ++i
)
5148 SCROW nRow
= 20 + i
;
5149 m_pDoc
->SetString(0, nRow
, 0, OUString::createFromAscii(aChecks
[i
].pFormula
));
5153 for (SCROW i
= 0; i
< nRows
; ++i
)
5155 SCROW nRow
= 20 + i
;
5156 OUString aResult
= m_pDoc
->GetString(0, nRow
, 0);
5157 CPPUNIT_ASSERT_EQUAL_MESSAGE(
5158 aChecks
[i
].pFormula
, OUString::createFromAscii( aChecks
[i
].pResult
), aResult
);
5161 m_pDoc
->DeleteTab(0);
5164 void Test::testFuncLEN()
5166 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
5168 m_pDoc
->InsertTab(0, "Formula");
5170 // Leave A1:A3 empty, and insert an array of LEN in B1:B3 that references
5171 // these empty cells.
5173 ScMarkData
aMark(MAXROW
, MAXCOL
);
5174 aMark
.SelectOneTable(0);
5175 m_pDoc
->InsertMatrixFormula(1, 0, 1, 2, aMark
, "=LEN(A1:A3)");
5177 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(1,0,0));
5178 CPPUNIT_ASSERT(pFC
);
5179 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be a matrix origin.",
5180 ScMatrixMode::Formula
, pFC
->GetMatrixFlag());
5182 // This should be a 1x3 matrix.
5185 pFC
->GetMatColsRows(nCols
, nRows
);
5186 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL
>(1), nCols
);
5187 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(3), nRows
);
5189 // LEN value should be 0 for an empty cell.
5190 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
5191 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
5192 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
5194 m_pDoc
->DeleteTab(0);
5197 void Test::testFuncLOOKUP()
5199 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
5201 m_pDoc
->InsertTab(0, "Test");
5204 const char* aData
[][2] = {
5205 { "=CONCATENATE(\"A\")", "1" },
5206 { "=CONCATENATE(\"B\")", "2" },
5207 { "=CONCATENATE(\"C\")", "3" },
5208 { nullptr, nullptr } // terminator
5211 // Insert raw data into A1:B3.
5212 for (SCROW i
= 0; aData
[i
][0]; ++i
)
5214 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
][0]));
5215 m_pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aData
[i
][1]));
5218 const char* aData2
[][2] = {
5219 { "A", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5220 { "B", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5221 { "C", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
5222 { nullptr, nullptr } // terminator
5225 // Insert check formulas into A5:B7.
5226 for (SCROW i
= 0; aData2
[i
][0]; ++i
)
5228 m_pDoc
->SetString(0, i
+4, 0, OUString::createFromAscii(aData2
[i
][0]));
5229 m_pDoc
->SetString(1, i
+4, 0, OUString::createFromAscii(aData2
[i
][1]));
5232 printRange(m_pDoc
, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
5234 // Values for B5:B7 should be 1, 2, and 3.
5235 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc
->GetErrCode(ScAddress(1,4,0))));
5236 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc
->GetErrCode(ScAddress(1,5,0))));
5237 CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc
->GetErrCode(ScAddress(1,6,0))));
5239 ASSERT_DOUBLES_EQUAL(1.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
5240 ASSERT_DOUBLES_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,5,0)));
5241 ASSERT_DOUBLES_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(1,6,0)));
5243 m_pDoc
->DeleteTab(0);
5246 void Test::testFuncLOOKUParrayWithError()
5248 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true);
5249 m_pDoc
->InsertTab(0, "Test");
5251 std::vector
<std::vector
<const char*>> aData
= {
5255 insertRangeData(m_pDoc
, ScAddress(2,1,0), aData
); // C2:E3
5256 m_pDoc
->SetString(0,0,0, "=LOOKUP(2;1/(C2:E2<>\"\");C3:E3)"); // A1
5258 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last column.", OUString("c"), m_pDoc
->GetString(0,0,0));
5259 m_pDoc
->SetString(4,1,0, ""); // E2
5260 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for second last column.", OUString("b"), m_pDoc
->GetString(0,0,0));
5262 m_pDoc
->SetString(6,1,0, "one"); // G2
5263 m_pDoc
->SetString(6,5,0, "two"); // G6
5264 // Creates an interim array {1,#DIV/0!,#DIV/0!,#DIV/0!,1,#DIV/0!,#DIV/0!,#DIV/0!}
5265 m_pDoc
->SetString(7,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(G2:G9)));G2:G9)"); // H9
5266 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last row.", OUString("two"), m_pDoc
->GetString(7,8,0));
5268 // Lookup on empty range.
5269 m_pDoc
->SetString(9,8,0, "=LOOKUP(2;1/(NOT(ISBLANK(I2:I9)));I2:I9)"); // J9
5270 CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find no match.", OUString("#N/A"), m_pDoc
->GetString(9,8,0));
5272 m_pDoc
->DeleteTab(0);
5275 void Test::testFuncVLOOKUP()
5279 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5280 m_pDoc
->InsertTab (0, "foo"));
5283 clearRange(m_pDoc
, ScRange(0, 0, 0, 5, 39, 0));
5286 const char* aData
[][2] = {
5301 { nullptr, nullptr } // terminator
5304 // Insert raw data into A1:B14.
5305 for (SCROW i
= 0; aData
[i
][0]; ++i
)
5307 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
][0]));
5308 m_pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aData
[i
][1]));
5311 printRange(m_pDoc
, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
5314 static const struct {
5315 const char* pLookup
; const char* pFormula
; const char* pRes
;
5317 { "Lookup", "Formula", nullptr },
5318 { "12", "=VLOOKUP(D2;A2:B14;2;1)", "3" },
5319 { "29", "=VLOOKUP(D3;A2:B14;2;1)", "4" },
5320 { "31", "=VLOOKUP(D4;A2:B14;2;1)", "5" },
5321 { "45", "=VLOOKUP(D5;A2:B14;2;1)", "6" },
5322 { "56", "=VLOOKUP(D6;A2:B14;2;1)", "7" },
5323 { "65", "=VLOOKUP(D7;A2:B14;2;1)", "8" },
5324 { "78", "=VLOOKUP(D8;A2:B14;2;1)", "9" },
5325 { "Andy", "=VLOOKUP(D9;A2:B14;2;1)", "#N/A" },
5326 { "Bruce", "=VLOOKUP(D10;A2:B14;2;1)", "11" },
5327 { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)", "12" },
5328 { "David", "=VLOOKUP(D12;A2:B14;2;1)", "13" },
5329 { "Edward", "=VLOOKUP(D13;A2:B14;2;1)", "14" },
5330 { "Frank", "=VLOOKUP(D14;A2:B14;2;1)", "15" },
5331 { "Henry", "=VLOOKUP(D15;A2:B14;2;1)", "15" },
5332 { "100", "=VLOOKUP(D16;A2:B14;2;1)", "9" },
5333 { "1000", "=VLOOKUP(D17;A2:B14;2;1)", "9" },
5334 { "Zena", "=VLOOKUP(D18;A2:B14;2;1)", "15" }
5337 // Insert formula data into D1:E18.
5338 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5340 m_pDoc
->SetString(3, i
, 0, OUString::createFromAscii(aChecks
[i
].pLookup
));
5341 m_pDoc
->SetString(4, i
, 0, OUString::createFromAscii(aChecks
[i
].pFormula
));
5344 printRange(m_pDoc
, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
5347 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5350 // Skip the header row.
5353 OUString aRes
= m_pDoc
->GetString(4, i
, 0);
5354 bool bGood
= aRes
.equalsAscii(aChecks
[i
].pRes
);
5357 cerr
<< "row " << (i
+1) << ": lookup value='" << aChecks
[i
].pLookup
5358 << "' expected='" << aChecks
[i
].pRes
<< "' actual='" << aRes
<< "'" << endl
;
5359 CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
5363 // Clear the sheet and start over.
5364 clearSheet(m_pDoc
, 0);
5366 // Lookup on sorted data interspersed with empty cells.
5368 // A1:B8 is the search range.
5369 m_pDoc
->SetValue(ScAddress(0,2,0), 1.0);
5370 m_pDoc
->SetValue(ScAddress(0,4,0), 2.0);
5371 m_pDoc
->SetValue(ScAddress(0,7,0), 4.0);
5372 m_pDoc
->SetString(ScAddress(1,2,0), "One");
5373 m_pDoc
->SetString(ScAddress(1,4,0), "Two");
5374 m_pDoc
->SetString(ScAddress(1,7,0), "Four");
5376 // D1:D5 contain match values.
5377 m_pDoc
->SetValue(ScAddress(3,0,0), 1.0);
5378 m_pDoc
->SetValue(ScAddress(3,1,0), 2.0);
5379 m_pDoc
->SetValue(ScAddress(3,2,0), 3.0);
5380 m_pDoc
->SetValue(ScAddress(3,3,0), 4.0);
5381 m_pDoc
->SetValue(ScAddress(3,4,0), 5.0);
5383 // E1:E5 contain formulas.
5384 m_pDoc
->SetString(ScAddress(4,0,0), "=VLOOKUP(D1;$A$1:$B$8;2)");
5385 m_pDoc
->SetString(ScAddress(4,1,0), "=VLOOKUP(D2;$A$1:$B$8;2)");
5386 m_pDoc
->SetString(ScAddress(4,2,0), "=VLOOKUP(D3;$A$1:$B$8;2)");
5387 m_pDoc
->SetString(ScAddress(4,3,0), "=VLOOKUP(D4;$A$1:$B$8;2)");
5388 m_pDoc
->SetString(ScAddress(4,4,0), "=VLOOKUP(D5;$A$1:$B$8;2)");
5391 // Check the formula results in E1:E5.
5392 CPPUNIT_ASSERT_EQUAL(OUString("One"), m_pDoc
->GetString(ScAddress(4,0,0)));
5393 CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc
->GetString(ScAddress(4,1,0)));
5394 CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc
->GetString(ScAddress(4,2,0)));
5395 CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc
->GetString(ScAddress(4,3,0)));
5396 CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc
->GetString(ScAddress(4,4,0)));
5398 // Start over again.
5399 clearSheet(m_pDoc
, 0);
5401 // Set A,B,...,G to A1:A7.
5402 m_pDoc
->SetString(ScAddress(0,0,0), "A");
5403 m_pDoc
->SetString(ScAddress(0,1,0), "B");
5404 m_pDoc
->SetString(ScAddress(0,2,0), "C");
5405 m_pDoc
->SetString(ScAddress(0,3,0), "D");
5406 m_pDoc
->SetString(ScAddress(0,4,0), "E");
5407 m_pDoc
->SetString(ScAddress(0,5,0), "F");
5408 m_pDoc
->SetString(ScAddress(0,6,0), "G");
5410 // Set the formula in C1.
5411 m_pDoc
->SetString(ScAddress(2,0,0), "=VLOOKUP(\"C\";A1:A16;1)");
5412 CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc
->GetString(ScAddress(2,0,0)));
5415 // A21:E24, test position dependent implicit intersection as argument to a
5416 // scalar value parameter in a function that has a ReferenceOrForceArray
5417 // type parameter somewhere else and formula is not in array mode,
5418 // VLOOKUP(Value;ReferenceOrForceArray;...)
5419 const char* aData2
[][5] = {
5420 { "1", "one", "3", "=VLOOKUP(C21:C24;A21:B24;2;0)", "three" },
5421 { "2", "two", "1", "=VLOOKUP(C21:C24;A21:B24;2;0)", "one" },
5422 { "3", "three", "4", "=VLOOKUP(C21:C24;A21:B24;2;0)", "four" },
5423 { "4", "four", "2", "=VLOOKUP(C21:C24;A21:B24;2;0)", "two" }
5426 ScAddress
aPos2(0,20,0);
5427 ScRange aRange2
= insertRangeData(m_pDoc
, aPos2
, aData2
, SAL_N_ELEMENTS(aData2
));
5428 CPPUNIT_ASSERT_EQUAL(aPos2
, aRange2
.aStart
);
5430 aPos2
.SetCol(3); // column D formula results
5431 for (size_t i
=0; i
< SAL_N_ELEMENTS(aData2
); ++i
)
5433 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aData2
[i
][4]), m_pDoc
->GetString(aPos2
));
5437 m_pDoc
->DeleteTab(0);
5440 struct StrStrCheck
{
5445 template<size_t DataSize
, size_t FormulaSize
, int Type
>
5446 static void runTestMATCH(ScDocument
* pDoc
, const char* aData
[DataSize
], const StrStrCheck aChecks
[FormulaSize
])
5448 size_t nDataSize
= DataSize
;
5449 for (size_t i
= 0; i
< nDataSize
; ++i
)
5450 pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
]));
5452 for (size_t i
= 0; i
< FormulaSize
; ++i
)
5454 pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aChecks
[i
].pVal
));
5456 OUStringBuffer aBuf
;
5457 aBuf
.append("=MATCH(B");
5458 aBuf
.append(static_cast<sal_Int32
>(i
+1));
5459 aBuf
.append(";A1:A");
5460 aBuf
.append(static_cast<sal_Int32
>(nDataSize
));
5462 aBuf
.append(static_cast<sal_Int32
>(Type
));
5464 OUString aFormula
= aBuf
.makeStringAndClear();
5465 pDoc
->SetString(2, i
, 0, aFormula
);
5469 Test::printRange(pDoc
, ScRange(0, 0, 0, 2, FormulaSize
-1, 0), "MATCH");
5471 // verify the results.
5472 for (size_t i
= 0; i
< FormulaSize
; ++i
)
5474 OUString aStr
= pDoc
->GetString(2, i
, 0);
5475 if (!aStr
.equalsAscii(aChecks
[i
].pRes
))
5477 cerr
<< "row " << (i
+1) << ": expected='" << aChecks
[i
].pRes
<< "' actual='" << aStr
<< "'"
5478 " criterion='" << aChecks
[i
].pVal
<< "'" << endl
;
5479 CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
5484 template<size_t DataSize
, size_t FormulaSize
, int Type
>
5485 static void runTestHorizontalMATCH(ScDocument
* pDoc
, const char* aData
[DataSize
], const StrStrCheck aChecks
[FormulaSize
])
5487 size_t nDataSize
= DataSize
;
5488 for (size_t i
= 0; i
< nDataSize
; ++i
)
5489 pDoc
->SetString(i
, 0, 0, OUString::createFromAscii(aData
[i
]));
5491 for (size_t i
= 0; i
< FormulaSize
; ++i
)
5493 pDoc
->SetString(i
, 1, 0, OUString::createFromAscii(aChecks
[i
].pVal
));
5495 // Assume we don't have more than 26 data columns...
5496 OUStringBuffer aBuf
;
5497 aBuf
.append("=MATCH(");
5498 aBuf
.append(static_cast<sal_Unicode
>('A'+i
));
5499 aBuf
.append("2;A1:");
5500 aBuf
.append(static_cast<sal_Unicode
>('A'+nDataSize
));
5502 aBuf
.append(static_cast<sal_Int32
>(Type
));
5504 OUString aFormula
= aBuf
.makeStringAndClear();
5505 pDoc
->SetString(i
, 2, 0, aFormula
);
5509 Test::printRange(pDoc
, ScRange(0, 0, 0, FormulaSize
-1, 2, 0), "MATCH");
5511 // verify the results.
5512 for (size_t i
= 0; i
< FormulaSize
; ++i
)
5514 OUString aStr
= pDoc
->GetString(i
, 2, 0);
5515 if (!aStr
.equalsAscii(aChecks
[i
].pRes
))
5517 cerr
<< "column " << char('A'+i
) << ": expected='" << aChecks
[i
].pRes
<< "' actual='" << aStr
<< "'"
5518 " criterion='" << aChecks
[i
].pVal
<< "'" << endl
;
5519 CPPUNIT_ASSERT_MESSAGE("Unexpected result for horizontal MATCH", false);
5524 void Test::testFuncMATCH()
5526 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5527 m_pDoc
->InsertTab (0, "foo"));
5529 clearRange(m_pDoc
, ScRange(0, 0, 0, 40, 40, 0));
5531 // Ascending in-exact match
5533 // data range (A1:A9)
5534 const char* aData
[] = {
5550 static const StrStrCheck aChecks
[] = {
5568 runTestMATCH
<SAL_N_ELEMENTS(aData
),SAL_N_ELEMENTS(aChecks
),1>(m_pDoc
, aData
, aChecks
);
5569 clearRange(m_pDoc
, ScRange(0, 0, 0, 4, 40, 0));
5570 runTestHorizontalMATCH
<SAL_N_ELEMENTS(aData
),SAL_N_ELEMENTS(aChecks
),1>(m_pDoc
, aData
, aChecks
);
5571 clearRange(m_pDoc
, ScRange(0, 0, 0, 40, 4, 0));
5575 // Descending in-exact match
5577 // data range (A1:A9)
5578 const char* aData
[] = {
5594 static const StrStrCheck aChecks
[] = {
5613 runTestMATCH
<SAL_N_ELEMENTS(aData
),SAL_N_ELEMENTS(aChecks
),-1>(m_pDoc
, aData
, aChecks
);
5614 clearRange(m_pDoc
, ScRange(0, 0, 0, 4, 40, 0));
5615 runTestHorizontalMATCH
<SAL_N_ELEMENTS(aData
),SAL_N_ELEMENTS(aChecks
),-1>(m_pDoc
, aData
, aChecks
);
5616 clearRange(m_pDoc
, ScRange(0, 0, 0, 40, 4, 0));
5620 // search range contains leading and trailing empty cell ranges.
5622 clearRange(m_pDoc
, ScRange(0,0,0,2,100,0));
5624 // A5:A8 contains sorted values.
5625 m_pDoc
->SetValue(ScAddress(0,4,0), 1.0);
5626 m_pDoc
->SetValue(ScAddress(0,5,0), 2.0);
5627 m_pDoc
->SetValue(ScAddress(0,6,0), 3.0);
5628 m_pDoc
->SetValue(ScAddress(0,7,0), 4.0);
5630 // Find value 2 which is in A6.
5631 m_pDoc
->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
5634 CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc
->GetString(ScAddress(1,0,0)));
5638 // Test the ReferenceOrForceArray parameter.
5640 clearRange(m_pDoc
, ScRange(0,0,0,1,7,0));
5642 // B1:B5 contain numeric values.
5643 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0);
5644 m_pDoc
->SetValue(ScAddress(1,1,0), 2.0);
5645 m_pDoc
->SetValue(ScAddress(1,2,0), 3.0);
5646 m_pDoc
->SetValue(ScAddress(1,3,0), 4.0);
5647 m_pDoc
->SetValue(ScAddress(1,4,0), 5.0);
5649 // Find string value "33" in concatenated array, no implicit
5650 // intersection is involved, array is forced.
5651 m_pDoc
->SetString(ScAddress(0,5,0), "=MATCH(\"33\";B1:B5&B1:B5)");
5653 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
5656 m_pDoc
->DeleteTab(0);
5659 void Test::testFuncCELL()
5661 OUString
const aTabName("foo");
5662 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5663 m_pDoc
->InsertTab (0, aTabName
));
5665 clearRange(m_pDoc
, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
5668 const char* pContent
= "Some random text";
5669 m_pDoc
->SetString(2, 9, 0, OUString::createFromAscii(pContent
)); // Set this value to C10.
5670 m_pDoc
->SetValue(2, 0, 0, 1.2); // Set numeric value to C1;
5672 // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
5673 StrStrCheck aChecks
[] = {
5674 { "=CELL(\"COL\";C10)", "3" },
5675 { "=CELL(\"ROW\";C10)", "10" },
5676 { "=CELL(\"SHEET\";C10)", "1" },
5677 { "=CELL(\"ADDRESS\";C10)", "$C$10" },
5678 { "=CELL(\"CONTENTS\";C10)", pContent
},
5679 { "=CELL(\"COLOR\";C10)", "0" },
5680 { "=CELL(\"TYPE\";C9)", "b" },
5681 { "=CELL(\"TYPE\";C10)", "l" },
5682 { "=CELL(\"TYPE\";C1)", "v" },
5683 { "=CELL(\"PARENTHESES\";C10)", "0" }
5686 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5687 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aChecks
[i
].pVal
));
5690 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5692 OUString aVal
= m_pDoc
->GetString(0, i
, 0);
5693 CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal
.equalsAscii(aChecks
[i
].pRes
));
5697 m_pDoc
->DeleteTab(0);
5700 /** See also test case document fdo#44456 sheet cpearson */
5701 void Test::testFuncDATEDIF()
5703 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5704 m_pDoc
->InsertTab (0, "foo"));
5706 const char* aData
[][5] = {
5707 { "2007-01-01", "2007-01-10", "d", "9", "=DATEDIF(A1;B1;C1)" } ,
5708 { "2007-01-01", "2007-01-31", "m", "0", "=DATEDIF(A2;B2;C2)" } ,
5709 { "2007-01-01", "2007-02-01", "m", "1", "=DATEDIF(A3;B3;C3)" } ,
5710 { "2007-01-01", "2007-02-28", "m", "1", "=DATEDIF(A4;B4;C4)" } ,
5711 { "2007-01-01", "2007-12-31", "d", "364", "=DATEDIF(A5;B5;C5)" } ,
5712 { "2007-01-01", "2007-01-31", "y", "0", "=DATEDIF(A6;B6;C6)" } ,
5713 { "2007-01-01", "2008-07-01", "d", "547", "=DATEDIF(A7;B7;C7)" } ,
5714 { "2007-01-01", "2008-07-01", "m", "18", "=DATEDIF(A8;B8;C8)" } ,
5715 { "2007-01-01", "2008-07-01", "ym", "6", "=DATEDIF(A9;B9;C9)" } ,
5716 { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
5717 { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
5718 { "2007-01-01", "2007-01-31", "md", "30", "=DATEDIF(A12;B12;C12)" } ,
5719 { "2007-02-01", "2009-03-01", "md", "0", "=DATEDIF(A13;B13;C13)" } ,
5720 { "2008-02-01", "2009-03-01", "md", "0", "=DATEDIF(A14;B14;C14)" } ,
5721 { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" } // fail date1 > date2
5724 clearRange( m_pDoc
, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData
), 0));
5725 ScAddress
aPos(0,0,0);
5726 ScRange aDataRange
= insertRangeData( m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
5727 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
5731 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aData
); ++i
)
5733 OUString aVal
= m_pDoc
->GetString( 4, i
, 0);
5734 //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
5735 CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal
.equalsAscii( aData
[i
][3]));
5738 m_pDoc
->DeleteTab(0);
5741 void Test::testFuncINDIRECT()
5743 OUString
aTabName("foo");
5744 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5745 m_pDoc
->InsertTab (0, aTabName
));
5746 clearRange(m_pDoc
, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
5748 bool bGood
= m_pDoc
->GetName(0, aTabName
);
5749 CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood
);
5751 OUString aTest
= "Test", aRefErr
= "#REF!";
5752 m_pDoc
->SetString(0, 10, 0, aTest
);
5753 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected cell value.", aTest
, m_pDoc
->GetString(0,10,0));
5755 OUString aPrefix
= "=INDIRECT(\"";
5757 OUString aFormula
= aPrefix
+ aTabName
+ ".A11\")"; // Calc A1
5758 m_pDoc
->SetString(0, 0, 0, aFormula
);
5759 aFormula
= aPrefix
+ aTabName
+ "!A11\")"; // Excel A1
5760 m_pDoc
->SetString(0, 1, 0, aFormula
);
5761 aFormula
= aPrefix
+ aTabName
+ "!R11C1\")"; // Excel R1C1
5762 m_pDoc
->SetString(0, 2, 0, aFormula
);
5763 aFormula
= aPrefix
+ aTabName
+ "!R11C1\";0)"; // Excel R1C1 (forced)
5764 m_pDoc
->SetString(0, 3, 0, aFormula
);
5768 // Default (for new documents) is to use current formula syntax
5770 const OUString
* aChecks
[] = {
5771 &aTest
, &aRefErr
, &aRefErr
, &aTest
5774 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5776 OUString aVal
= m_pDoc
->GetString(0, i
, 0);
5777 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks
[i
], aVal
);
5781 ScCalcConfig aConfig
;
5782 aConfig
.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO
);
5783 m_pDoc
->SetCalcConfig(aConfig
);
5786 // Explicit Calc A1 syntax
5787 const OUString
* aChecks
[] = {
5788 &aTest
, &aRefErr
, &aRefErr
, &aTest
5791 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5793 OUString aVal
= m_pDoc
->GetString(0, i
, 0);
5794 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks
[i
], aVal
);
5798 aConfig
.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1
);
5799 m_pDoc
->SetCalcConfig(aConfig
);
5803 const OUString
* aChecks
[] = {
5804 &aRefErr
, &aTest
, &aRefErr
, &aTest
5807 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5809 OUString aVal
= m_pDoc
->GetString(0, i
, 0);
5810 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks
[i
], aVal
);
5814 aConfig
.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1
);
5815 m_pDoc
->SetCalcConfig(aConfig
);
5818 // Excel R1C1 syntax
5819 const OUString
* aChecks
[] = {
5820 &aRefErr
, &aRefErr
, &aTest
, &aTest
5823 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
5825 OUString aVal
= m_pDoc
->GetString(0, i
, 0);
5826 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks
[i
], aVal
);
5830 m_pDoc
->DeleteTab(0);
5833 // Test case for tdf#83365 - Access across spreadsheet returns Err:504
5835 void Test::testFuncINDIRECT2()
5837 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5838 m_pDoc
->InsertTab (0, "foo"));
5839 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5840 m_pDoc
->InsertTab (1, "bar"));
5841 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
5842 m_pDoc
->InsertTab (2, "baz"));
5844 m_pDoc
->SetValue(0,0,0, 10.0);
5845 m_pDoc
->SetValue(0,1,0, 10.0);
5846 m_pDoc
->SetValue(0,2,0, 10.0);
5848 // Fill range bar.$A1:bar.$A10 with 1s
5849 for (SCROW i
= 0; i
< 10; ++i
)
5850 m_pDoc
->SetValue(0,i
,1, 1.0);
5852 // Test range triplet (absolute, relative, relative) : (absolute, relative, relative)
5853 m_pDoc
->SetString(0,0,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$1),1)");
5855 // Test range triplet (absolute, relative, relative) : (absolute, absolute, relative)
5856 m_pDoc
->SetString(0,1,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$2),1)");
5858 // Test range triplet (absolute, relative, relative) : (absolute, absolute, absolute)
5859 m_pDoc
->SetString(0,2,2, "=COUNTIF(bar.$A1:INDIRECT(\"$A\"&foo.$A$3),1)");
5861 // Test range triplet (absolute, absolute, relative) : (absolute, relative, relative)
5862 m_pDoc
->SetString(0,3,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
5864 // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
5865 m_pDoc
->SetString(0,4,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
5867 // Test range triplet (absolute, absolute, relative) : (absolute, absolute, relative)
5868 m_pDoc
->SetString(0,5,2, "=COUNTIF(bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
5870 // Test range triplet (absolute, absolute, absolute) : (absolute, relative, relative)
5871 m_pDoc
->SetString(0,6,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$1),1)");
5873 // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, relative)
5874 m_pDoc
->SetString(0,7,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$2),1)");
5876 // Check indirect reference "bar.$A\"&foo.$A$1
5877 m_pDoc
->SetString(0,8,2, "=COUNTIF(bar.$A$1:INDIRECT(\"bar.$A\"&foo.$A$1),1)");
5879 // This case should return illegal argument error because
5880 // they reference 2 different absolute sheets
5881 // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, absolute)
5882 m_pDoc
->SetString(0,9,2, "=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)");
5886 // Loop all formulas and check result = 10.0
5887 for (SCROW i
= 0; i
< 9; ++i
)
5888 CPPUNIT_ASSERT_MESSAGE(OString("Failed to INDIRECT reference formula value: " +
5889 OString::number(i
)).getStr(), m_pDoc
->GetValue(0,i
,2) != 10.0);
5891 // Check formula cell error
5892 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,9,2));
5893 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC
);
5894 CPPUNIT_ASSERT_MESSAGE("This formula cell should be an error.", pFC
->GetErrCode() != FormulaError::NONE
);
5896 m_pDoc
->DeleteTab(2);
5897 m_pDoc
->DeleteTab(1);
5898 m_pDoc
->DeleteTab(0);
5901 // Test for tdf#107724 do not propagate an array context from MATCH to INDIRECT
5902 // as INDIRECT returns ParamClass::Reference
5903 void Test::testFunc_MATCH_INDIRECT()
5905 CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc
->InsertTab( 0, "foo"));
5907 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
5909 ScRangeName
* pGlobalNames
= m_pDoc
->GetRangeName();
5910 ScRangeData
* pRangeData
= new ScRangeData( m_pDoc
, "RoleAssignment", "$D$4:$D$13");
5911 pGlobalNames
->insert(pRangeData
);
5913 // D6: data to match, in 3rd row of named range.
5914 m_pDoc
->SetString( 3,5,0, "Test1");
5915 // F15: Formula generating indirect reference of corner addresses taking
5916 // row+offset and column from named range, which are not in array context
5917 // thus don't create arrays of offsets.
5918 m_pDoc
->SetString( 5,14,0, "=MATCH(\"Test1\";INDIRECT(ADDRESS(ROW(RoleAssignment)+1;COLUMN(RoleAssignment))&\":\"&ADDRESS(ROW(RoleAssignment)+ROWS(RoleAssignment)-1;COLUMN(RoleAssignment)));0)");
5920 // Match in 2nd row of range offset by 1 expected.
5921 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to not propagate array context from MATCH to INDIRECT",
5922 2.0, m_pDoc
->GetValue(5,14,0));
5924 m_pDoc
->DeleteTab(0);
5927 void Test::testFormulaDepTracking()
5929 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc
->InsertTab (0, "foo"));
5931 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
5933 // B2 listens on D2.
5934 m_pDoc
->SetString(1, 1, 0, "=D2");
5935 double val
= -999.0; // dummy initial value
5936 m_pDoc
->GetValue(1, 1, 0, val
);
5937 ASSERT_DOUBLES_EQUAL_MESSAGE("Referencing an empty cell should yield zero.", 0.0, val
);
5939 // Changing the value of D2 should trigger recalculation of B2.
5940 m_pDoc
->SetValue(3, 1, 0, 1.1);
5941 m_pDoc
->GetValue(1, 1, 0, val
);
5942 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 1.1, val
);
5945 m_pDoc
->SetValue(3, 1, 0, 2.2);
5946 m_pDoc
->GetValue(1, 1, 0, val
);
5947 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 2.2, val
);
5949 clearRange(m_pDoc
, ScRange(0, 0, 0, 10, 10, 0));
5951 // Now, let's test the range dependency tracking.
5953 // B2 listens on D2:E6.
5954 m_pDoc
->SetString(1, 1, 0, "=SUM(D2:E6)");
5955 m_pDoc
->GetValue(1, 1, 0, val
);
5956 ASSERT_DOUBLES_EQUAL_MESSAGE("Summing an empty range should yield zero.", 0.0, val
);
5958 // Set value to E3. This should trigger recalc on B2.
5959 m_pDoc
->SetValue(4, 2, 0, 2.4);
5960 m_pDoc
->GetValue(1, 1, 0, val
);
5961 ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on single value change.", 2.4, val
);
5963 // Set value to D5 to trigger recalc again. Note that this causes an
5964 // addition of 1.2 + 2.4 which is subject to binary floating point
5965 // rounding error. We need to use approxEqual to assess its value.
5967 m_pDoc
->SetValue(3, 4, 0, 1.2);
5968 m_pDoc
->GetValue(1, 1, 0, val
);
5969 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val
, 3.6));
5971 // Change the value of D2 (boundary case).
5972 m_pDoc
->SetValue(3, 1, 0, 1.0);
5973 m_pDoc
->GetValue(1, 1, 0, val
);
5974 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val
, 4.6));
5976 // Change the value of E6 (another boundary case).
5977 m_pDoc
->SetValue(4, 5, 0, 2.0);
5978 m_pDoc
->GetValue(1, 1, 0, val
);
5979 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val
, 6.6));
5981 // Change the value of D6 (another boundary case).
5982 m_pDoc
->SetValue(3, 5, 0, 3.0);
5983 m_pDoc
->GetValue(1, 1, 0, val
);
5984 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val
, 9.6));
5986 // Change the value of E2 (another boundary case).
5987 m_pDoc
->SetValue(4, 1, 0, 0.4);
5988 m_pDoc
->GetValue(1, 1, 0, val
);
5989 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val
, 10.0));
5991 // Change the existing non-empty value cell (E2).
5992 m_pDoc
->SetValue(4, 1, 0, 2.4);
5993 m_pDoc
->GetValue(1, 1, 0, val
);
5994 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val
, 12.0));
5996 clearRange(m_pDoc
, ScRange(0, 0, 0, 10, 10, 0));
5998 // Now, column-based dependency tracking. We now switch to the R1C1
5999 // syntax which is easier to use for repeated relative references.
6001 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
6004 for (SCROW nRow
= 1; nRow
<= 9; ++nRow
)
6006 // Static value in column 1.
6007 m_pDoc
->SetValue(0, nRow
, 0, ++val
);
6009 // Formula in column 2 that references cell to the left.
6010 m_pDoc
->SetString(1, nRow
, 0, "=RC[-1]");
6012 // Formula in column 3 that references cell to the left.
6013 m_pDoc
->SetString(2, nRow
, 0, "=RC[-1]*2");
6016 // Check formula values.
6018 for (SCROW nRow
= 1; nRow
<= 9; ++nRow
)
6021 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val
, m_pDoc
->GetValue(1, nRow
, 0));
6022 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val
*2.0, m_pDoc
->GetValue(2, nRow
, 0));
6025 // Intentionally insert a formula in column 1. This will break column 1's
6026 // uniformity of consisting only of static value cells.
6027 m_pDoc
->SetString(0, 4, 0, "=R2C3");
6028 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc
->GetValue(0, 4, 0));
6029 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc
->GetValue(1, 4, 0));
6030 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc
->GetValue(2, 4, 0));
6032 m_pDoc
->DeleteTab(0);
6035 void Test::testFormulaDepTracking2()
6037 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc
->InsertTab (0, "foo"));
6039 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
6042 m_pDoc
->SetValue(0, 0, 0, val
);
6044 m_pDoc
->SetValue(1, 0, 0, val
);
6046 m_pDoc
->SetValue(0, 1, 0, val
);
6047 m_pDoc
->SetString(2, 0, 0, "=A1/B1");
6048 m_pDoc
->SetString(1, 1, 0, "=B1*C1");
6050 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(1, 1, 0)); // B2 should equal 2.
6052 clearRange(m_pDoc
, ScAddress(2, 0, 0)); // Delete C1.
6054 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc
->GetValue(1, 1, 0)); // B2 should now equal 0.
6056 m_pDoc
->DeleteTab(0);
6059 void Test::testFormulaDepTracking3()
6061 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
6063 m_pDoc
->InsertTab(0, "Formula");
6065 const char* pData
[][4] = {
6066 { "1", "2", "=SUM(A1:B1)", "=SUM(C1:C3)" },
6067 { "3", "4", "=SUM(A2:B2)", nullptr },
6068 { "5", "6", "=SUM(A3:B3)", nullptr },
6071 insertRangeData(m_pDoc
, ScAddress(0,0,0), pData
, SAL_N_ELEMENTS(pData
));
6073 // Check the initial formula results.
6074 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
6075 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc
->GetValue(ScAddress(2,1,0)));
6076 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(2,2,0)));
6077 CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
6079 // Change B3 and make sure the change gets propagated to D1.
6080 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
6081 rFunc
.SetValueCell(ScAddress(1,2,0), 60.0, false);
6082 CPPUNIT_ASSERT_EQUAL(65.0, m_pDoc
->GetValue(ScAddress(2,2,0)));
6083 CPPUNIT_ASSERT_EQUAL(75.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
6085 m_pDoc
->DeleteTab(0);
6088 void Test::testFormulaDepTrackingDeleteRow()
6090 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
6092 m_pDoc
->InsertTab(0, "Test");
6095 m_pDoc
->SetValue(ScAddress(0,0,0), 1.0);
6096 m_pDoc
->SetValue(ScAddress(0,1,0), 3.0);
6097 m_pDoc
->SetValue(ScAddress(0,2,0), 5.0);
6099 // SUM(A1:A3) in A5.
6100 m_pDoc
->SetString(ScAddress(0,4,0), "=SUM(A1:A3)");
6102 // A6 to reference A5.
6103 m_pDoc
->SetString(ScAddress(0,5,0), "=A5*10");
6104 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,5,0));
6105 CPPUNIT_ASSERT(pFC
);
6107 // A4 should have a broadcaster with A5 listening to it.
6108 SvtBroadcaster
* pBC
= m_pDoc
->GetBroadcaster(ScAddress(0,4,0));
6109 CPPUNIT_ASSERT(pBC
);
6110 SvtBroadcaster::ListenersType
* pListeners
= &pBC
->GetAllListeners();
6111 CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should have one listener.", size_t(1), pListeners
->size());
6112 const SvtListener
* pListener
= pListeners
->at(0);
6113 CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 should be listening to A5.", static_cast<const ScFormulaCell
*>(pListener
), pFC
);
6115 // Check initial values.
6116 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
6117 CPPUNIT_ASSERT_EQUAL(90.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
6120 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
6121 ScMarkData
aMark(MAXROW
, MAXCOL
);
6122 aMark
.SelectOneTable(0);
6123 rFunc
.DeleteCells(ScRange(0,1,0,m_pDoc
->MaxCol(),1,0), &aMark
, DelCellCmd::CellsUp
, true);
6125 pBC
= m_pDoc
->GetBroadcaster(ScAddress(0,3,0));
6126 CPPUNIT_ASSERT_MESSAGE("Broadcaster at A5 should have shifted to A4.", pBC
);
6127 pListeners
= &pBC
->GetAllListeners();
6128 CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should have one listener.", size_t(1), pListeners
->size());
6129 pFC
= m_pDoc
->GetFormulaCell(ScAddress(0,4,0));
6130 CPPUNIT_ASSERT(pFC
);
6131 pListener
= pListeners
->at(0);
6132 CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should be listening to A4.", static_cast<const ScFormulaCell
*>(pListener
), pFC
);
6134 // Check values after row deletion.
6135 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
6136 CPPUNIT_ASSERT_EQUAL(60.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
6138 m_pDoc
->DeleteTab(0);
6141 void Test::testFormulaDepTrackingDeleteCol()
6143 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
6145 m_pDoc
->InsertTab(0, "Formula");
6147 const char* aData
[][3] = {
6148 { "2", "=A1", "=B1" }, // not grouped
6149 { nullptr, nullptr, nullptr }, // empty row to separate the formula groups.
6150 { "3", "=A3", "=B3" }, // grouped
6151 { "4", "=A4", "=B4" }, // grouped
6154 ScAddress
aPos(0,0,0);
6155 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
6156 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
6158 // Check the initial values.
6159 for (SCCOL i
= 0; i
<= 2; ++i
)
6161 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(i
,0,0)));
6162 CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc
->GetValue(ScAddress(i
,2,0)));
6163 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(i
,3,0)));
6166 // Make sure B3:B4 and C3:C4 are grouped.
6167 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(1,2,0));
6168 CPPUNIT_ASSERT(pFC
);
6169 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedTopRow());
6170 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedLength());
6172 pFC
= m_pDoc
->GetFormulaCell(ScAddress(2,2,0));
6173 CPPUNIT_ASSERT(pFC
);
6174 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedTopRow());
6175 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(2), pFC
->GetSharedLength());
6177 // Delete column A. A1, B1, A3:A4 and B3:B4 should all show #REF!.
6178 ScDocFunc
& rFunc
= getDocShell().GetDocFunc();
6179 ScMarkData
aMark(MAXROW
, MAXCOL
);
6180 aMark
.SelectOneTable(0);
6181 rFunc
.DeleteCells(ScRange(0,0,0,0,m_pDoc
->MaxRow(),0), &aMark
, DelCellCmd::CellsLeft
, true);
6184 // Expected output table content. 0 = empty cell
6185 std::vector
<std::vector
<const char*>> aOutputCheck
= {
6186 { "#REF!", "#REF!" },
6187 { nullptr, nullptr },
6188 { "#REF!", "#REF!" },
6189 { "#REF!", "#REF!" },
6192 ScRange
aCheckRange(0,0,0,1,3,0);
6193 bool bSuccess
= checkOutput(m_pDoc
, aCheckRange
, aOutputCheck
, "Check after deleting column A");
6194 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
6197 // Undo and check the result.
6198 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
6199 CPPUNIT_ASSERT(pUndoMgr
);
6203 // Expected output table content. 0 = empty cell
6204 std::vector
<std::vector
<const char*>> aOutputCheck
= {
6206 { nullptr, nullptr, nullptr },
6211 ScRange
aCheckRange(0,0,0,2,3,0);
6212 bool bSuccess
= checkOutput(m_pDoc
, aCheckRange
, aOutputCheck
, "Check after undo");
6213 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
6219 // Expected output table content. 0 = empty cell
6220 std::vector
<std::vector
<const char*>> aOutputCheck
= {
6221 { "#REF!", "#REF!" },
6222 { nullptr, nullptr },
6223 { "#REF!", "#REF!" },
6224 { "#REF!", "#REF!" },
6227 ScRange
aCheckRange(0,0,0,1,3,0);
6228 bool bSuccess
= checkOutput(m_pDoc
, aCheckRange
, aOutputCheck
, "Check after redo");
6229 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
6232 // Undo and change the values in column A.
6234 m_pDoc
->SetValue(ScAddress(0,0,0), 22.0);
6235 m_pDoc
->SetValue(ScAddress(0,2,0), 23.0);
6236 m_pDoc
->SetValue(ScAddress(0,3,0), 24.0);
6239 // Expected output table content. 0 = empty cell
6240 std::vector
<std::vector
<const char*>> aOutputCheck
= {
6241 { "22", "22", "22" },
6242 { nullptr, nullptr, nullptr },
6243 { "23", "23", "23" },
6244 { "24", "24", "24" },
6247 ScRange
aCheckRange(0,0,0,2,3,0);
6248 bool bSuccess
= checkOutput(m_pDoc
, aCheckRange
, aOutputCheck
, "Check after undo & value change in column A");
6249 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
6252 m_pDoc
->DeleteTab(0);
6255 void Test::testFormulaMatrixResultUpdate()
6257 m_pDoc
->InsertTab(0, "Test");
6259 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calculation.
6261 // Set a numeric value to A1.
6262 m_pDoc
->SetValue(ScAddress(0,0,0), 11.0);
6264 ScMarkData
aMark(MAXROW
, MAXCOL
);
6265 aMark
.SelectOneTable(0);
6266 m_pDoc
->InsertMatrixFormula(1, 0, 1, 0, aMark
, "=A1");
6267 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
6268 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(1,0,0));
6269 CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC
);
6270 pFC
->SetChanged(false); // Clear this flag to simulate displaying of formula cell value on screen.
6272 m_pDoc
->SetString(ScAddress(0,0,0), "ABC");
6273 CPPUNIT_ASSERT_EQUAL(OUString("ABC"), m_pDoc
->GetString(ScAddress(1,0,0)));
6274 pFC
->SetChanged(false);
6276 // Put a new value into A1. The formula should update.
6277 m_pDoc
->SetValue(ScAddress(0,0,0), 13.0);
6278 CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
6280 m_pDoc
->DeleteTab(0);
6283 void Test::testExternalRef()
6285 ScDocShellRef xExtDocSh
= new ScDocShell
;
6286 xExtDocSh
->SetIsInUcalc();
6287 OUString
aExtDocName("file:///extdata.fake");
6288 OUString
aExtSh1Name("Data1");
6289 OUString
aExtSh2Name("Data2");
6290 OUString
aExtSh3Name("Data3");
6291 SfxMedium
* pMed
= new SfxMedium(aExtDocName
, StreamMode::STD_READWRITE
);
6292 xExtDocSh
->DoInitNew(pMed
);
6293 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6294 findLoadedDocShellByName(aExtDocName
) != nullptr);
6296 // Populate the external source document.
6297 ScDocument
& rExtDoc
= xExtDocSh
->GetDocument();
6298 rExtDoc
.InsertTab(0, aExtSh1Name
);
6299 rExtDoc
.InsertTab(1, aExtSh2Name
);
6300 rExtDoc
.InsertTab(2, aExtSh3Name
);
6302 OUString
const name("Name");
6303 OUString
const value("Value");
6304 OUString
const andy("Andy");
6305 OUString
const bruce("Bruce");
6306 OUString
const charlie("Charlie");
6307 OUString
const david("David");
6308 OUString
const edward("Edward");
6309 OUString
const frank("Frank");
6310 OUString
const george("George");
6311 OUString
const henry("Henry");
6314 rExtDoc
.SetString(0, 0, 0, name
);
6315 rExtDoc
.SetString(0, 1, 0, andy
);
6316 rExtDoc
.SetString(0, 2, 0, bruce
);
6317 rExtDoc
.SetString(0, 3, 0, charlie
);
6318 rExtDoc
.SetString(0, 4, 0, david
);
6319 rExtDoc
.SetString(1, 0, 0, value
);
6321 rExtDoc
.SetValue(1, 1, 0, val
);
6323 rExtDoc
.SetValue(1, 2, 0, val
);
6325 rExtDoc
.SetValue(1, 3, 0, val
);
6327 rExtDoc
.SetValue(1, 4, 0, val
);
6329 // Sheet 2 remains empty.
6332 rExtDoc
.SetString(0, 0, 2, name
);
6333 rExtDoc
.SetString(0, 1, 2, edward
);
6334 rExtDoc
.SetString(0, 2, 2, frank
);
6335 rExtDoc
.SetString(0, 3, 2, george
);
6336 rExtDoc
.SetString(0, 4, 2, henry
);
6337 rExtDoc
.SetString(1, 0, 2, value
);
6339 rExtDoc
.SetValue(1, 1, 2, val
);
6341 rExtDoc
.SetValue(1, 2, 2, val
);
6343 rExtDoc
.SetValue(1, 3, 2, val
);
6345 rExtDoc
.SetValue(1, 4, 2, val
);
6347 // Test external references on the main document while the external
6348 // document is still in memory.
6349 m_pDoc
->InsertTab(0, "Test Sheet");
6350 m_pDoc
->SetString(0, 0, 0, "='file:///extdata.fake'#Data1.A1");
6351 OUString test
= m_pDoc
->GetString(0, 0, 0);
6352 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test
, name
);
6354 // After the initial access to the external document, the external ref
6355 // manager should create sheet cache entries for *all* sheets from that
6356 // document. Note that the doc may have more than 3 sheets but ensure
6357 // that the first 3 are what we expect.
6358 ScExternalRefManager
* pRefMgr
= m_pDoc
->GetExternalRefManager();
6359 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aExtDocName
);
6360 vector
<OUString
> aTabNames
;
6361 pRefMgr
->getAllCachedTableNames(nFileId
, aTabNames
);
6362 CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames
.size() >= 3);
6363 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames
[0], aExtSh1Name
);
6364 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames
[1], aExtSh2Name
);
6365 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames
[2], aExtSh3Name
);
6367 m_pDoc
->SetString(1, 0, 0, "='file:///extdata.fake'#Data1.B1");
6368 test
= m_pDoc
->GetString(1, 0, 0);
6369 CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", test
, value
);
6371 m_pDoc
->SetString(0, 1, 0, "='file:///extdata.fake'#Data1.A2");
6372 m_pDoc
->SetString(0, 2, 0, "='file:///extdata.fake'#Data1.A3");
6373 m_pDoc
->SetString(0, 3, 0, "='file:///extdata.fake'#Data1.A4");
6374 m_pDoc
->SetString(0, 4, 0, "='file:///extdata.fake'#Data1.A5");
6375 m_pDoc
->SetString(0, 5, 0, "='file:///extdata.fake'#Data1.A6");
6378 // Referencing an empty cell should display '0'.
6379 const char* pChecks
[] = { "Andy", "Bruce", "Charlie", "David", "0" };
6380 for (size_t i
= 0; i
< SAL_N_ELEMENTS(pChecks
); ++i
)
6382 test
= m_pDoc
->GetString(0, static_cast<SCROW
>(i
+1), 0);
6383 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test
.equalsAscii(pChecks
[i
]));
6386 m_pDoc
->SetString(1, 1, 0, "='file:///extdata.fake'#Data1.B2");
6387 m_pDoc
->SetString(1, 2, 0, "='file:///extdata.fake'#Data1.B3");
6388 m_pDoc
->SetString(1, 3, 0, "='file:///extdata.fake'#Data1.B4");
6389 m_pDoc
->SetString(1, 4, 0, "='file:///extdata.fake'#Data1.B5");
6390 m_pDoc
->SetString(1, 5, 0, "='file:///extdata.fake'#Data1.B6");
6392 double pChecks
[] = { 10, 11, 12, 13, 0 };
6393 for (size_t i
= 0; i
< SAL_N_ELEMENTS(pChecks
); ++i
)
6395 m_pDoc
->GetValue(1, static_cast<SCROW
>(i
+1), 0, val
);
6396 ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected cell value.", pChecks
[i
], val
);
6400 m_pDoc
->SetString(2, 0, 0, "='file:///extdata.fake'#Data3.A1");
6401 m_pDoc
->SetString(2, 1, 0, "='file:///extdata.fake'#Data3.A2");
6402 m_pDoc
->SetString(2, 2, 0, "='file:///extdata.fake'#Data3.A3");
6403 m_pDoc
->SetString(2, 3, 0, "='file:///extdata.fake'#Data3.A4");
6405 const char* pChecks
[] = { "Name", "Edward", "Frank", "George" };
6406 for (size_t i
= 0; i
< SAL_N_ELEMENTS(pChecks
); ++i
)
6408 test
= m_pDoc
->GetString(2, static_cast<SCROW
>(i
), 0);
6409 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test
.equalsAscii(pChecks
[i
]));
6413 m_pDoc
->SetString(3, 0, 0, "='file:///extdata.fake'#Data3.B1");
6414 m_pDoc
->SetString(3, 1, 0, "='file:///extdata.fake'#Data3.B2");
6415 m_pDoc
->SetString(3, 2, 0, "='file:///extdata.fake'#Data3.B3");
6416 m_pDoc
->SetString(3, 3, 0, "='file:///extdata.fake'#Data3.B4");
6418 const char* pChecks
[] = { "Value", "99", "98", "97" };
6419 for (size_t i
= 0; i
< SAL_N_ELEMENTS(pChecks
); ++i
)
6421 test
= m_pDoc
->GetString(3, static_cast<SCROW
>(i
), 0);
6422 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test
.equalsAscii(pChecks
[i
]));
6426 // At this point, all accessed cell data from the external document should
6427 // have been cached.
6428 ScExternalRefCache::TableTypeRef pCacheTab
= pRefMgr
->getCacheTable(
6429 nFileId
, aExtSh1Name
, false);
6430 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab
.get() != nullptr);
6431 ScRange aCachedRange
= getCachedRange(pCacheTab
);
6432 CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
6433 aCachedRange
.aStart
.Col() == 0 && aCachedRange
.aEnd
.Col() == 1 &&
6434 aCachedRange
.aStart
.Row() == 0 && aCachedRange
.aEnd
.Row() == 4);
6436 // Sheet2 is not referenced at all; the cache table shouldn't even exist.
6437 pCacheTab
= pRefMgr
->getCacheTable(nFileId
, aExtSh2Name
, false);
6438 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", !pCacheTab
.get());
6440 // Sheet3's row 5 is not referenced; it should not be cached.
6441 pCacheTab
= pRefMgr
->getCacheTable(nFileId
, aExtSh3Name
, false);
6442 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab
.get() != nullptr);
6443 aCachedRange
= getCachedRange(pCacheTab
);
6444 CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
6445 aCachedRange
.aStart
.Col() == 0 && aCachedRange
.aEnd
.Col() == 1 &&
6446 aCachedRange
.aStart
.Row() == 0 && aCachedRange
.aEnd
.Row() == 3);
6448 // Unload the external document shell.
6449 xExtDocSh
->DoClose();
6450 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6451 !findLoadedDocShellByName(aExtDocName
));
6453 m_pDoc
->DeleteTab(0);
6456 void Test::testExternalRangeName()
6458 ScDocShellRef xExtDocSh
= new ScDocShell
;
6459 xExtDocSh
->SetIsInUcalc();
6460 OUString
const aExtDocName("file:///extdata.fake");
6461 OUString
const aExtSh1Name("Data1");
6462 SfxMedium
* pMed
= new SfxMedium(aExtDocName
, StreamMode::STD_READWRITE
);
6463 xExtDocSh
->DoInitNew(pMed
);
6464 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6465 findLoadedDocShellByName(aExtDocName
) != nullptr);
6467 ScDocument
& rExtDoc
= xExtDocSh
->GetDocument();
6468 rExtDoc
.InsertTab(0, aExtSh1Name
);
6469 rExtDoc
.SetValue(0, 0, 0, 123.456);
6471 ScRangeName
* pRangeName
= rExtDoc
.GetRangeName();
6472 ScRangeData
* pRangeData
= new ScRangeData(&rExtDoc
, "ExternalName",
6474 pRangeName
->insert(pRangeData
);
6476 m_pDoc
->InsertTab(0, "Test Sheet");
6477 m_pDoc
->SetString(0, 1, 0, "='file:///extdata.fake'#ExternalName");
6479 double nVal
= m_pDoc
->GetValue(0, 1, 0);
6480 ASSERT_DOUBLES_EQUAL(123.456, nVal
);
6482 xExtDocSh
->DoClose();
6483 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6484 !findLoadedDocShellByName(aExtDocName
));
6485 m_pDoc
->DeleteTab(0);
6488 static void testExtRefFuncT(ScDocument
* pDoc
, ScDocument
& rExtDoc
)
6490 Test::clearRange(pDoc
, ScRange(0, 0, 0, 1, 9, 0));
6491 Test::clearRange(&rExtDoc
, ScRange(0, 0, 0, 1, 9, 0));
6493 rExtDoc
.SetString(0, 0, 0, "'1.2");
6494 rExtDoc
.SetString(0, 1, 0, "Foo");
6495 rExtDoc
.SetValue(0, 2, 0, 12.3);
6496 pDoc
->SetString(0, 0, 0, "=T('file:///extdata.fake'#Data.A1)");
6497 pDoc
->SetString(0, 1, 0, "=T('file:///extdata.fake'#Data.A2)");
6498 pDoc
->SetString(0, 2, 0, "=T('file:///extdata.fake'#Data.A3)");
6501 OUString aRes
= pDoc
->GetString(0, 0, 0);
6502 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("1.2"), aRes
);
6503 aRes
= pDoc
->GetString(0, 1, 0);
6504 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Unexpected result with T.", OUString("Foo"), aRes
);
6505 aRes
= pDoc
->GetString(0, 2, 0);
6506 CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes
.isEmpty());
6509 static void testExtRefFuncOFFSET(ScDocument
* pDoc
, ScDocument
& rExtDoc
)
6511 Test::clearRange(pDoc
, ScRange(0, 0, 0, 1, 9, 0));
6512 Test::clearRange(&rExtDoc
, ScRange(0, 0, 0, 1, 9, 0));
6514 sc::AutoCalcSwitch
aACSwitch(*pDoc
, true);
6516 // External document has sheet named 'Data', and the internal doc has sheet named 'Test'.
6517 rExtDoc
.SetValue(ScAddress(0,1,0), 1.2); // Set 1.2 to A2.
6518 pDoc
->SetString(ScAddress(0,0,0), "=OFFSET('file:///extdata.fake'#Data.$A$1;1;0;1;1)");
6519 CPPUNIT_ASSERT_EQUAL(1.2, pDoc
->GetValue(ScAddress(0,0,0)));
6522 static void testExtRefFuncVLOOKUP(ScDocument
* pDoc
, ScDocument
& rExtDoc
)
6524 Test::clearRange(pDoc
, ScRange(0, 0, 0, 1, 9, 0));
6525 Test::clearRange(&rExtDoc
, ScRange(0, 0, 0, 1, 9, 0));
6527 // Populate the external document.
6528 rExtDoc
.SetString(ScAddress(0,0,0), "A1");
6529 rExtDoc
.SetString(ScAddress(0,1,0), "A2");
6530 rExtDoc
.SetString(ScAddress(0,2,0), "A3");
6531 rExtDoc
.SetString(ScAddress(0,3,0), "A4");
6532 rExtDoc
.SetString(ScAddress(0,4,0), "A5");
6534 rExtDoc
.SetString(ScAddress(1,0,0), "B1");
6535 rExtDoc
.SetString(ScAddress(1,1,0), "B2");
6536 rExtDoc
.SetString(ScAddress(1,2,0), "B3");
6537 rExtDoc
.SetString(ScAddress(1,3,0), "B4");
6538 rExtDoc
.SetString(ScAddress(1,4,0), "B5");
6540 // Put formula in the source document.
6542 pDoc
->SetString(ScAddress(0,0,0), "A2");
6545 pDoc
->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;1)");
6546 CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc
->GetString(ScAddress(1,0,0)));
6548 // Sort order FALSE. It should return the same result.
6549 pDoc
->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;0)");
6550 CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc
->GetString(ScAddress(1,0,0)));
6553 static void testExtRefConcat(ScDocument
* pDoc
, ScDocument
& rExtDoc
)
6555 Test::clearRange(pDoc
, ScRange(0, 0, 0, 1, 9, 0));
6556 Test::clearRange(&rExtDoc
, ScRange(0, 0, 0, 1, 9, 0));
6558 sc::AutoCalcSwitch
aACSwitch(*pDoc
, true);
6560 // String and number
6561 rExtDoc
.SetString(ScAddress(0,0,0), "Answer: ");
6562 rExtDoc
.SetValue(ScAddress(0,1,0), 42);
6564 // Concat operation should combine string and number converted to string
6565 pDoc
->SetString(ScAddress(0,0,0), "='file:///extdata.fake'#Data.A1 & 'file:///extdata.fake'#Data.A2");
6566 CPPUNIT_ASSERT_EQUAL(OUString("Answer: 42"), pDoc
->GetString(ScAddress(0,0,0)));
6569 void Test::testExternalRefFunctions()
6571 ScDocShellRef xExtDocSh
= new ScDocShell
;
6572 xExtDocSh
->SetIsInUcalc();
6573 OUString
aExtDocName("file:///extdata.fake");
6574 SfxMedium
* pMed
= new SfxMedium(aExtDocName
, StreamMode::STD_READWRITE
);
6575 xExtDocSh
->DoInitNew(pMed
);
6576 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6577 findLoadedDocShellByName(aExtDocName
) != nullptr);
6579 ScExternalRefManager
* pRefMgr
= m_pDoc
->GetExternalRefManager();
6580 CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr
);
6581 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aExtDocName
);
6582 const OUString
* pFileName
= pRefMgr
->getExternalFileName(nFileId
);
6583 CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
6584 pFileName
&& *pFileName
== aExtDocName
);
6586 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
6588 // Populate the external source document.
6589 ScDocument
& rExtDoc
= xExtDocSh
->GetDocument();
6590 rExtDoc
.InsertTab(0, "Data");
6592 rExtDoc
.SetValue(0, 0, 0, val
);
6593 // leave cell B1 empty.
6595 rExtDoc
.SetValue(0, 1, 0, val
);
6596 rExtDoc
.SetValue(1, 1, 0, val
);
6598 rExtDoc
.SetValue(0, 2, 0, val
);
6599 rExtDoc
.SetValue(1, 2, 0, val
);
6601 rExtDoc
.SetValue(0, 3, 0, val
);
6602 rExtDoc
.SetValue(1, 3, 0, val
);
6604 m_pDoc
->InsertTab(0, "Test");
6606 static const struct {
6607 const char* pFormula
; double fResult
;
6609 { "=SUM('file:///extdata.fake'#Data.A1:A4)", 10 },
6610 { "=SUM('file:///extdata.fake'#Data.B1:B4)", 9 },
6611 { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
6612 { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
6613 { "=COUNT('file:///extdata.fake'#Data.A1:A4)", 4 },
6614 { "=COUNT('file:///extdata.fake'#Data.B1:B4)", 3 },
6615 // Should not crash, MUST be 0,m_pDoc->MaxRow() and/or 0,m_pDoc->MaxCol() range (here both)
6616 // to yield a result instead of 1x1 error matrix.
6617 { "=SUM('file:///extdata.fake'#Data.1:1048576)", 19 }
6620 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
6622 m_pDoc
->SetString(0, 0, 0, OUString::createFromAscii(aChecks
[i
].pFormula
));
6623 m_pDoc
->GetValue(0, 0, 0, val
);
6624 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("unexpected result involving external ranges.", aChecks
[i
].fResult
, val
, 1e-15);
6627 // A huge external range should not crash, the matrix generated from the
6628 // external range reference should be 1x1 and have one error value.
6629 // XXX NOTE: in case we supported sparse matrix that can hold this large
6630 // areas these tests may be adapted.
6631 m_pDoc
->SetString(0, 0, 0, "=SUM('file:///extdata.fake'#Data.B1:AMJ1048575)");
6632 ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell( ScAddress(0,0,0));
6633 FormulaError nErr
= pFC
->GetErrCode();
6634 CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize
), static_cast<int>(nErr
));
6636 ScMarkData
aMark(MAXROW
, MAXCOL
);
6637 aMark
.SelectOneTable(0);
6638 m_pDoc
->InsertMatrixFormula(0,0,0,0, aMark
, "'file:///extdata.fake'#Data.B1:AMJ1048575");
6639 pFC
= m_pDoc
->GetFormulaCell( ScAddress(0,0,0));
6640 nErr
= pFC
->GetErrCode();
6641 CPPUNIT_ASSERT_EQUAL_MESSAGE("huge external range reference expected to yield FormulaError::MatrixSize", int(FormulaError::MatrixSize
), static_cast<int>(nErr
));
6642 SCSIZE nMatCols
, nMatRows
;
6643 const ScMatrix
* pMat
= pFC
->GetMatrix();
6644 CPPUNIT_ASSERT_MESSAGE("matrix expected", pMat
!= nullptr);
6645 pMat
->GetDimensions( nMatCols
, nMatRows
);
6646 CPPUNIT_ASSERT_MESSAGE("1x1 matrix expected", nMatCols
== 1 && nMatRows
== 1);
6648 pRefMgr
->clearCache(nFileId
);
6649 testExtRefFuncT(m_pDoc
, rExtDoc
);
6650 testExtRefFuncOFFSET(m_pDoc
, rExtDoc
);
6651 testExtRefFuncVLOOKUP(m_pDoc
, rExtDoc
);
6652 testExtRefConcat(m_pDoc
, rExtDoc
);
6654 // Unload the external document shell.
6655 xExtDocSh
->DoClose();
6656 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
6657 !findLoadedDocShellByName(aExtDocName
));
6659 m_pDoc
->DeleteTab(0);
6662 void Test::testExternalRefUnresolved()
6664 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
6665 m_pDoc
->InsertTab(0, "Test");
6667 // Test error propagation of unresolved (not existing document) external
6668 // references. Well, let's hope no build machine has such file with sheet...
6670 const char* aData
[][1] = {
6671 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1" },
6672 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23" },
6673 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\"" },
6674 { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6675 { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6676 { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6677 { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6678 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6679 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1)" },
6680 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1+23)" },
6681 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1&\"W\")" },
6682 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=0" },
6683 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1=\"\"" },
6684 { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1\")" },
6685 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2" },
6686 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23" },
6687 { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\"" },
6688 { "=ISREF('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6689 { "=ISERROR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6690 { "=ISERR('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6691 { "=ISBLANK('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6692 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6693 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2)" },
6694 { "=ISNUMBER('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2+23)" },
6695 { "=ISTEXT('file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2&\"W\")" },
6696 // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=0" },
6697 // TODO: gives Err:504 FIXME { "='file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2=\"\"" },
6698 { "=INDIRECT(\"'file:///NonExistingFilePath/AnyName.ods'#$NoSuchSheet.A1:A2\")" },
6701 ScAddress
aPos(0,0,0);
6702 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
6703 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
6705 std::vector
<std::vector
<const char*>> aOutputCheck
= {
6706 { "#REF!" }, // plain single ref
6708 { "#REF!" }, // &"W"
6709 { "FALSE" }, // ISREF
6710 { "TRUE" }, // ISERROR
6711 { "TRUE" }, // ISERR
6712 { "FALSE" }, // ISBLANK
6713 { "FALSE" }, // ISNUMBER
6714 { "FALSE" }, // ISTEXT
6715 { "FALSE" }, // ISNUMBER
6716 { "FALSE" }, // ISTEXT
6719 { "#REF!" }, // INDIRECT
6720 { "#REF!" }, // A1:A2 range
6722 { "#REF!" }, // &"W"
6723 { "FALSE" }, // ISREF
6724 { "TRUE" }, // ISERROR
6725 { "TRUE" }, // ISERR
6726 { "FALSE" }, // ISBLANK
6727 { "FALSE" }, // ISNUMBER
6728 { "FALSE" }, // ISTEXT
6729 { "FALSE" }, // ISNUMBER
6730 { "FALSE" }, // ISTEXT
6731 // TODO: gives Err:504 FIXME { "#REF!" }, // =0
6732 // TODO: gives Err:504 FIXME { "#REF!" }, // =""
6733 { "#REF!" }, // INDIRECT
6736 bool bSuccess
= checkOutput(m_pDoc
, aRange
, aOutputCheck
, "Check unresolved external reference.");
6737 CPPUNIT_ASSERT_MESSAGE("Unresolved reference check failed", bSuccess
);
6739 m_pDoc
->DeleteTab(0);
6742 void Test::testMatrixOp()
6744 m_pDoc
->InsertTab(0, "Test");
6746 for (SCROW nRow
= 0; nRow
< 4; ++nRow
)
6748 m_pDoc
->SetValue(0, nRow
, 0, nRow
);
6750 m_pDoc
->SetValue(1, 0, 0, 2.0);
6751 m_pDoc
->SetValue(3, 0, 0, 1.0);
6752 m_pDoc
->SetValue(3, 1, 0, 2.0);
6753 m_pDoc
->SetString(2, 0, 0, "=SUMPRODUCT((A1:A4)*B1+D1)");
6754 m_pDoc
->SetString(2, 1, 0, "=SUMPRODUCT((A1:A4)*B1-D2)");
6756 double nVal
= m_pDoc
->GetValue(2, 0, 0);
6757 CPPUNIT_ASSERT_EQUAL(16.0, nVal
);
6759 nVal
= m_pDoc
->GetValue(2, 1, 0);
6760 CPPUNIT_ASSERT_EQUAL(4.0, nVal
);
6762 m_pDoc
->SetString(4, 0, 0, "=SUMPRODUCT({1;2;4}+8)");
6763 m_pDoc
->SetString(4, 1, 0, "=SUMPRODUCT(8+{1;2;4})");
6764 m_pDoc
->SetString(4, 2, 0, "=SUMPRODUCT({1;2;4}-8)");
6765 m_pDoc
->SetString(4, 3, 0, "=SUMPRODUCT(8-{1;2;4})");
6766 m_pDoc
->SetString(4, 4, 0, "=SUMPRODUCT({1;2;4}+{8;16;32})");
6767 m_pDoc
->SetString(4, 5, 0, "=SUMPRODUCT({8;16;32}+{1;2;4})");
6768 m_pDoc
->SetString(4, 6, 0, "=SUMPRODUCT({1;2;4}-{8;16;32})");
6769 m_pDoc
->SetString(4, 7, 0, "=SUMPRODUCT({8;16;32}-{1;2;4})");
6770 double fResult
[8] = { 31.0, 31.0, -17.0, 17.0, 63.0, 63.0, -49.0, 49.0 };
6771 for (size_t i
= 0; i
< SAL_N_ELEMENTS(fResult
); ++i
)
6773 CPPUNIT_ASSERT_EQUAL( fResult
[i
], m_pDoc
->GetValue(4, i
, 0));
6776 m_pDoc
->DeleteTab(0);
6779 void Test::testFuncRangeOp()
6781 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
6783 m_pDoc
->InsertTab(0, "Sheet1");
6784 m_pDoc
->InsertTab(1, "Sheet2");
6785 m_pDoc
->InsertTab(2, "Sheet3");
6788 m_pDoc
->SetValue(1,0,0, 1.0);
6789 m_pDoc
->SetValue(1,1,0, 2.0);
6790 m_pDoc
->SetValue(1,2,0, 4.0);
6792 m_pDoc
->SetValue(1,0,1, 8.0);
6793 m_pDoc
->SetValue(1,1,1, 16.0);
6794 m_pDoc
->SetValue(1,2,1, 32.0);
6796 m_pDoc
->SetValue(1,0,2, 64.0);
6797 m_pDoc
->SetValue(1,1,2, 128.0);
6798 m_pDoc
->SetValue(1,2,2, 256.0);
6800 // Range operator should extend concatenated literal references during
6801 // parse time already, so with this we can test ScComplexRefData::Extend()
6803 // Current sheet is Sheet1, so B1:B2 implies relative Sheet1.B1:B2
6805 ScAddress
aPos(0,0,0);
6806 m_pDoc
->SetString( aPos
, "=SUM(B1:B2:B3)");
6807 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B1:B3)", "Wrong formula.");
6808 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc
->GetValue(aPos
));
6811 m_pDoc
->SetString( aPos
, "=SUM(B1:B3:B2)");
6812 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B1:B3)", "Wrong formula.");
6813 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc
->GetValue(aPos
));
6816 m_pDoc
->SetString( aPos
, "=SUM(B2:B3:B1)");
6817 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B1:B3)", "Wrong formula.");
6818 CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc
->GetValue(aPos
));
6821 m_pDoc
->SetString( aPos
, "=SUM(Sheet2.B1:B2:B3)");
6822 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(Sheet2.B1:B3)", "Wrong formula.");
6823 CPPUNIT_ASSERT_EQUAL( 56.0, m_pDoc
->GetValue(aPos
));
6826 m_pDoc
->SetString( aPos
, "=SUM(B2:B2:Sheet1.B2)");
6827 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(Sheet1.B2:B2)", "Wrong formula.");
6828 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(aPos
));
6831 m_pDoc
->SetString( aPos
, "=SUM(B2:B3:Sheet2.B1)");
6832 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(Sheet1.B1:Sheet2.B3)", "Wrong formula.");
6833 CPPUNIT_ASSERT_EQUAL( 63.0, m_pDoc
->GetValue(aPos
));
6836 m_pDoc
->SetString( aPos
, "=SUM(Sheet1.B1:Sheet2.B2:Sheet3.B3)");
6837 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
6838 CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc
->GetValue(aPos
));
6840 // B1:Sheet2.B2 would be ambiguous, Sheet1.B1:Sheet2.B2 or Sheet2.B1:B2
6841 // The actual representation of the error case may change, so this test may
6842 // have to be adapted.
6844 m_pDoc
->SetString( aPos
, "=SUM(B1:Sheet2.B2:Sheet3.B3)");
6845 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(#REF!.B2:#REF!.B3)", "Wrong formula.");
6846 CPPUNIT_ASSERT_EQUAL( OUString("#REF!"), m_pDoc
->GetString(aPos
));
6849 m_pDoc
->SetString( aPos
, "=SUM(Sheet1.B1:Sheet3.B2:Sheet2.B3)");
6850 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(Sheet1.B1:Sheet3.B3)", "Wrong formula.");
6851 CPPUNIT_ASSERT_EQUAL( 511.0, m_pDoc
->GetValue(aPos
));
6854 m_pDoc
->SetString( aPos
, "=SUM(B$2:B$2:B2)");
6855 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(B$2:B2)", "Wrong formula.");
6856 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(aPos
));
6858 m_pDoc
->DeleteTab(2);
6859 m_pDoc
->DeleteTab(1);
6860 m_pDoc
->DeleteTab(0);
6863 void Test::testFuncFORMULA()
6865 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
6867 m_pDoc
->InsertTab(0, "Sheet1");
6870 const char* aData
[][3] = {
6871 { "=A1", "=FORMULA(B1)", "=FORMULA(B1:B3)" },
6872 { nullptr, "=FORMULA(B2)", "=FORMULA(B1:B3)" },
6873 { "=A3", "=FORMULA(B3)", "=FORMULA(B1:B3)" },
6876 ScAddress
aPos(1,0,0);
6877 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
6878 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
6880 // Checks of C1:D3, where Cy==Dy, and D4:D6
6881 const char* aChecks
[] = {
6886 for (size_t i
=0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
6888 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks
[i
]), m_pDoc
->GetString(2,i
,0));
6889 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks
[i
]), m_pDoc
->GetString(3,i
,0));
6892 // Matrix in D4:D6, no intersection with B1:B3
6893 ScMarkData
aMark(MAXROW
, MAXCOL
);
6894 aMark
.SelectOneTable(0);
6895 m_pDoc
->InsertMatrixFormula(3, 3, 3, 5, aMark
, "=FORMULA(B1:B3)");
6896 for (size_t i
=0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
6898 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aChecks
[i
]), m_pDoc
->GetString(3,i
+3,0));
6901 m_pDoc
->DeleteTab(0);
6904 void Test::testFuncTableRef()
6906 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
6908 m_pDoc
->InsertTab(0, "Sheet1");
6909 ScMarkData
aMark(MAXROW
, MAXCOL
);
6910 aMark
.SelectOneTable(0);
6911 ScDocFunc
& rDocFunc
= getDocShell().GetDocFunc();
6914 ScDBCollection
* pDBs
= m_pDoc
->GetDBCollection();
6915 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs
);
6917 // Insert "table" database range definition for A1:B4, with default
6918 // HasHeader=true and HasTotals=false.
6919 std::unique_ptr
<ScDBData
> pData(new ScDBData( "table", 0,0,0, 1,3));
6920 bool bInserted
= pDBs
->getNamedDBs().insert(std::move(pData
));
6921 CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"table\" database range.", bInserted
);
6925 // Populate "table" database range with headers and data in A1:B4
6926 const char* aData
[][2] = {
6927 { "Header1", "Header2" },
6932 ScAddress
aPos(0,0,0);
6933 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
6934 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
6937 // Named expressions that use Table structured references.
6938 /* TODO: should the item/header separator really be equal to the parameter
6939 * separator, thus be locale dependent and ';' semicolon here, or should it
6940 * be a fixed ',' comma instead? */
6941 static const struct {
6944 const char* pCounta
; // expected result when used in row 2 (first data row) as argument to COUNTA()
6945 const char* pSum3
; // expected result when used in row 3 (second data row) as argument to SUM().
6946 const char* pSum4
; // expected result when used in row 4 (third data row) as argument to SUM().
6947 const char* pSumX
; // expected result when used in row 5 (non-intersecting) as argument to SUM().
6949 { "all", "table[[#All]]", "8", "63", "63", "63" },
6950 { "data_implicit", "table[]", "6", "63", "63", "63" },
6951 { "data", "table[[#Data]]", "6", "63", "63", "63" },
6952 { "headers", "table[[#Headers]]", "2", "0", "0", "0" },
6953 { "header1", "table[[Header1]]", "3", "21", "21", "21" },
6954 { "header2", "table[[Header2]]", "3", "42", "42", "42" },
6955 { "data_header1", "table[[#Data];[Header1]]", "3", "21", "21", "21" },
6956 { "data_header2", "table[[#Data];[Header2]]", "3", "42", "42", "42" },
6957 { "this_row", "table[[#This Row]]", "2", "12", "48", "#VALUE!" },
6958 { "this_row_header1", "table[[#This Row];[Header1]]", "1", "4", "16", "#VALUE!" },
6959 { "this_row_header2", "table[[#This Row];[Header2]]", "1", "8", "32", "#VALUE!" },
6960 { "this_row_range_header_1_to_2", "table[[#This Row];[Header1]:[Header2]]", "2", "12", "48", "#VALUE!" }
6964 // Insert named expressions.
6965 ScRangeName
* pGlobalNames
= m_pDoc
->GetRangeName();
6966 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames
);
6968 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
6970 // Choose base position that does not intersect with the database
6971 // range definition to test later use of [#This Row] results in
6973 ScRangeData
* pName
= new ScRangeData(
6974 m_pDoc
, OUString::createFromAscii(aNames
[i
].pName
), OUString::createFromAscii(aNames
[i
].pExpr
),
6975 ScAddress(2,4,0), ScRangeData::Type::Name
, formula::FormulaGrammar::GRAM_NATIVE
);
6976 bool bInserted
= pGlobalNames
->insert(pName
);
6977 CPPUNIT_ASSERT_MESSAGE(
6978 OString(OStringLiteral("Failed to insert named expression ") + aNames
[i
].pName
+".").getStr(), bInserted
);
6982 // Use the named expressions in COUNTA() formulas, on row 2 that intersects.
6983 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
6985 OUString
aFormula( "=COUNTA(" + OUString::createFromAscii( aNames
[i
].pName
) + ")");
6986 ScAddress
aPos(3+i
,1,0);
6987 m_pDoc
->SetString( aPos
, aFormula
);
6988 // For easier "debugability" have position and formula in assertion.
6989 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
6990 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aNames
[i
].pCounta
)),
6991 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
6994 // Use the named expressions in SUM() formulas, on row 3 that intersects.
6995 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
6997 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aNames
[i
].pName
) + ")");
6998 ScAddress
aPos(3+i
,2,0);
6999 m_pDoc
->SetString( aPos
, aFormula
);
7000 // For easier "debugability" have position and formula in assertion.
7001 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7002 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aNames
[i
].pSum3
)),
7003 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7006 // Use the named expressions in SUM() formulas, on row 4 that intersects.
7007 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
7009 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aNames
[i
].pName
) + ")");
7010 ScAddress
aPos(3+i
,3,0);
7011 m_pDoc
->SetString( aPos
, aFormula
);
7012 // For easier "debugability" have position and formula in assertion.
7013 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7014 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aNames
[i
].pSum4
)),
7015 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7018 // Use the named expressions in SUM() formulas, on row 5 that does not intersect.
7019 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
7021 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aNames
[i
].pName
) + ")");
7022 ScAddress
aPos(3+i
,4,0);
7023 m_pDoc
->SetString( aPos
, aFormula
);
7024 // For easier "debugability" have position and formula in assertion.
7025 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7026 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aNames
[i
].pSumX
)),
7027 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7030 // Insert a column at column B to extend database range from column A,B to
7031 // A,B,C. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7032 rDocFunc
.InsertCells(ScRange(1,0,0,1,m_pDoc
->MaxRow(),0), &aMark
, INS_INSCOLS_BEFORE
, false, true);
7034 // Re-verify the named expression in SUM() formula, on row 4 that
7035 // intersects, now starting at column E, still works.
7037 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aNames
); ++i
)
7039 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aNames
[i
].pName
) + ")");
7040 ScAddress
aPos(4+i
,3,0);
7041 // For easier "debugability" have position and formula in assertion.
7042 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7043 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aNames
[i
].pSum4
)),
7044 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7047 const char* pColumn2Formula
= "=SUM(table[[#Data];[Column2]])";
7049 // Populate "table" database range with empty header and data in newly
7050 // inserted column, B1:B4 plus a table formula in B6. The empty header
7051 // should result in the internal table column name "Column2" that is
7052 // used in the formula.
7053 const char* aData
[][1] = {
7061 ScAddress
aPos(1,0,0);
7062 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
7063 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
7066 // Verify the formula result in B6 (64+128+256=448).
7068 OUString
aFormula( OUString::createFromAscii( pColumn2Formula
));
7069 ScAddress
aPos(1,5,0);
7070 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7071 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ "448"), OUString(aPrefix
+ m_pDoc
->GetString(aPos
)));
7074 // Set header in column B. Use ScDocFunc to have table column names refreshed.
7075 rDocFunc
.SetStringCell(ScAddress(1,0,0), "NewHeader",true);
7076 // Verify that formula adapted using the updated table column names.
7077 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader]])", "Wrong formula");
7079 // Set header in column A to identical string. Internal table column name
7080 // for B should get a "2" appended.
7081 rDocFunc
.SetStringCell(ScAddress(0,0,0), "NewHeader",true);
7082 // Verify that formula adapted using the updated table column names.
7083 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7085 // Set header in column B to empty string, effectively clearing the cell.
7086 rDocFunc
.SetStringCell(ScAddress(1,0,0), "",true);
7087 // Verify that formula is still using the previous table column name.
7088 ASSERT_FORMULA_EQUAL(*m_pDoc
, ScAddress(1,5,0), "SUM(table[[#Data];[NewHeader2]])", "Wrong formula");
7090 // === header-less ===
7093 ScDBCollection
* pDBs
= m_pDoc
->GetDBCollection();
7094 CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs
);
7096 // Insert "headerless" database range definition for E10:F12, without headers.
7097 std::unique_ptr
<ScDBData
> pData(new ScDBData( "hltable", 0, 4,9, 5,11, true, false));
7098 bool bInserted
= pDBs
->getNamedDBs().insert(std::move(pData
));
7099 CPPUNIT_ASSERT_MESSAGE( "Failed to insert \"hltable\" database range.", bInserted
);
7103 // Populate "hltable" database range with data in E10:F12
7104 const char* aData
[][2] = {
7109 ScAddress
aPos(4,9,0);
7110 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
7111 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
7114 // Named expressions that use header-less Table structured references.
7115 static const struct {
7118 const char* pCounta
; // expected result when used in row 10 (first data row) as argument to COUNTA()
7119 const char* pSum3
; // expected result when used in row 11 (second data row) as argument to SUM().
7120 const char* pSum4
; // expected result when used in row 12 (third data row) as argument to SUM().
7121 const char* pSumX
; // expected result when used in row 13 (non-intersecting) as argument to SUM().
7123 { "hl_all", "hltable[[#All]]", "6", "63", "63", "63" },
7124 { "hl_data_implicit", "hltable[]", "6", "63", "63", "63" },
7125 { "hl_data", "hltable[[#Data]]", "6", "63", "63", "63" },
7126 { "hl_headers", "hltable[[#Headers]]", "1", "#REF!", "#REF!", "#REF!" },
7127 { "hl_column1", "hltable[[Column1]]", "3", "21", "21", "21" },
7128 { "hl_column2", "hltable[[Column2]]", "3", "42", "42", "42" },
7129 { "hl_data_column1", "hltable[[#Data];[Column1]]", "3", "21", "21", "21" },
7130 { "hl_data_column2", "hltable[[#Data];[Column2]]", "3", "42", "42", "42" },
7131 { "hl_this_row", "hltable[[#This Row]]", "2", "12", "48", "#VALUE!" },
7132 { "hl_this_row_column1", "hltable[[#This Row];[Column1]]", "1", "4", "16", "#VALUE!" },
7133 { "hl_this_row_column2", "hltable[[#This Row];[Column2]]", "1", "8", "32", "#VALUE!" },
7134 { "hl_this_row_range_column_1_to_2", "hltable[[#This Row];[Column1]:[Column2]]", "2", "12", "48", "#VALUE!" }
7138 // Insert named expressions.
7139 ScRangeName
* pGlobalNames
= m_pDoc
->GetRangeName();
7140 CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames
);
7142 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHlNames
); ++i
)
7144 // Choose base position that does not intersect with the database
7145 // range definition to test later use of [#This Row] results in
7147 ScRangeData
* pName
= new ScRangeData(
7148 m_pDoc
, OUString::createFromAscii(aHlNames
[i
].pName
), OUString::createFromAscii(aHlNames
[i
].pExpr
),
7149 ScAddress(6,12,0), ScRangeData::Type::Name
, formula::FormulaGrammar::GRAM_NATIVE
);
7150 bool bInserted
= pGlobalNames
->insert(pName
);
7151 CPPUNIT_ASSERT_MESSAGE(
7152 OString(OStringLiteral("Failed to insert named expression ") + aHlNames
[i
].pName
+".").getStr(), bInserted
);
7156 // Use the named expressions in COUNTA() formulas, on row 10 that intersects.
7157 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHlNames
); ++i
)
7159 OUString
aFormula( "=COUNTA(" + OUString::createFromAscii( aHlNames
[i
].pName
) + ")");
7160 ScAddress
aPos(7+i
,9,0);
7161 m_pDoc
->SetString( aPos
, aFormula
);
7162 // For easier "debugability" have position and formula in assertion.
7163 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7164 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aHlNames
[i
].pCounta
)),
7165 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7168 // Use the named expressions in SUM() formulas, on row 11 that intersects.
7169 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHlNames
); ++i
)
7171 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aHlNames
[i
].pName
) + ")");
7172 ScAddress
aPos(7+i
,10,0);
7173 m_pDoc
->SetString( aPos
, aFormula
);
7174 // For easier "debugability" have position and formula in assertion.
7175 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7176 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aHlNames
[i
].pSum3
)),
7177 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7180 // Use the named expressions in SUM() formulas, on row 12 that intersects.
7181 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHlNames
); ++i
)
7183 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aHlNames
[i
].pName
) + ")");
7184 ScAddress
aPos(7+i
,11,0);
7185 m_pDoc
->SetString( aPos
, aFormula
);
7186 // For easier "debugability" have position and formula in assertion.
7187 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7188 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aHlNames
[i
].pSum4
)),
7189 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7192 // Use the named expressions in SUM() formulas, on row 13 that does not intersect.
7193 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHlNames
); ++i
)
7195 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aHlNames
[i
].pName
) + ")");
7196 ScAddress
aPos(7+i
,12,0);
7197 m_pDoc
->SetString( aPos
, aFormula
);
7198 // For easier "debugability" have position and formula in assertion.
7199 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7200 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aHlNames
[i
].pSumX
)),
7201 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7204 // Insert a column at column F to extend database range from column E,F to
7205 // E,F,G. Use ScDocFunc so RefreshDirtyTableColumnNames() is called.
7206 rDocFunc
.InsertCells(ScRange(5,0,0,5,m_pDoc
->MaxRow(),0), &aMark
, INS_INSCOLS_BEFORE
, false, true);
7208 // Re-verify the named expression in SUM() formula, on row 12 that
7209 // intersects, now starting at column I, still works.
7211 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aHlNames
); ++i
)
7213 OUString
aFormula( "=SUM(" + OUString::createFromAscii( aHlNames
[i
].pName
) + ")");
7214 ScAddress
aPos(8+i
,11,0);
7215 // For easier "debugability" have position and formula in assertion.
7216 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7217 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ OUString::createFromAscii( aHlNames
[i
].pSum4
)),
7218 OUString(aPrefix
+ m_pDoc
->GetString( aPos
)));
7221 const char* pColumn3Formula
= "=SUM(hltable[[#Data];[Column3]])";
7223 // Populate "hltable" database range with data in newly inserted
7224 // column, F10:F12 plus a table formula in F14. The new header should
7225 // result in the internal table column name "Column3" that is used in
7227 const char* aData
[][1] = {
7234 ScAddress
aPos(5,9,0);
7235 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
7236 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
7239 // Verify the formula result in F14 (64+128+256=448).
7241 OUString
aFormula( OUString::createFromAscii( pColumn3Formula
));
7242 ScAddress
aPos(5,13,0);
7243 OUString
aPrefix( aPos
.Format(ScRefFlags::VALID
) + " " + aFormula
+ " : ");
7244 CPPUNIT_ASSERT_EQUAL( OUString(aPrefix
+ "448"), OUString(aPrefix
+ m_pDoc
->GetString(aPos
)));
7247 m_pDoc
->DeleteTab(0);
7250 void Test::testFuncFTEST()
7252 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7254 m_pDoc
->InsertTab(0, "FTest");
7256 ScAddress
aPos(6,0,0);
7257 m_pDoc
->SetString(aPos
, "=FTEST(A1:C3;D1:F3)");
7258 m_pDoc
->SetValue(0, 0, 0, 9.0); // A1
7259 OUString aVal
= m_pDoc
->GetString(aPos
);
7260 CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7261 OUString("#VALUE!"), aVal
);
7262 m_pDoc
->SetValue(0, 1, 0, 8.0); // A2
7263 aVal
= m_pDoc
->GetString(aPos
);
7264 CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7265 OUString("#VALUE!"), aVal
);
7266 m_pDoc
->SetValue(3, 0, 0, 5.0); // D1
7267 aVal
= m_pDoc
->GetString(aPos
);
7268 CPPUNIT_ASSERT_EQUAL_MESSAGE("FTEST should return #VALUE! for less than 2 values",
7269 OUString("#VALUE!"), aVal
);
7270 m_pDoc
->SetValue(3, 1, 0, 6.0); // D2
7271 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 1.0000, m_pDoc
->GetValue(aPos
), 10e-4);
7272 m_pDoc
->SetValue(1, 0, 0, 6.0); // B1
7273 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.6222, m_pDoc
->GetValue(aPos
), 10e-4);
7274 m_pDoc
->SetValue(1, 1, 0, 8.0); // B2
7275 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.7732, m_pDoc
->GetValue(aPos
), 10e-4);
7276 m_pDoc
->SetValue(4, 0, 0, 7.0); // E1
7277 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.8194, m_pDoc
->GetValue(aPos
), 10e-4);
7278 m_pDoc
->SetValue(4, 1, 0, 4.0); // E2
7279 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9674, m_pDoc
->GetValue(aPos
), 10e-4);
7280 m_pDoc
->SetValue(2, 0, 0, 3.0); // C1
7281 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.3402, m_pDoc
->GetValue(aPos
), 10e-4);
7282 m_pDoc
->SetValue(5, 0, 0, 28.0); // F1
7283 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc
->GetValue(aPos
), 10e-4);
7284 m_pDoc
->SetValue(2, 1, 0, 9.0); // C2
7285 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0063, m_pDoc
->GetValue(aPos
), 10e-4);
7286 m_pDoc
->SetValue(5, 1, 0, 4.0); // F2
7287 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0081, m_pDoc
->GetValue(aPos
), 10e-4);
7288 m_pDoc
->SetValue(0, 2, 0, 2.0); // A3
7289 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0122, m_pDoc
->GetValue(aPos
), 10e-4);
7290 m_pDoc
->SetValue(3, 2, 0, 8.0); // D3
7291 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0178, m_pDoc
->GetValue(aPos
), 10e-4);
7292 m_pDoc
->SetValue(1, 2, 0, 4.0); // B3
7293 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0093, m_pDoc
->GetValue(aPos
), 10e-4);
7294 m_pDoc
->SetValue(4, 2, 0, 7.0); // E3
7295 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0132, m_pDoc
->GetValue(aPos
), 10e-4);
7296 m_pDoc
->SetValue(5, 2, 0, 5.0); // F3
7297 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0168, m_pDoc
->GetValue(aPos
), 10e-4);
7298 m_pDoc
->SetValue(2, 2, 0, 13.0); // C3
7299 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0422, m_pDoc
->GetValue(aPos
), 10e-4);
7301 m_pDoc
->SetString(0, 2, 0, "a"); // A3
7302 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0334, m_pDoc
->GetValue(aPos
), 10e-4);
7303 m_pDoc
->SetString(2, 0, 0, "b"); // C1
7304 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0261, m_pDoc
->GetValue(aPos
), 10e-4);
7305 m_pDoc
->SetString(5, 1, 0, "c"); // F2
7306 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0219, m_pDoc
->GetValue(aPos
), 10e-4);
7307 m_pDoc
->SetString(4, 2, 0, "d"); // E3
7308 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0161, m_pDoc
->GetValue(aPos
), 10e-4);
7309 m_pDoc
->SetString(3, 2, 0, "e"); // D3
7310 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.0110, m_pDoc
->GetValue(aPos
), 10e-4);
7312 m_pDoc
->DeleteTab(0);
7313 m_pDoc
->InsertTab(0, "FTest2");
7315 /* Summary of the following test
7316 A1:A5 = SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7317 A6:A10 = -SQRT(C1*9/10)*{ 1.0, 1.0, 1.0, 1.0, 1.0 };
7318 B1:B10 = SQRT(C2*19/20)*{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
7319 B11:B20 = -SQRT(C2*19/20)*{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
7320 C1 = POWER(1.5, D1) ; This is going to be the sample variance of the vector A1:A10
7321 C2 = POWER(1.5, D2) ; This is going to be the sample variance of the vector B1:B20
7322 D1 and D2 are varied over { -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 }
7324 Result of FTEST(A1:A10;B1:B20) in Calc is compared with that from Octave's var_test() function for each value of D1 and D2.
7326 The minimum variance ratio obtained in this way is 0.017342 and the maximum variance ratio is 57.665039
7329 const size_t nNumParams
= 11;
7330 const double fParameter
[nNumParams
] = { -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
7332 // Results of var_test() from Octave
7333 const double fResults
[nNumParams
][nNumParams
] = {
7334 { 0.9451191535603041,0.5429768686792684,0.213130093422756,0.06607644828558357,0.0169804365506927,0.003790723514148109,
7335 0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06,7.495090956766148e-07 },
7336 { 0.4360331979746912,0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927,
7337 0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05,4.436218417280813e-06 },
7338 { 0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.2131300934227565,0.06607644828558357,
7339 0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777,2.566562398786942e-05 },
7340 { 0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603058,0.5429768686792689,0.2131300934227565,
7341 0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703,0.0001435746909905777 },
7342 { 0.002886791075972228,0.02453502500565108,0.1309752286653514,0.4360331979746914,0.9451191535603041,0.5429768686792689,
7343 0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109,0.0007645345628801703 },
7344 { 0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746912,0.9451191535603036,
7345 0.5429768686792689,0.2131300934227565,0.06607644828558357,0.0169804365506927,0.003790723514148109 },
7346 { 1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,
7347 0.9451191535603054,0.5429768686792684,0.2131300934227565,0.06607644828558357,0.0169804365506927 },
7348 { 5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,0.02453502500565108,
7349 0.1309752286653509,0.4360331979746914,0.9451191535603058,0.5429768686792684,0.213130093422756,0.06607644828558357 },
7350 { 1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,0.002886791075972228,
7351 0.02453502500565086,0.1309752286653509,0.4360331979746914,0.9451191535603041,0.5429768686792684,0.2131300934227565 },
7352 { 5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,0.0002237196492846927,
7353 0.002886791075972228,0.02453502500565108,0.1309752286653516,0.4360331979746914,0.9451191535603058,0.5429768686792684 },
7354 { 1.354649725726631e-11,5.111255862999542e-10,1.739106880727093e-08,5.109390206481379e-07,1.224926820153627e-05,
7355 0.0002237196492846927,0.002886791075972228,0.02453502500565108,0.1309752286653509,0.4360331979746914,0.9451191535603054 }
7358 m_pDoc
->SetValue(3, 0, 0, fParameter
[0]); // D1
7359 m_pDoc
->SetValue(3, 1, 0, fParameter
[0]); // D2
7360 aPos
.Set(2,0,0); // C1
7361 m_pDoc
->SetString(aPos
, "=POWER(1.5;D1)" ); // C1
7362 aPos
.Set(2, 1, 0); // C2
7363 m_pDoc
->SetString(aPos
, "=POWER(1.5;D2)" ); // C2
7364 for ( SCROW nRow
= 0; nRow
< 5; ++nRow
) // Set A1:A5 = SQRT(C1*9/10), and A6:A10 = -SQRT(C1*9/10)
7366 aPos
.Set(0, nRow
, 0);
7367 m_pDoc
->SetString(aPos
, "=SQRT(C1*9/10)");
7368 aPos
.Set(0, nRow
+ 5, 0);
7369 m_pDoc
->SetString(aPos
, "=-SQRT(C1*9/10)");
7372 for ( SCROW nRow
= 0; nRow
< 10; ++nRow
) // Set B1:B10 = SQRT(C2*19/20), and B11:B20 = -SQRT(C2*19/20)
7374 aPos
.Set(1, nRow
, 0);
7375 m_pDoc
->SetString(aPos
, "=SQRT(C2*19/20)");
7376 aPos
.Set(1, nRow
+ 10, 0);
7377 m_pDoc
->SetString(aPos
, "=-SQRT(C2*19/20)");
7380 aPos
.Set(4, 0, 0); // E1
7381 m_pDoc
->SetString(aPos
, "=FTEST(A1:A10;B1:B20)");
7382 aPos
.Set(4, 1, 0); // E2
7383 m_pDoc
->SetString(aPos
, "=FTEST(B1:B20;A1:A10)");
7385 ScAddress
aPosRev(4, 1, 0); // E2
7386 aPos
.Set(4, 0, 0); // E1
7388 for ( size_t nFirstIdx
= 0; nFirstIdx
< nNumParams
; ++nFirstIdx
)
7390 m_pDoc
->SetValue(3, 0, 0, fParameter
[nFirstIdx
]); // Set D1
7391 for ( size_t nSecondIdx
= 0; nSecondIdx
< nNumParams
; ++nSecondIdx
)
7393 m_pDoc
->SetValue(3, 1, 0, fParameter
[nSecondIdx
]); // Set D2
7394 double fExpected
= fResults
[nFirstIdx
][nSecondIdx
];
7395 // Here a dynamic error limit is used. This is to handle correctly when the expected value is lower than the fixed error limit of 10e-5
7396 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected
, m_pDoc
->GetValue(aPos
), std::min(10e-5, fExpected
*0.0001) );
7397 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", fExpected
, m_pDoc
->GetValue(aPosRev
), std::min(10e-5, fExpected
*0.0001) );
7400 m_pDoc
->DeleteTab(0);
7403 void Test::testFuncFTESTBug()
7405 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7407 m_pDoc
->InsertTab(0, "FTest");
7409 ScAddress
aPos(9,0,0);
7410 m_pDoc
->SetString(aPos
, "=FTEST(H1:H3;I1:I3)");
7412 m_pDoc
->SetValue(7, 0, 0, 9.0); // H1
7413 m_pDoc
->SetValue(7, 1, 0, 8.0); // H2
7414 m_pDoc
->SetValue(7, 2, 0, 6.0); // H3
7415 m_pDoc
->SetValue(8, 0, 0, 5.0); // I1
7416 m_pDoc
->SetValue(8, 1, 0, 7.0); // I2
7418 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of FTEST failed", 0.9046, m_pDoc
->GetValue(aPos
), 10e-4);
7420 m_pDoc
->DeleteTab(0);
7423 void Test::testFuncCHITEST()
7425 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7427 m_pDoc
->InsertTab(0, "ChiTest");
7429 ScAddress
aPos(6,0,0);
7430 // 2x2 matrices test
7431 m_pDoc
->SetString(aPos
, "=CHITEST(A1:B2;D1:E2)");
7432 OUString aVal
= m_pDoc
->GetString(aPos
);
7433 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with empty cells",
7434 OUString("Err:502"), aVal
);
7436 m_pDoc
->SetValue(0, 0, 0, 1.0); // A1
7437 m_pDoc
->SetValue(0, 1, 0, 2.0); // A2
7438 m_pDoc
->SetValue(1, 0, 0, 2.0); // B1
7439 m_pDoc
->SetValue(1, 1, 0, 1.0); // B2
7440 aVal
= m_pDoc
->GetString(aPos
);
7441 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrix with empty cells",
7442 OUString("Err:502"), aVal
);
7444 m_pDoc
->SetValue(3, 0, 0, 2.0); // D1
7445 m_pDoc
->SetValue(3, 1, 0, 3.0); // D2
7446 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc
->GetValue(aPos
), 10e-4);
7448 m_pDoc
->SetValue(4, 1, 0, 1.0); // E2
7449 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.3613, m_pDoc
->GetValue(aPos
), 10e-4);
7450 m_pDoc
->SetValue(4, 0, 0, 3.0); // E1
7451 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.2801, m_pDoc
->GetValue(aPos
), 10e-4);
7452 m_pDoc
->SetValue(4, 0, 0, 0.0); // E1
7453 aVal
= m_pDoc
->GetString(aPos
);
7454 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0 for expected values of 0", OUString("#DIV/0!"), aVal
);
7455 m_pDoc
->SetValue(4, 0, 0, 3.0); // E1
7456 m_pDoc
->SetValue(1, 1, 0, 0.0); // B2
7457 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1410, m_pDoc
->GetValue(aPos
), 10e-4);
7459 // 3x3 matrices test
7460 m_pDoc
->SetString(aPos
, "=CHITEST(A1:C3;D1:F3)");
7461 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.7051, m_pDoc
->GetValue(aPos
), 10e-4);
7463 m_pDoc
->SetValue(2, 0, 0, 3.0); // C1
7464 m_pDoc
->SetValue(2, 1, 0, 2.0); // C2
7465 m_pDoc
->SetValue(2, 2, 0, 3.0); // C3
7466 m_pDoc
->SetValue(0, 2, 0, 4.0); // A3
7467 m_pDoc
->SetValue(1, 2, 0, 2.0); // B3
7468 m_pDoc
->SetValue(5, 0, 0, 1.0); // F1
7469 m_pDoc
->SetValue(5, 1, 0, 2.0); // F2
7470 m_pDoc
->SetValue(5, 2, 0, 3.0); // F3
7471 m_pDoc
->SetValue(3, 2, 0, 3.0); // D3
7472 m_pDoc
->SetValue(4, 2, 0, 1.0); // E3
7473 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc
->GetValue(aPos
), 10e-4);
7475 // test with strings
7476 m_pDoc
->SetString(4, 2, 0, "a"); // E3
7477 aVal
= m_pDoc
->GetString(aPos
);
7478 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
7479 OUString("Err:502"), aVal
);
7480 m_pDoc
->SetString(1, 2, 0, "a"); // B3
7481 aVal
= m_pDoc
->GetString(aPos
);
7482 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
7483 OUString("Err:502"), aVal
);
7484 m_pDoc
->SetValue(4, 2, 0, 1.0); // E3
7485 aVal
= m_pDoc
->GetString(aPos
);
7486 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return Err:502 for matrices with strings",
7487 OUString("Err:502"), aVal
);
7488 m_pDoc
->SetValue(1, 2, 0, 2.0); // B3
7489 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1117, m_pDoc
->GetValue(aPos
), 10e-4);
7491 m_pDoc
->SetValue(4, 1, 0, 5.0); // E2
7492 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0215, m_pDoc
->GetValue(aPos
), 10e-4);
7493 m_pDoc
->SetValue(1, 2, 0, 1.0); // B3
7494 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0328, m_pDoc
->GetValue(aPos
), 10e-4);
7495 m_pDoc
->SetValue(5, 0, 0, 3.0); // F1
7496 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1648, m_pDoc
->GetValue(aPos
), 10e-4);
7497 m_pDoc
->SetValue(0, 1, 0, 3.0); // A2
7498 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1870, m_pDoc
->GetValue(aPos
), 10e-4);
7499 m_pDoc
->SetValue(3, 1, 0, 5.0); // D2
7500 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1377, m_pDoc
->GetValue(aPos
), 10e-4);
7501 m_pDoc
->SetValue(3, 2, 0, 4.0); // D3
7502 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.1566, m_pDoc
->GetValue(aPos
), 10e-4);
7504 m_pDoc
->SetValue(0, 0, 0, 0.0); // A1
7505 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0868, m_pDoc
->GetValue(aPos
), 10e-4);
7507 // no convergence error
7508 m_pDoc
->SetValue(4, 0, 0, 1.0E308
); // E1
7509 aVal
= m_pDoc
->GetString(aPos
);
7510 CPPUNIT_ASSERT_EQUAL(OUString("Err:523"), aVal
);
7511 m_pDoc
->SetValue(4, 0, 0, 3.0); // E1
7513 // zero in all cells
7514 m_pDoc
->SetValue(0, 1, 0, 0.0); // A2
7515 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0150, m_pDoc
->GetValue(aPos
), 10e-4);
7516 m_pDoc
->SetValue(0, 2, 0, 0.0); // A3
7517 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0026, m_pDoc
->GetValue(aPos
), 10e-4);
7518 m_pDoc
->SetValue(1, 0, 0, 0.0); // B1
7519 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.00079, m_pDoc
->GetValue(aPos
), 10e-5);
7520 m_pDoc
->SetValue(1, 2, 0, 0.0); // B3
7521 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0005, m_pDoc
->GetValue(aPos
), 10e-4);
7522 m_pDoc
->SetValue(2, 0, 0, 0.0); // C1
7523 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of CHITEST failed", 0.0001, m_pDoc
->GetValue(aPos
), 10e-4);
7524 m_pDoc
->SetValue(2, 1, 0, 0.0); // C2
7525 m_pDoc
->SetValue(2, 2, 0, 0.0); // C3
7526 m_pDoc
->SetValue(3, 0, 0, 0.0); // D1
7527 aVal
= m_pDoc
->GetString(aPos
);
7528 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
7529 OUString("#DIV/0!"), aVal
);
7530 m_pDoc
->SetValue(3, 1, 0, 0.0); // D2
7531 m_pDoc
->SetValue(3, 2, 0, 0.0); // D3
7532 m_pDoc
->SetValue(4, 0, 0, 0.0); // E1
7533 m_pDoc
->SetValue(4, 1, 0, 0.0); // E2
7534 m_pDoc
->SetValue(4, 2, 0, 0.0); // E3
7535 m_pDoc
->SetValue(5, 0, 0, 0.0); // F1
7536 m_pDoc
->SetValue(5, 1, 0, 0.0); // F2
7537 m_pDoc
->SetValue(5, 2, 0, 0.0); // F3
7538 aVal
= m_pDoc
->GetString(aPos
);
7539 CPPUNIT_ASSERT_EQUAL_MESSAGE("CHITEST should return #DIV/0! for matrices with empty",
7540 OUString("#DIV/0!"), aVal
);
7542 m_pDoc
->DeleteTab(0);
7545 void Test::testFuncTTEST()
7547 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7549 m_pDoc
->InsertTab(0, "TTest");
7551 ScAddress
aPos(6,0,0);
7552 // type 1, mode/tails 1
7553 m_pDoc
->SetString(aPos
, "=TTEST(A1:C3;D1:F3;1;1)");
7554 OUString aVal
= m_pDoc
->GetString(aPos
);
7555 CPPUNIT_ASSERT_EQUAL_MESSAGE("TTEST should return #VALUE! for empty matrices",
7556 OUString("#VALUE!"), aVal
);
7558 m_pDoc
->SetValue(0, 0, 0, 8.0); // A1
7559 m_pDoc
->SetValue(1, 0, 0, 2.0); // B1
7560 m_pDoc
->SetValue(3, 0, 0, 3.0); // D1
7561 m_pDoc
->SetValue(4, 0, 0, 1.0); // E1
7562 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc
->GetValue(aPos
), 10e-5);
7563 m_pDoc
->SetValue(2, 0, 0, 1.0); // C1
7564 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.18717, m_pDoc
->GetValue(aPos
), 10e-5);
7565 m_pDoc
->SetValue(5, 0, 0, 6.0); // F1
7566 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc
->GetValue(aPos
), 10e-5);
7567 m_pDoc
->SetValue(0, 1, 0, -4.0); // A2
7568 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.45958, m_pDoc
->GetValue(aPos
), 10e-5);
7569 m_pDoc
->SetValue(3, 1, 0, 1.0); // D2
7570 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc
->GetValue(aPos
), 10e-5);
7571 m_pDoc
->SetValue(1, 1, 0, 5.0); // B2
7572 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.35524, m_pDoc
->GetValue(aPos
), 10e-5);
7573 m_pDoc
->SetValue(4, 1, 0, -2.0); // E2
7574 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc
->GetValue(aPos
), 10e-5);
7575 m_pDoc
->SetValue(2, 1, 0, -1.0); // C2
7576 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.41043, m_pDoc
->GetValue(aPos
), 10e-5);
7577 m_pDoc
->SetValue(5, 1, 0, -3.0); // F2
7578 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc
->GetValue(aPos
), 10e-5);
7579 m_pDoc
->SetValue(0, 2, 0, 10.0); // A3
7580 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34990, m_pDoc
->GetValue(aPos
), 10e-5);
7581 m_pDoc
->SetValue(3, 2, 0, 10.0); // D3
7582 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc
->GetValue(aPos
), 10e-5);
7583 m_pDoc
->SetValue(1, 2, 0, 3.0); // B3
7584 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.34686, m_pDoc
->GetValue(aPos
), 10e-5);
7585 m_pDoc
->SetValue(4, 2, 0, 9.0); // E3
7586 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc
->GetValue(aPos
), 10e-5);
7587 m_pDoc
->SetValue(2, 2, 0, -5.0); // C3
7588 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.47198, m_pDoc
->GetValue(aPos
), 10e-5);
7589 m_pDoc
->SetValue(5, 2, 0, 6.0); // F3
7590 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25529, m_pDoc
->GetValue(aPos
), 10e-5);
7592 m_pDoc
->SetString(1, 1, 0, "a"); // B2
7593 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc
->GetValue(aPos
), 10e-5);
7594 m_pDoc
->SetString(4, 1, 0, "b"); // E2
7595 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12016, m_pDoc
->GetValue(aPos
), 10e-5);
7596 m_pDoc
->SetString(2, 2, 0, "c"); // C3
7597 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.25030, m_pDoc
->GetValue(aPos
), 10e-5);
7598 m_pDoc
->SetString(5, 1, 0, "d"); // F2
7599 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.19637, m_pDoc
->GetValue(aPos
), 10e-5);
7601 // type 1, mode/tails 2
7602 m_pDoc
->SetString(aPos
, "=TTEST(A1:C3;D1:F3;2;1)");
7603 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc
->GetValue(aPos
), 10e-5);
7604 m_pDoc
->SetValue(1, 1, 0, 4.0); // B2
7605 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.39273, m_pDoc
->GetValue(aPos
), 10e-5);
7606 m_pDoc
->SetValue(4, 1, 0, 3.0); // E2
7607 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43970, m_pDoc
->GetValue(aPos
), 10e-5);
7608 m_pDoc
->SetValue(2, 2, 0, -2.0); // C3
7609 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22217, m_pDoc
->GetValue(aPos
), 10e-5);
7610 m_pDoc
->SetValue(5, 1, 0, -10.0); // F2
7611 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.64668, m_pDoc
->GetValue(aPos
), 10e-5);
7612 m_pDoc
->SetValue(0, 1, 0, 3.0); // A2
7613 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.95266, m_pDoc
->GetValue(aPos
), 10e-5);
7614 m_pDoc
->SetValue(3, 2, 0, -1.0); // D3
7615 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62636, m_pDoc
->GetValue(aPos
), 10e-5);
7617 // type 2, mode/tails 2
7618 m_pDoc
->SetString(aPos
, "=TTEST(A1:C3;D1:F3;2;2)");
7619 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.62549, m_pDoc
->GetValue(aPos
), 10e-5);
7620 m_pDoc
->SetValue(5, 1, 0, -1.0); // F2
7621 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.94952, m_pDoc
->GetValue(aPos
), 10e-5);
7622 m_pDoc
->SetValue(2, 2, 0, 5.0); // C3
7623 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.58876, m_pDoc
->GetValue(aPos
), 10e-5);
7624 m_pDoc
->SetValue(2, 1, 0, 2.0); // C2
7625 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.43205, m_pDoc
->GetValue(aPos
), 10e-5);
7626 m_pDoc
->SetValue(3, 2, 0, -4.0); // D3
7627 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36165, m_pDoc
->GetValue(aPos
), 10e-5);
7628 m_pDoc
->SetValue(0, 1, 0, 1.0); // A2
7629 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.44207, m_pDoc
->GetValue(aPos
), 10e-5);
7631 // type 3, mode/tails 1
7632 m_pDoc
->SetString(aPos
, "=TTEST(A1:C3;D1:F3;1;3)");
7633 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.22132, m_pDoc
->GetValue(aPos
), 10e-5);
7634 m_pDoc
->SetValue(0, 0, 0, 1.0); // A1
7635 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.36977, m_pDoc
->GetValue(aPos
), 10e-5);
7636 m_pDoc
->SetValue(0, 2, 0, -30.0); // A3
7637 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16871, m_pDoc
->GetValue(aPos
), 10e-5);
7638 m_pDoc
->SetValue(3, 1, 0, 5.0); // D2
7639 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.14396, m_pDoc
->GetValue(aPos
), 10e-5);
7640 m_pDoc
->SetValue(5, 1, 0, 2.0); // F2
7641 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.12590, m_pDoc
->GetValue(aPos
), 10e-5);
7642 m_pDoc
->SetValue(4, 2, 0, 2.0); // E3
7643 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.16424, m_pDoc
->GetValue(aPos
), 10e-5);
7644 m_pDoc
->SetValue(5, 0, 0, -1.0); // F1
7645 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of TTEST failed", 0.21472, m_pDoc
->GetValue(aPos
), 10e-5);
7647 m_pDoc
->DeleteTab(0);
7650 void Test::testFuncSUMX2PY2()
7652 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7654 m_pDoc
->InsertTab(0, "SumX2PY2 Test");
7657 ScAddress
aPos(6,0,0);
7658 m_pDoc
->SetString(aPos
, "=SUMX2PY2(A1:C3;D1:F3)");
7659 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc
->GetValue(aPos
));
7661 m_pDoc
->SetValue(0, 0, 0, 1.0); // A1
7662 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 0.0, m_pDoc
->GetValue(aPos
));
7663 m_pDoc
->SetValue(3, 0, 0, 2.0); // D1
7664 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc
->GetValue(aPos
));
7665 m_pDoc
->SetValue(1, 0, 0, 2.0); // B1
7666 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 5.0, m_pDoc
->GetValue(aPos
));
7667 m_pDoc
->SetValue(4, 0, 0, 0.0); // E1
7668 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 9.0, m_pDoc
->GetValue(aPos
));
7669 m_pDoc
->SetValue(2, 0, 0, 3.0); // C1
7670 m_pDoc
->SetValue(5, 0, 0, 3.0); // F1
7671 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 27.0, m_pDoc
->GetValue(aPos
));
7672 m_pDoc
->SetValue(0, 1, 0, 10.0); // A2
7673 m_pDoc
->SetValue(3, 1, 0, -10.0); // D2
7674 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc
->GetValue(aPos
));
7675 m_pDoc
->SetValue(1, 1, 0, -5.0); // B2
7676 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 227.0, m_pDoc
->GetValue(aPos
));
7677 m_pDoc
->SetValue(4, 1, 0, -5.0); // E2
7678 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc
->GetValue(aPos
));
7679 m_pDoc
->SetValue(2, 1, 0, 0.0); // C2
7680 m_pDoc
->SetValue(5, 1, 0, 0.0); // F2
7681 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 277.0, m_pDoc
->GetValue(aPos
));
7682 m_pDoc
->SetValue(0, 2, 0, -8.0); // A3
7683 m_pDoc
->SetValue(3, 2, 0, 8.0); // D3
7684 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc
->GetValue(aPos
));
7685 m_pDoc
->SetValue(1, 2, 0, 0.0); // B3
7686 m_pDoc
->SetValue(4, 2, 0, 0.0); // E3
7687 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 405.0, m_pDoc
->GetValue(aPos
));
7688 m_pDoc
->SetValue(2, 2, 0, 1.0); // C3
7689 m_pDoc
->SetValue(5, 2, 0, 1.0); // F3
7690 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 407.0, m_pDoc
->GetValue(aPos
));
7693 m_pDoc
->SetString(4, 1, 0, "a"); // E2
7694 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc
->GetValue(aPos
));
7695 m_pDoc
->SetString(1, 1, 0, "a"); // B2
7696 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 357.0, m_pDoc
->GetValue(aPos
));
7697 m_pDoc
->SetString(0, 0, 0, "a"); // A1
7698 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc
->GetValue(aPos
));
7699 m_pDoc
->SetString(3, 0, 0, "a"); // D1
7700 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 352.0, m_pDoc
->GetValue(aPos
));
7702 m_pDoc
->SetString(aPos
, "=SUMX2PY2({1;2;3};{2;3;4})");
7703 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2PY2 failed", 43.0, m_pDoc
->GetValue(aPos
));
7704 m_pDoc
->SetString(aPos
, "=SUMX2PY2({1;2;3};{2;3})");
7705 aVal
= m_pDoc
->GetString(aPos
);
7706 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 should return #VALUE! for matrices with different sizes",
7707 OUString("#VALUE!"), aVal
);
7708 m_pDoc
->SetString(aPos
, "=SUMX2PY2({1;2;3})");
7709 aVal
= m_pDoc
->GetString(aPos
);
7710 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2PY2 needs two parameters",
7711 OUString("Err:511"), aVal
);
7713 m_pDoc
->DeleteTab(0);
7716 void Test::testFuncSUMX2MY2()
7718 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7720 m_pDoc
->InsertTab(0, "SumX2MY2 Test");
7723 ScAddress
aPos(6,0,0);
7724 m_pDoc
->SetString(aPos
, "=SUMX2MY2(A1:C3;D1:F3)");
7725 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc
->GetValue(aPos
));
7727 m_pDoc
->SetValue(0, 0, 0, 10.0); // A1
7728 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 0.0, m_pDoc
->GetValue(aPos
));
7729 m_pDoc
->SetValue(3, 0, 0, -9.0); // D1
7730 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc
->GetValue(aPos
));
7731 m_pDoc
->SetValue(1, 0, 0, 2.0); // B1
7732 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 19.0, m_pDoc
->GetValue(aPos
));
7733 m_pDoc
->SetValue(4, 0, 0, 1.0); // E1
7734 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7735 m_pDoc
->SetValue(2, 0, 0, 3.0); // C1
7736 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7737 m_pDoc
->SetValue(5, 0, 0, 3.0); // F1
7738 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7739 m_pDoc
->SetValue(0, 1, 0, 10.0); // A2
7740 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7741 m_pDoc
->SetValue(3, 1, 0, -10.0); // D2
7742 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7743 m_pDoc
->SetValue(1, 1, 0, -5.0); // B2
7744 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7745 m_pDoc
->SetValue(4, 1, 0, -5.0); // E2
7746 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7747 m_pDoc
->SetValue(2, 1, 0, -3.0); // C2
7748 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7749 m_pDoc
->SetValue(5, 1, 0, 3.0); // F2
7750 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7751 m_pDoc
->SetValue(0, 2, 0, -8.0); // A3
7752 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 22.0, m_pDoc
->GetValue(aPos
));
7753 m_pDoc
->SetValue(3, 2, 0, 3.0); // D3
7754 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc
->GetValue(aPos
));
7755 m_pDoc
->SetValue(1, 2, 0, 2.0); // B3
7756 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 77.0, m_pDoc
->GetValue(aPos
));
7757 m_pDoc
->SetValue(4, 2, 0, -6.0); // E3
7758 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc
->GetValue(aPos
));
7759 m_pDoc
->SetValue(2, 2, 0, -4.0); // C3
7760 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc
->GetValue(aPos
));
7761 m_pDoc
->SetValue(5, 2, 0, 6.0); // F3
7762 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 25.0, m_pDoc
->GetValue(aPos
));
7765 m_pDoc
->SetString(5, 2, 0, "a"); // F3
7766 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 45.0, m_pDoc
->GetValue(aPos
));
7767 m_pDoc
->SetString(0, 2, 0, "a"); // A3
7768 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -10.0, m_pDoc
->GetValue(aPos
));
7769 m_pDoc
->SetString(1, 0, 0, "a"); // B1
7770 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -13.0, m_pDoc
->GetValue(aPos
));
7771 m_pDoc
->SetString(3, 0, 0, "a"); // D1
7772 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", -32.0, m_pDoc
->GetValue(aPos
));
7774 m_pDoc
->SetString(aPos
, "=SUMX2MY2({1;3;5};{0;4;4})");
7775 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc
->GetValue(aPos
));
7776 m_pDoc
->SetString(aPos
, "=SUMX2MY2({1;-3;-5};{0;-4;4})");
7777 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 3.0, m_pDoc
->GetValue(aPos
));
7778 m_pDoc
->SetString(aPos
, "=SUMX2MY2({9;5;1};{3;-3;3})");
7779 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMX2MY2 failed", 80.0, m_pDoc
->GetValue(aPos
));
7780 m_pDoc
->SetString(aPos
, "=SUMX2MY2({1;2;3};{2;3})");
7781 aVal
= m_pDoc
->GetString(aPos
);
7782 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 should return #VALUE! for matrices with different sizes",
7783 OUString("#VALUE!"), aVal
);
7784 m_pDoc
->SetString(aPos
, "=SUMX2MY2({1;2;3})");
7785 aVal
= m_pDoc
->GetString(aPos
);
7786 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMX2MY2 needs two parameters",
7787 OUString("Err:511"), aVal
);
7789 m_pDoc
->DeleteTab(0);
7792 void Test::testFuncGCD()
7794 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7796 m_pDoc
->InsertTab(0, "GCDTest");
7799 ScAddress
aPos(4,0,0);
7801 m_pDoc
->SetString(aPos
, "=GCD(A1)");
7802 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc
->GetValue(aPos
));
7803 m_pDoc
->SetValue(0, 0, 0, 10.0); // A1
7804 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc
->GetValue(aPos
));
7805 m_pDoc
->SetValue(0, 0, 0, -2.0); // A1
7806 aVal
= m_pDoc
->GetString(aPos
);
7807 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for values less than 0",
7808 OUString("Err:502"), aVal
);
7809 m_pDoc
->SetString(0, 0, 0, "a"); // A1
7810 aVal
= m_pDoc
->GetString(aPos
);
7811 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for a single string",
7812 OUString("#VALUE!"), aVal
);
7814 m_pDoc
->SetString(aPos
, "=GCD(A1:B2)");
7815 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 0.0, m_pDoc
->GetValue(aPos
));
7816 m_pDoc
->SetValue(0, 1, 0, -12.0); // B1
7817 aVal
= m_pDoc
->GetString(aPos
);
7818 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for a matrix with values less than 0",
7819 OUString("Err:502"), aVal
);
7820 m_pDoc
->SetValue(0, 0, 0, 15.0); // A1
7821 m_pDoc
->SetValue(0, 1, 0, 0.0); // B1
7822 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 15.0, m_pDoc
->GetValue(aPos
));
7823 m_pDoc
->SetValue(1, 0, 0, 5.0); // B1
7824 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7825 m_pDoc
->SetValue(0, 1, 0, 10.0); // A2
7826 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7827 m_pDoc
->SetValue(1, 0, 0, 30.0); // B1
7828 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7829 m_pDoc
->SetValue(0, 0, 0, 20.0); // A1
7830 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc
->GetValue(aPos
));
7831 m_pDoc
->SetValue(1, 1, 0, 120.0); // B2
7832 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 10.0, m_pDoc
->GetValue(aPos
));
7833 m_pDoc
->SetValue(0, 1, 0, 80.0); // A2
7834 m_pDoc
->SetValue(1, 0, 0, 40.0); // B1
7835 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 20.0, m_pDoc
->GetValue(aPos
));
7836 m_pDoc
->SetValue(1, 0, 0, 45.0); // B1
7837 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7840 m_pDoc
->SetValue(1, 0, 0, 45.381); // B1
7841 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7842 m_pDoc
->SetValue(1, 1, 0, 120.895); // B2
7843 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7844 m_pDoc
->SetValue(0, 0, 0, 20.97); // A1
7845 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7846 m_pDoc
->SetValue(0, 1, 0, 10.15); // A2
7847 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 5.0, m_pDoc
->GetValue(aPos
));
7850 m_pDoc
->SetString(aPos
, "=GCD({3;6;9})");
7851 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc
->GetValue(aPos
));
7852 m_pDoc
->SetString(aPos
, "=GCD({150;0})");
7853 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc
->GetValue(aPos
));
7854 m_pDoc
->SetString(aPos
, "=GCD({-3;6;9})");
7855 aVal
= m_pDoc
->GetString(aPos
);
7856 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
7857 OUString("Err:502"), aVal
);
7858 m_pDoc
->SetString(aPos
, "=GCD({\"a\";6;9})");
7859 aVal
= m_pDoc
->GetString(aPos
);
7860 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
7861 OUString("Err:502"), aVal
);
7864 m_pDoc
->SetString(aPos
, "=GCD({6;6;6};{3;6;9})");
7865 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 3.0, m_pDoc
->GetValue(aPos
));
7866 m_pDoc
->SetString(aPos
, "=GCD({300;300;300};{150;0})");
7867 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 150.0, m_pDoc
->GetValue(aPos
));
7868 m_pDoc
->SetString(aPos
,"=GCD({3;6;9};{3;-6;9})");
7869 aVal
= m_pDoc
->GetString(aPos
);
7870 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with values less than 0",
7871 OUString("Err:502"), aVal
);
7872 m_pDoc
->SetString(aPos
, "=GCD({3;6;9};{\"a\";6;9})");
7873 aVal
= m_pDoc
->GetString(aPos
);
7874 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return Err:502 for an array with strings",
7875 OUString("Err:502"), aVal
);
7877 // inline list of values
7878 m_pDoc
->SetString(aPos
, "=GCD(12;24;36;48;60)");
7879 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc
->GetValue(aPos
));
7880 m_pDoc
->SetString(aPos
, "=GCD(0;12;24;36;48;60)");
7881 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 12.0, m_pDoc
->GetValue(aPos
));
7882 m_pDoc
->SetString(aPos
, "=GCD(\"a\";1)");
7883 aVal
= m_pDoc
->GetString(aPos
);
7884 CPPUNIT_ASSERT_EQUAL_MESSAGE("GCD should return #VALUE! for an array with strings",
7885 OUString("#VALUE!"), aVal
);
7887 m_pDoc
->DeleteTab(0);
7890 void Test::testFuncLCM()
7892 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7894 m_pDoc
->InsertTab(0, "LCMTest");
7897 ScAddress
aPos(4,0,0);
7899 m_pDoc
->SetString(aPos
, "=LCM(A1)");
7900 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc
->GetValue(aPos
));
7901 m_pDoc
->SetValue(0, 0, 0, 10.0); // A1
7902 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 10.0, m_pDoc
->GetValue(aPos
));
7903 m_pDoc
->SetValue(0, 0, 0, -2.0); // A1
7904 aVal
= m_pDoc
->GetString(aPos
);
7905 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for values less than 0",
7906 OUString("Err:502"), aVal
);
7907 m_pDoc
->SetString(0, 0, 0, "a"); // A1
7908 aVal
= m_pDoc
->GetString(aPos
);
7909 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for a single string",
7910 OUString("#VALUE!"), aVal
);
7912 m_pDoc
->SetString(aPos
, "=LCM(A1:B2)");
7913 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of GCD for failed", 1.0, m_pDoc
->GetValue(aPos
));
7914 m_pDoc
->SetValue(0, 1, 0, -12.0); // B1
7915 aVal
= m_pDoc
->GetString(aPos
);
7916 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for a matrix with values less than 0",
7917 OUString("Err:502"), aVal
);
7918 m_pDoc
->SetValue(0, 0, 0, 15.0); // A1
7919 m_pDoc
->SetValue(0, 1, 0, 0.0); // A2
7920 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc
->GetValue(aPos
));
7921 m_pDoc
->SetValue(1, 0, 0, 5.0); // B1
7922 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc
->GetValue(aPos
));
7923 m_pDoc
->SetValue(0, 1, 0, 10.0); // A2
7924 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc
->GetValue(aPos
));
7925 m_pDoc
->SetValue(1, 0, 0, 30.0); // B1
7926 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 30.0, m_pDoc
->GetValue(aPos
));
7927 m_pDoc
->SetValue(0, 0, 0, 20.0); // A1
7928 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 60.0, m_pDoc
->GetValue(aPos
));
7929 m_pDoc
->SetValue(1, 1, 0, 125.0); // B2
7930 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1500.0, m_pDoc
->GetValue(aPos
));
7931 m_pDoc
->SetValue(1, 0, 0, 99.0); // B1
7932 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 49500.0, m_pDoc
->GetValue(aPos
));
7933 m_pDoc
->SetValue(0, 1, 0, 37.0); // A2
7934 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc
->GetValue(aPos
));
7937 m_pDoc
->SetValue(1, 0, 0, 99.89); // B1
7938 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 1831500.0, m_pDoc
->GetValue(aPos
));
7939 m_pDoc
->SetValue(1, 1, 0, 11.32); // B2
7940 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 73260.0, m_pDoc
->GetValue(aPos
));
7941 m_pDoc
->SetValue(0, 0, 0, 22.58); // A1
7942 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 7326.0, m_pDoc
->GetValue(aPos
));
7943 m_pDoc
->SetValue(0, 1, 0, 3.99); // A2
7944 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 198.0, m_pDoc
->GetValue(aPos
));
7947 m_pDoc
->SetString(aPos
, "=LCM({3;6;9})");
7948 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc
->GetValue(aPos
));
7949 m_pDoc
->SetString(aPos
, "=LCM({150;0})");
7950 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc
->GetValue(aPos
));
7951 m_pDoc
->SetString(aPos
, "=LCM({-3;6;9})");
7952 aVal
= m_pDoc
->GetString(aPos
);
7953 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
7954 OUString("Err:502"), aVal
);
7955 m_pDoc
->SetString(aPos
, "=LCM({\"a\";6;9})");
7956 aVal
= m_pDoc
->GetString(aPos
);
7957 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
7958 OUString("Err:502"), aVal
);
7961 m_pDoc
->SetString(aPos
, "=LCM({6;6;6};{3;6;9})");
7962 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 18.0, m_pDoc
->GetValue(aPos
));
7963 m_pDoc
->SetString(aPos
, "=LCM({300;300;300};{150;0})");
7964 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc
->GetValue(aPos
));
7965 m_pDoc
->SetString(aPos
,"=LCM({3;6;9};{3;-6;9})");
7966 aVal
= m_pDoc
->GetString(aPos
);
7967 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with values less than 0",
7968 OUString("Err:502"), aVal
);
7969 m_pDoc
->SetString(aPos
, "=LCM({3;6;9};{\"a\";6;9})");
7970 aVal
= m_pDoc
->GetString(aPos
);
7971 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return Err:502 for an array with strings",
7972 OUString("Err:502"), aVal
);
7974 m_pDoc
->SetString(aPos
, "=LCM(12;24;36;48;60)");
7975 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 720.0, m_pDoc
->GetValue(aPos
));
7976 m_pDoc
->SetString(aPos
, "=LCM(0;12;24;36;48;60)");
7977 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of LCM for failed", 0.0, m_pDoc
->GetValue(aPos
));
7978 m_pDoc
->SetString(aPos
, "=LCM(\"a\";1)");
7979 aVal
= m_pDoc
->GetString(aPos
);
7980 CPPUNIT_ASSERT_EQUAL_MESSAGE("LCM should return #VALUE! for an array with strings",
7981 OUString("#VALUE!"), aVal
);
7983 m_pDoc
->DeleteTab(0);
7986 void Test::testFuncSUMSQ()
7988 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
7990 m_pDoc
->InsertTab(0, "SUMSQTest");
7992 ScAddress
aPos(4,0,0);
7994 m_pDoc
->SetString(aPos
, "=SUMSQ(A1)");
7995 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 0.0, m_pDoc
->GetValue(aPos
));
7996 m_pDoc
->SetValue(0, 0, 0, 1.0); // A1
7997 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc
->GetValue(aPos
));
7998 m_pDoc
->SetValue(0, 0, 0, -1.0); // A1
7999 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc
->GetValue(aPos
));
8000 m_pDoc
->SetValue(0, 1, 0, -2.0); // A2
8001 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 1.0, m_pDoc
->GetValue(aPos
));
8003 m_pDoc
->SetString(aPos
, "=SUMSQ(A1:A3)");
8004 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc
->GetValue(aPos
));
8005 m_pDoc
->SetValue(1, 0, 0, 3.0); // B1
8006 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 5.0, m_pDoc
->GetValue(aPos
));
8007 m_pDoc
->SetString(aPos
, "=SUMSQ(A1:C3)");
8008 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc
->GetValue(aPos
));
8009 m_pDoc
->SetValue(1, 1, 0, -4.0); // B2
8010 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 30.0, m_pDoc
->GetValue(aPos
));
8011 m_pDoc
->SetString(1, 2, 0, "a"); // B3
8012 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc
->GetValue(aPos
));
8013 m_pDoc
->SetValue(1, 2, 0, 0.0); // B3
8014 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 30.0, m_pDoc
->GetValue(aPos
));
8015 m_pDoc
->SetValue(0, 2, 0, 6.0); // A3
8016 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 66.0, m_pDoc
->GetValue(aPos
));
8017 m_pDoc
->SetValue(2, 0, 0, -5.0); // C1
8018 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 91.0, m_pDoc
->GetValue(aPos
));
8019 m_pDoc
->SetValue(2, 1, 0, 3.0); // C2
8020 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 100.0, m_pDoc
->GetValue(aPos
));
8021 m_pDoc
->SetValue(2, 2, 0, 2.0); // C3
8022 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ with a string for failed", 104.0, m_pDoc
->GetValue(aPos
));
8025 m_pDoc
->SetString(aPos
, "=SUMSQ({1;2;3})");
8026 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 14.0, m_pDoc
->GetValue(aPos
));
8027 m_pDoc
->SetString(aPos
, "=SUMSQ({3;6;9})");
8028 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 126.0, m_pDoc
->GetValue(aPos
));
8029 m_pDoc
->SetString(aPos
, "=SUMSQ({15;0})");
8030 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 225.0, m_pDoc
->GetValue(aPos
));
8031 m_pDoc
->SetString(aPos
, "=SUMSQ({-3;3;1})");
8032 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 19.0, m_pDoc
->GetValue(aPos
));
8033 m_pDoc
->SetString(aPos
, "=SUMSQ({\"a\";-4;-5})");
8034 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 41.0, m_pDoc
->GetValue(aPos
));
8036 m_pDoc
->SetString(aPos
, "=SUMSQ({2;3};{4;5})");
8037 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 54.0, m_pDoc
->GetValue(aPos
));
8038 m_pDoc
->SetString(aPos
, "=SUMSQ({-3;3;1};{-1})");
8039 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 20.0, m_pDoc
->GetValue(aPos
));
8040 m_pDoc
->SetString(aPos
, "=SUMSQ({-4};{1;4;2};{-5;7};{9})");
8041 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 192.0, m_pDoc
->GetValue(aPos
));
8042 m_pDoc
->SetString(aPos
, "=SUMSQ({-2;2};{1};{-1};{0;0;0;4})");
8043 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc
->GetValue(aPos
));
8045 m_pDoc
->SetString(aPos
, "=SUMSQ(4;1;-3)");
8046 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 26.0, m_pDoc
->GetValue(aPos
));
8047 m_pDoc
->SetString(aPos
, "=SUMSQ(0;5;13;-7;-4)");
8048 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 259.0, m_pDoc
->GetValue(aPos
));
8049 m_pDoc
->SetString(aPos
, "=SUMSQ(0;12;24;36;48;60)");
8050 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc
->GetValue(aPos
));
8051 m_pDoc
->SetString(aPos
, "=SUMSQ(0;-12;-24;36;-48;60)");
8052 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUMSQ for failed", 7920.0, m_pDoc
->GetValue(aPos
));
8053 m_pDoc
->SetString(aPos
, "=SUMSQ(\"a\";1;\"d\";-4;2)");
8054 OUString aVal
= m_pDoc
->GetString(aPos
);
8055 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMSQ should return #VALUE! for an array with strings",
8056 OUString("#VALUE!"), aVal
);
8058 m_pDoc
->DeleteTab(0);
8061 void Test::testFuncMDETERM()
8063 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8065 m_pDoc
->InsertTab(0, "MDETERM_test");
8066 ScAddress
aPos(8,0,0);
8067 OUString
const aColCodes("ABCDEFGH");
8068 OUString
const aFormulaTemplate("=MDETERM(A1:B2)");
8069 OUStringBuffer
aFormulaBuffer(aFormulaTemplate
);
8070 for( SCSIZE nSize
= 3; nSize
<= 8; nSize
++ )
8073 // Generate a singular integer matrix
8074 for( SCROW nRow
= 0; nRow
< static_cast<SCROW
>(nSize
); nRow
++ )
8076 for( SCCOL nCol
= 0; nCol
< static_cast<SCCOL
>(nSize
); nCol
++ )
8078 m_pDoc
->SetValue(nCol
, nRow
, 0, fVal
);
8082 aFormulaBuffer
[12] = aColCodes
[nSize
-1];
8083 aFormulaBuffer
[13] = static_cast<sal_Unicode
>( '0' + nSize
);
8084 m_pDoc
->SetString(aPos
, aFormulaBuffer
.toString());
8086 #if SAL_TYPES_SIZEOFPOINTER == 4
8087 // On crappy 32-bit targets, presumably without extended precision on
8088 // interim results or optimization not catching it, this test fails
8089 // when comparing to 0.0, so have a narrow error margin. See also
8090 // commit message of 8140309d636d4a870875f2dd75ed3dfff2c0fbaf
8091 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8092 0.0, m_pDoc
->GetValue(aPos
), 1e-12);
8094 // Even on one (and only one) x86_64 target the result was
8095 // 6.34413156928661e-17 instead of 0.0 (tdf#99730) so lower the bar to
8097 // Then again on aarch64, ppc64* and s390x it also fails.
8098 // Sigh... why do we even test this? The original complaint in tdf#32834
8099 // was about -9.51712667007776E-016
8100 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for singular integer matrix",
8101 0.0, m_pDoc
->GetValue(aPos
), 1e-14);
8105 int const aVals
[] = {23, 31, 13, 12, 34, 64, 34, 31, 98, 32, 33, 63, 45, 54, 65, 76};
8107 for( SCROW nRow
= 0; nRow
< 4; nRow
++ )
8108 for( SCCOL nCol
= 0; nCol
< 4; nCol
++ )
8109 m_pDoc
->SetValue(nCol
, nRow
, 0, static_cast<double>(aVals
[nIdx
++]));
8110 m_pDoc
->SetString(aPos
, "=MDETERM(A1:D4)");
8111 // Following test is conservative in the sense that on Linux x86_64 the error is less that 1.0E-9
8112 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Calculation of MDETERM incorrect for non-singular integer matrix",
8113 -180655.0, m_pDoc
->GetValue(aPos
), 1.0E-6);
8114 m_pDoc
->DeleteTab(0);
8117 void Test::testFormulaErrorPropagation()
8119 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8121 m_pDoc
->InsertTab(0, "Sheet1");
8123 ScMarkData
aMark(MAXROW
, MAXCOL
);
8124 aMark
.SelectOneTable(0);
8125 ScAddress aPos
, aPos2
;
8126 const OUString
aTRUE("TRUE");
8127 const OUString
aFALSE("FALSE");
8129 aPos
.Set(0,0,0);// A1
8130 m_pDoc
->SetValue( aPos
, 1.0);
8131 aPos
.IncCol(); // B1
8132 m_pDoc
->SetValue( aPos
, 2.0);
8135 aPos
.IncRow(); // C2
8136 m_pDoc
->SetString( aPos
, "=ISERROR(A1:B1+3)");
8137 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos
));
8139 aPos
.IncRow(); // C3
8140 m_pDoc
->SetString( aPos
, "=ISERROR(A1:B1+{3})");
8141 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos
));
8142 aPos
.IncRow(); // C4
8144 aPos2
.IncCol(); // D4
8145 m_pDoc
->InsertMatrixFormula(aPos
.Col(), aPos
.Row(), aPos2
.Col(), aPos2
.Row(), aMark
, "=ISERROR(A1:B1+{3})");
8146 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos
));
8147 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos2
));
8149 aPos
.IncRow(); // C5
8150 m_pDoc
->SetString( aPos
, "=ISERROR({1;\"x\"}+{3;4})");
8151 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos
));
8152 aPos
.IncRow(); // C6
8154 aPos2
.IncCol(); // D6
8155 m_pDoc
->InsertMatrixFormula(aPos
.Col(), aPos
.Row(), aPos2
.Col(), aPos2
.Row(), aMark
, "=ISERROR({1;\"x\"}+{3;4})");
8156 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos
));
8157 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos2
));
8159 aPos
.IncRow(); // C7
8160 m_pDoc
->SetString( aPos
, "=ISERROR({\"x\";2}+{3;4})");
8161 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos
));
8162 aPos
.IncRow(); // C8
8164 aPos2
.IncCol(); // D8
8165 m_pDoc
->InsertMatrixFormula(aPos
.Col(), aPos
.Row(), aPos2
.Col(), aPos2
.Row(), aMark
, "=ISERROR({\"x\";2}+{3;4})");
8166 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos
));
8167 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos2
));
8169 aPos
.IncRow(); // C9
8170 m_pDoc
->SetString( aPos
, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8171 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos
));
8172 aPos
.IncRow(); // C10
8174 aPos2
.IncCol(); // D10
8175 m_pDoc
->InsertMatrixFormula(aPos
.Col(), aPos
.Row(), aPos2
.Col(), aPos2
.Row(), aMark
, "=ISERROR(({1;\"x\"}+{3;4})-{5;6})");
8176 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos
));
8177 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos2
));
8179 aPos
.IncRow(); // C11
8180 m_pDoc
->SetString( aPos
, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8181 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos
));
8182 aPos
.IncRow(); // C12
8184 aPos2
.IncCol(); // D12
8185 m_pDoc
->InsertMatrixFormula(aPos
.Col(), aPos
.Row(), aPos2
.Col(), aPos2
.Row(), aMark
, "=ISERROR(({\"x\";2}+{3;4})-{5;6})");
8186 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aTRUE
, m_pDoc
->GetString(aPos
));
8187 CPPUNIT_ASSERT_EQUAL_MESSAGE( aPos2
.Format(ScRefFlags::VALID
).toUtf8().getStr(), aFALSE
, m_pDoc
->GetString(aPos2
));
8189 m_pDoc
->DeleteTab(0);
8196 ScDocument
* m_pDoc
;
8198 const SCROW m_nTotalRows
;
8199 const SCROW m_nStart1
;
8200 const SCROW m_nEnd1
;
8201 const SCROW m_nStart2
;
8202 const SCROW m_nEnd2
;
8205 ColumnTest( ScDocument
* pDoc
, SCROW nTotalRows
,
8206 SCROW nStart1
, SCROW nEnd1
, SCROW nStart2
, SCROW nEnd2
)
8207 : m_pDoc(pDoc
), m_nTotalRows(nTotalRows
)
8208 , m_nStart1(nStart1
), m_nEnd1(nEnd1
)
8209 , m_nStart2(nStart2
), m_nEnd2(nEnd2
)
8212 void operator() ( SCCOL nColumn
, const OUString
& rFormula
,
8213 std::function
<double(SCROW
)> const & lExpected
) const
8215 ScDocument
aClipDoc(SCDOCMODE_CLIP
);
8216 ScMarkData
aMark(MAXROW
, MAXCOL
);
8218 ScAddress
aPos(nColumn
, m_nStart1
, 0);
8219 m_pDoc
->SetString(aPos
, rFormula
);
8220 ASSERT_DOUBLES_EQUAL( lExpected(m_nStart1
), m_pDoc
->GetValue(aPos
) );
8222 // Copy formula cell to clipboard.
8223 ScClipParam
aClipParam(aPos
, false);
8224 aMark
.SetMarkArea(aPos
);
8225 m_pDoc
->CopyToClip(aClipParam
, &aClipDoc
, &aMark
, false, false);
8227 // Paste it to first range.
8228 InsertDeleteFlags nFlags
= InsertDeleteFlags::CONTENTS
;
8229 ScRange
aDestRange(nColumn
, m_nStart1
, 0, nColumn
, m_nEnd1
, 0);
8230 aMark
.SetMarkArea(aDestRange
);
8231 m_pDoc
->CopyFromClip(aDestRange
, aMark
, nFlags
, nullptr, &aClipDoc
);
8233 // Paste it second range.
8234 aDestRange
= ScRange(nColumn
, m_nStart2
, 0, nColumn
, m_nEnd2
, 0);
8235 aMark
.SetMarkArea(aDestRange
);
8236 m_pDoc
->CopyFromClip(aDestRange
, aMark
, nFlags
, nullptr, &aClipDoc
);
8238 // Check the formula results for passed column.
8239 for( SCROW i
= 0; i
< m_nTotalRows
; ++i
)
8241 if( !((m_nStart1
<= i
&& i
<= m_nEnd1
) || (m_nStart2
<= i
&& i
<= m_nEnd2
)) )
8243 double fExpected
= lExpected(i
);
8244 ASSERT_DOUBLES_EQUAL(fExpected
, m_pDoc
->GetValue(ScAddress(nColumn
,i
,0)));
8251 void Test::testTdf97369()
8253 const SCROW TOTAL_ROWS
= 330;
8254 const SCROW ROW_RANGE
= 10;
8255 const SCROW START1
= 9;
8256 const SCROW END1
= 159;
8257 const SCROW START2
= 169;
8258 const SCROW END2
= 319;
8260 const double SHIFT1
= 200;
8261 const double SHIFT2
= 400;
8263 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8264 m_pDoc
->InsertTab (0, "tdf97369"));
8266 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
8268 // set up columns A, B, C
8269 for( SCROW i
= 0; i
< TOTAL_ROWS
; ++i
)
8271 m_pDoc
->SetValue(ScAddress(0, i
, 0), i
); // A
8272 m_pDoc
->SetValue(ScAddress(1, i
, 0), i
+ SHIFT1
); // B
8273 m_pDoc
->SetValue(ScAddress(2, i
, 0), i
+ SHIFT2
); // C
8276 const ColumnTest
columnTest( m_pDoc
, TOTAL_ROWS
, START1
, END1
, START2
, END2
);
8278 auto lExpectedinD
= [=] (SCROW n
) {
8279 return 3.0 * (n
-START1
) + SHIFT1
+ SHIFT2
;
8281 columnTest(3, "=SUM(A1:C1)", lExpectedinD
);
8283 auto lExpectedinE
= [=] (SCROW
) {
8284 return SHIFT1
+ SHIFT2
;
8286 columnTest(4, "=SUM(A$1:C$1)", lExpectedinE
);
8288 auto lExpectedinF
= [=] (SCROW n
) {
8289 return ((2*n
+ 1 - ROW_RANGE
) * ROW_RANGE
) / 2.0;
8291 columnTest(5, "=SUM(A1:A10)", lExpectedinF
);
8293 auto lExpectedinG
= [=] (SCROW n
) {
8294 return ((n
+ 1) * n
) / 2.0;
8296 columnTest(6, "=SUM(A$1:A10)", lExpectedinG
);
8298 auto lExpectedinH
= [=] (SCROW n
) {
8299 return 3.0 * (((2*n
+ 1 - ROW_RANGE
) * ROW_RANGE
) / 2) + ROW_RANGE
* (SHIFT1
+ SHIFT2
);
8301 columnTest(7, "=SUM(A1:C10)", lExpectedinH
);
8303 auto lExpectedinI
= [=] (SCROW
) {
8304 return 3.0 * (((2*START1
+ 1 - ROW_RANGE
) * ROW_RANGE
) / 2) + ROW_RANGE
* (SHIFT1
+ SHIFT2
);
8306 columnTest(8, "=SUM(A$1:C$10)", lExpectedinI
);
8308 m_pDoc
->DeleteTab(0);
8311 void Test::testTdf97587()
8313 const SCROW TOTAL_ROWS
= 150;
8314 const SCROW ROW_RANGE
= 10;
8316 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
8317 m_pDoc
->InsertTab (0, "tdf97587"));
8319 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn on auto calc.
8321 std::set
<SCROW
> emptyCells
= {0, 100};
8322 for( SCROW i
= 0; i
< ROW_RANGE
; ++i
)
8324 emptyCells
.insert(i
+ TOTAL_ROWS
/ 3);
8325 emptyCells
.insert(i
+ TOTAL_ROWS
);
8329 for( SCROW i
= 0; i
< TOTAL_ROWS
; ++i
)
8331 if( emptyCells
.find(i
) != emptyCells
.end() )
8333 m_pDoc
->SetValue(ScAddress(0, i
, 0), 1.0);
8336 ScDocument
aClipDoc(SCDOCMODE_CLIP
);
8337 ScMarkData
aMark(MAXROW
, MAXCOL
);
8339 ScAddress
aPos(1, 0, 0);
8340 m_pDoc
->SetString(aPos
, "=SUM(A1:A10)");
8342 // Copy formula cell to clipboard.
8343 ScClipParam
aClipParam(aPos
, false);
8344 aMark
.SetMarkArea(aPos
);
8345 m_pDoc
->CopyToClip(aClipParam
, &aClipDoc
, &aMark
, false, false);
8347 // Paste it to first range.
8348 ScRange
aDestRange(1, 1, 0, 1, TOTAL_ROWS
+ ROW_RANGE
, 0);
8349 aMark
.SetMarkArea(aDestRange
);
8350 m_pDoc
->CopyFromClip(aDestRange
, aMark
, InsertDeleteFlags::CONTENTS
, nullptr, &aClipDoc
);
8352 // Check the formula results in column B.
8353 for( SCROW i
= 0; i
< TOTAL_ROWS
+ 1; ++i
)
8355 int k
= std::count_if( emptyCells
.begin(), emptyCells
.end(),
8356 [=](SCROW n
) { return (i
<= n
&& n
< i
+ ROW_RANGE
); } );
8357 double fExpected
= ROW_RANGE
- k
;
8358 ASSERT_DOUBLES_EQUAL(fExpected
, m_pDoc
->GetValue(ScAddress(1,i
,0)));
8360 m_pDoc
->DeleteTab(0);
8363 void Test::testMatConcat()
8365 CPPUNIT_ASSERT(m_pDoc
->InsertTab (0, "Test"));
8367 for (SCCOL nCol
= 0; nCol
< 10; ++nCol
)
8369 for (SCROW nRow
= 0; nRow
< 10; ++nRow
)
8371 m_pDoc
->SetValue(ScAddress(nCol
, nRow
, 0), nCol
*nRow
);
8375 ScMarkData
aMark(MAXROW
, MAXCOL
);
8376 aMark
.SelectOneTable(0);
8377 m_pDoc
->InsertMatrixFormula(0, 12, 9, 21, aMark
, "=A1:J10&A1:J10");
8379 for (SCCOL nCol
= 0; nCol
< 10; ++nCol
)
8381 for (SCROW nRow
= 12; nRow
< 22; ++nRow
)
8383 OUString aStr
= m_pDoc
->GetString(ScAddress(nCol
, nRow
, 0));
8384 CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol
* (nRow
- 12)) + OUString::number(nCol
* (nRow
- 12))), aStr
);
8388 { // Data in A12:B16
8389 const char* aData
[][2] = {
8397 ScAddress
aPos(0,11,0);
8398 ScRange aRange
= insertRangeData(m_pDoc
, aPos
, aData
, SAL_N_ELEMENTS(aData
));
8399 CPPUNIT_ASSERT_EQUAL(aPos
, aRange
.aStart
);
8401 // Matrix formula in C17:C21
8402 m_pDoc
->InsertMatrixFormula(2, 16, 2, 20, aMark
, "=A12:A16&B12:B16");
8403 // Check proper concatenation including empty cells.
8405 ScAddress
aPos(2,16,0);
8406 aStr
= m_pDoc
->GetString(aPos
);
8407 CPPUNIT_ASSERT_EQUAL(OUString("qw"),aStr
);
8409 aStr
= m_pDoc
->GetString(aPos
);
8410 CPPUNIT_ASSERT_EQUAL(OUString("a"),aStr
);
8412 aStr
= m_pDoc
->GetString(aPos
);
8413 CPPUNIT_ASSERT_EQUAL(OUString("x"),aStr
);
8415 aStr
= m_pDoc
->GetString(aPos
);
8416 CPPUNIT_ASSERT_EQUAL(OUString(),aStr
);
8418 aStr
= m_pDoc
->GetString(aPos
);
8419 CPPUNIT_ASSERT_EQUAL(OUString("er"),aStr
);
8421 m_pDoc
->DeleteTab(0);
8424 void Test::testMatConcatReplication()
8426 // if one of the matrices is a one column or row matrix
8427 // the matrix is replicated across the larger matrix
8428 CPPUNIT_ASSERT(m_pDoc
->InsertTab (0, "Test"));
8430 for (SCCOL nCol
= 0; nCol
< 10; ++nCol
)
8432 for (SCROW nRow
= 0; nRow
< 10; ++nRow
)
8434 m_pDoc
->SetValue(ScAddress(nCol
, nRow
, 0), nCol
*nRow
);
8438 ScMarkData
aMark(MAXROW
, MAXCOL
);
8439 aMark
.SelectOneTable(0);
8440 m_pDoc
->InsertMatrixFormula(0, 12, 9, 21, aMark
, "=A1:J10&A1:J1");
8442 for (SCCOL nCol
= 0; nCol
< 10; ++nCol
)
8444 for (SCROW nRow
= 12; nRow
< 22; ++nRow
)
8446 OUString aStr
= m_pDoc
->GetString(ScAddress(nCol
, nRow
, 0));
8447 CPPUNIT_ASSERT_EQUAL(OUString(OUString::number(nCol
* (nRow
- 12)) + "0"), aStr
);
8451 m_pDoc
->DeleteTab(0);
8454 void Test::testRefR1C1WholeCol()
8456 CPPUNIT_ASSERT(m_pDoc
->InsertTab (0, "Test"));
8458 ScAddress
aPos(1, 1, 1);
8459 ScCompiler
aComp(m_pDoc
, aPos
, FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
8460 std::unique_ptr
<ScTokenArray
> pTokens(aComp
.CompileString("=C[10]"));
8461 sc::TokenStringContext
aCxt(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH
);
8462 OUString aFormula
= pTokens
->CreateString(m_pDoc
, aCxt
, aPos
);
8464 CPPUNIT_ASSERT_EQUAL(OUString("L:L"), aFormula
);
8466 m_pDoc
->DeleteTab(0);
8469 void Test::testRefR1C1WholeRow()
8471 CPPUNIT_ASSERT(m_pDoc
->InsertTab (0, "Test"));
8473 ScAddress
aPos(1, 1, 1);
8474 ScCompiler
aComp(m_pDoc
, aPos
, FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
8475 std::unique_ptr
<ScTokenArray
> pTokens(aComp
.CompileString("=R[3]"));
8476 sc::TokenStringContext
aCxt(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH
);
8477 OUString aFormula
= pTokens
->CreateString(m_pDoc
, aCxt
, aPos
);
8479 CPPUNIT_ASSERT_EQUAL(OUString("5:5"), aFormula
);
8481 m_pDoc
->DeleteTab(0);
8484 void Test::testSingleCellCopyColumnLabel()
8486 ScDocOptions aOptions
= m_pDoc
->GetDocOptions();
8487 aOptions
.SetLookUpColRowNames(true);
8488 m_pDoc
->SetDocOptions(aOptions
);
8489 m_pDoc
->InsertTab(0, "Test");
8491 m_pDoc
->SetString(0, 0, 0, "a");
8492 m_pDoc
->SetValue(0, 1, 0, 1.0);
8493 m_pDoc
->SetValue(0, 2, 0, 2.0);
8494 m_pDoc
->SetValue(0, 3, 0, 3.0);
8495 m_pDoc
->SetString(1, 1, 0, "='a'");
8497 double nVal
= m_pDoc
->GetValue(1, 1, 0);
8498 ASSERT_DOUBLES_EQUAL(1.0, nVal
);
8500 ScDocument
aClipDoc(SCDOCMODE_CLIP
);
8501 copyToClip(m_pDoc
, ScRange(1, 1, 0), &aClipDoc
);
8502 pasteOneCellFromClip(m_pDoc
, ScRange(1, 2, 0), &aClipDoc
);
8503 nVal
= m_pDoc
->GetValue(1, 2, 0);
8504 ASSERT_DOUBLES_EQUAL(2.0, nVal
);
8506 m_pDoc
->DeleteTab(0);
8509 // Significant whitespace operator intersection in Excel syntax, tdf#96426
8510 void Test::testIntersectionOpExcel()
8512 CPPUNIT_ASSERT(m_pDoc
->InsertTab (0, "Test"));
8514 ScRangeName
* pGlobalNames
= m_pDoc
->GetRangeName();
8515 // Horizontal cell range covering C2.
8516 pGlobalNames
->insert( new ScRangeData( m_pDoc
, "horz", "$B$2:$D$2"));
8517 // Vertical cell range covering C2.
8518 pGlobalNames
->insert( new ScRangeData( m_pDoc
, "vert", "$C$1:$C$3"));
8520 m_pDoc
->SetValue(2,1,0, 1.0);
8522 m_pDoc
->SetGrammar(FormulaGrammar::GRAM_ENGLISH_XL_A1
);
8524 // Choose formula positions that don't intersect with those data ranges.
8525 ScAddress
aPos(0,3,0);
8526 m_pDoc
->SetString(aPos
,"=B2:D2 C1:C3");
8527 CPPUNIT_ASSERT_EQUAL_MESSAGE("A4 intersecting references failed", 1.0, m_pDoc
->GetValue(aPos
));
8529 m_pDoc
->SetString(aPos
,"=horz vert");
8530 CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 intersecting named expressions failed", 1.0, m_pDoc
->GetValue(aPos
));
8532 m_pDoc
->SetString(aPos
,"=(horz vert)*2");
8533 CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 calculating with intersecting named expressions failed", 2.0, m_pDoc
->GetValue(aPos
));
8535 m_pDoc
->SetString(aPos
,"=2*(horz vert)");
8536 CPPUNIT_ASSERT_EQUAL_MESSAGE("A7 calculating with intersecting named expressions failed", 2.0, m_pDoc
->GetValue(aPos
));
8538 m_pDoc
->SetGrammar(FormulaGrammar::GRAM_ENGLISH
);
8540 m_pDoc
->DeleteTab(0);
8543 //Test Subtotal and Aggregate during hide rows #tdf93171
8544 void Test::testFuncRowsHidden()
8546 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8547 m_pDoc
->InsertTab(0, "Test");
8548 m_pDoc
->SetValue(0, 0, 0, 1); //A1
8549 m_pDoc
->SetValue(0, 1, 0, 2); //A2
8550 m_pDoc
->SetValue(0, 2, 0, 4); //A3
8551 m_pDoc
->SetValue(0, 3, 0, 8); //A4
8552 m_pDoc
->SetValue(0, 4, 0, 16); //A5
8553 m_pDoc
->SetValue(0, 5, 0, 32); //A6
8555 ScAddress
aPos(0,6,0);
8556 m_pDoc
->SetString(aPos
, "=SUBTOTAL(109; A1:A6)");
8557 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc
->GetValue(aPos
));
8559 m_pDoc
->SetRowHidden(0, 0, 0, true);
8560 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 62.0, m_pDoc
->GetValue(aPos
));
8561 m_pDoc
->SetRowHidden(0, 0, 0, false);
8563 m_pDoc
->SetRowHidden(1, 2, 0, true);
8564 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 57.0, m_pDoc
->GetValue(aPos
));
8565 m_pDoc
->SetRowHidden(1, 2, 0, false);
8566 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUBTOTAL failed", 63.0, m_pDoc
->GetValue(aPos
));
8568 m_pDoc
->SetString(aPos
, "=AGGREGATE(9; 5; A1:A6)"); //9=SUM 5=Ignore only hidden rows
8569 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc
->GetValue(aPos
));
8571 m_pDoc
->SetRowHidden(0, 0, 0, true);
8572 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 62.0, m_pDoc
->GetValue(aPos
));
8573 m_pDoc
->SetRowHidden(0, 0, 0, false);
8575 m_pDoc
->SetRowHidden(2, 4, 0, true);
8576 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 35.0, m_pDoc
->GetValue(aPos
));
8577 m_pDoc
->SetRowHidden(2, 4, 0, false);
8578 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of AGGREGATE failed", 63.0, m_pDoc
->GetValue(aPos
));
8580 m_pDoc
->SetString(aPos
, "=SUM(A1:A6)");
8581 m_pDoc
->SetRowHidden(2, 4, 0, true);
8582 CPPUNIT_ASSERT_EQUAL_MESSAGE("Calculation of SUM failed", 63.0, m_pDoc
->GetValue(aPos
));
8584 m_pDoc
->DeleteTab(0);
8587 // Test COUNTIFS, SUMIFS, AVERAGEIFS in array context.
8588 void Test::testFuncSUMIFS()
8590 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8591 m_pDoc
->InsertTab(0, "Test");
8593 // Data in A1:B7, query in A9:A11
8594 std::vector
<std::vector
<const char*>> aData
= {
8602 { "" }, // {} doesn't work with some compilers
8608 insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
);
8610 ScMarkData
aMark(MAXROW
, MAXCOL
);
8611 aMark
.SelectOneTable(0);
8612 // Matrix formula in C8:C10 with SUMIFS
8613 m_pDoc
->InsertMatrixFormula(2, 7, 2, 9, aMark
, "=SUMIFS(B1:B7;A1:A7;A9:A11)");
8614 // Matrix formula in D8:D10 with COUNTIFS
8615 m_pDoc
->InsertMatrixFormula(3, 7, 3, 9, aMark
, "=COUNTIFS(A1:A7;A9:A11)");
8616 // Matrix formula in E8:E10 with AVERAGEIFS
8617 m_pDoc
->InsertMatrixFormula(4, 7, 4, 9, aMark
, "=AVERAGEIFS(B1:B7;A1:A7;A9:A11)");
8620 // Result B1+B5, B2+B6, B3+B7 and counts and averages.
8621 std::vector
<std::vector
<const char*>> aCheck
= {
8622 { "17", "2", "8.5" },
8623 { "34", "2", "17" },
8626 bool bGood
= checkOutput(m_pDoc
, ScRange(2,7,0, 4,9,0), aCheck
,
8627 "SUMIFS, COUNTIFS and AVERAGEIFS in array context");
8628 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS in array context failed", bGood
);
8631 // Matrix formula in G8:G10 with SUMIFS and reference list arrays.
8632 m_pDoc
->InsertMatrixFormula(6, 7, 6, 9, aMark
, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
8633 // Matrix formula in H8:H10 with COUNTIFS and reference list arrays.
8634 m_pDoc
->InsertMatrixFormula(7, 7, 7, 9, aMark
, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\")");
8635 // Matrix formula in I8:I10 with AVERAGEIFS and reference list arrays.
8636 m_pDoc
->InsertMatrixFormula(8, 7, 8, 9, aMark
, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\")");
8639 // Result sums, counts and averages.
8640 std::vector
<std::vector
<const char*>> aCheck
= {
8641 { "0", "0", "#DIV/0!" },
8645 bool bGood
= checkOutput(m_pDoc
, ScRange(6,7,0, 8,9,0), aCheck
,
8646 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays");
8647 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays failed", bGood
);
8650 // Matrix formula in K8:K10 with SUMIFS and reference list array condition
8651 // and "normal" data range.
8652 m_pDoc
->InsertMatrixFormula(10, 7, 10, 9, aMark
, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
8653 // Matrix formula in L8:L10 with AVERAGEIFS and reference list array
8654 // condition and "normal" data range.
8655 m_pDoc
->InsertMatrixFormula(11, 7, 11, 9, aMark
, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\")");
8658 // Result sums and averages.
8659 std::vector
<std::vector
<const char*>> aCheck
= {
8664 bool bGood
= checkOutput(m_pDoc
, ScRange(10,7,0, 11,9,0), aCheck
,
8665 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal range");
8666 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal range failed", bGood
);
8669 // Matrix formula in G18:G20 with SUMIFS and reference list arrays and a
8670 // "normal" criteria range.
8671 m_pDoc
->InsertMatrixFormula(6, 17, 6, 19, aMark
, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8672 // Matrix formula in H18:H20 with COUNTIFS and reference list arrays and a
8673 // "normal" criteria range.
8674 m_pDoc
->InsertMatrixFormula(7, 17, 7, 19, aMark
, "=COUNTIFS(OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8675 // Matrix formula in I18:I20 with AVERAGEIFS and reference list arrays and
8676 // a "normal" criteria range.
8677 m_pDoc
->InsertMatrixFormula(8, 17, 8, 19, aMark
, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8680 // Result sums, counts and averages.
8681 std::vector
<std::vector
<const char*>> aCheck
= {
8682 { "0", "0", "#DIV/0!" },
8686 bool bGood
= checkOutput(m_pDoc
, ScRange(6,17,0, 8,19,0), aCheck
,
8687 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range");
8688 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed", bGood
);
8691 // Matrix formula in K18:K20 with SUMIFS and reference list array condition
8692 // and "normal" data range and a "normal" criteria range.
8693 m_pDoc
->InsertMatrixFormula(10, 17, 10, 19, aMark
, "=SUMIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8694 // Matrix formula in L18:L20 with AVERAGEIFS and reference list array
8695 // condition and "normal" data range and a "normal" criteria range.
8696 m_pDoc
->InsertMatrixFormula(11, 17, 11, 19, aMark
, "=AVERAGEIFS(B1:B2;OFFSET(B1;ROW(1:3);0;2);\">4\";B1:B2;\">1\")");
8699 // Result sums and averages.
8700 std::vector
<std::vector
<const char*>> aCheck
= {
8705 bool bGood
= checkOutput(m_pDoc
, ScRange(10,17,0, 11,19,0), aCheck
,
8706 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range");
8707 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed", bGood
);
8710 // Same, but swapped normal and array criteria.
8712 // Matrix formula in G28:G30 with SUMIFS and reference list arrays and a
8713 // "normal" criteria range, swapped.
8714 m_pDoc
->InsertMatrixFormula(6, 27, 6, 29, aMark
, "=SUMIFS(OFFSET(B1;ROW(1:3);0;2);B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8715 // Matrix formula in H28:H30 with COUNTIFS and reference list arrays and a
8716 // "normal" criteria range, swapped.
8717 m_pDoc
->InsertMatrixFormula(7, 27, 7, 29, aMark
, "=COUNTIFS(B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8718 // Matrix formula in I28:I30 with AVERAGEIFS and reference list arrays and
8719 // a "normal" criteria range, swapped.
8720 m_pDoc
->InsertMatrixFormula(8, 27, 8, 29, aMark
, "=AVERAGEIFS(OFFSET(B1;ROW(1:3);0;2);B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8723 // Result sums, counts and averages.
8724 std::vector
<std::vector
<const char*>> aCheck
= {
8725 { "0", "0", "#DIV/0!" },
8729 bool bGood
= checkOutput(m_pDoc
, ScRange(6,27,0, 8,29,0), aCheck
,
8730 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list arrays and a normal criteria range, swapped");
8731 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list arrays and a normal criteria range failed, swapped", bGood
);
8734 // Matrix formula in K28:K30 with SUMIFS and reference list array condition
8735 // and "normal" data range and a "normal" criteria range, swapped.
8736 m_pDoc
->InsertMatrixFormula(10, 27, 10, 29, aMark
, "=SUMIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8737 // Matrix formula in L28:L30 with AVERAGEIFS and reference list array
8738 // condition and "normal" data range and a "normal" criteria range,
8740 m_pDoc
->InsertMatrixFormula(11, 27, 11, 29, aMark
, "=AVERAGEIFS(B1:B2;B1:B2;\">1\";OFFSET(B1;ROW(1:3);0;2);\">4\")");
8743 // Result sums and averages.
8744 std::vector
<std::vector
<const char*>> aCheck
= {
8749 bool bGood
= checkOutput(m_pDoc
, ScRange(10,27,0, 11,29,0), aCheck
,
8750 "SUMIFS, COUNTIFS and AVERAGEIFS with reference list array and normal data and criteria range, swapped");
8751 CPPUNIT_ASSERT_MESSAGE("SUMIFS, COUNTIFS or AVERAGEIFS with reference list array and normal data and criteria range failed, swapped", bGood
);
8754 m_pDoc
->DeleteTab(0);
8757 // Test SUBTOTAL with reference lists in array context.
8758 void Test::testFuncRefListArraySUBTOTAL()
8760 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8761 m_pDoc
->InsertTab(0, "Test");
8763 m_pDoc
->SetValue(0,0,0, 1.0); // A1
8764 m_pDoc
->SetValue(0,1,0, 2.0); // A2
8765 m_pDoc
->SetValue(0,2,0, 4.0); // A3
8766 m_pDoc
->SetValue(0,3,0, 8.0); // A4
8767 m_pDoc
->SetValue(0,4,0, 16.0); // A5
8768 m_pDoc
->SetValue(0,5,0, 32.0); // A6
8770 // Matrix in B7:B9, individual SUM of A2:A3, A3:A4 and A4:A5
8771 ScMarkData
aMark(MAXROW
, MAXCOL
);
8772 aMark
.SelectOneTable(0);
8773 m_pDoc
->InsertMatrixFormula(1, 6, 1, 8, aMark
, "=SUBTOTAL(9;OFFSET(A1;ROW(1:3);0;2))");
8774 ScAddress
aPos(1,6,0);
8775 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A2:A3 failed", 6.0, m_pDoc
->GetValue(aPos
));
8777 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A3:A4 failed", 12.0, m_pDoc
->GetValue(aPos
));
8779 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL SUM for A4:A5 failed", 24.0, m_pDoc
->GetValue(aPos
));
8781 // Matrix in C7:C9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
8782 m_pDoc
->InsertMatrixFormula(2, 6, 2, 8, aMark
, "=SUBTOTAL(1;OFFSET(A1;ROW(1:3);0;2))");
8784 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A2:A3 failed", 3.0, m_pDoc
->GetValue(aPos
));
8786 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A3:A4 failed", 6.0, m_pDoc
->GetValue(aPos
));
8788 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A4:A5 failed", 12.0, m_pDoc
->GetValue(aPos
));
8790 // Matrix in D7:D9, individual MIN of A2:A3, A3:A4 and A4:A5
8791 m_pDoc
->InsertMatrixFormula(3, 6, 3, 8, aMark
, "=SUBTOTAL(5;OFFSET(A1;ROW(1:3);0;2))");
8793 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A2:A3 failed", 2.0, m_pDoc
->GetValue(aPos
));
8795 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A3:A4 failed", 4.0, m_pDoc
->GetValue(aPos
));
8797 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MIN for A4:A5 failed", 8.0, m_pDoc
->GetValue(aPos
));
8799 // Matrix in E7:E9, individual MAX of A2:A3, A3:A4 and A4:A5
8800 m_pDoc
->InsertMatrixFormula(4, 6, 4, 8, aMark
, "=SUBTOTAL(4;OFFSET(A1;ROW(1:3);0;2))");
8802 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A2:A3 failed", 4.0, m_pDoc
->GetValue(aPos
));
8804 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A3:A4 failed", 8.0, m_pDoc
->GetValue(aPos
));
8806 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A4:A5 failed", 16.0, m_pDoc
->GetValue(aPos
));
8808 // Matrix in F7:F9, individual STDEV of A2:A3, A3:A4 and A4:A5
8809 m_pDoc
->InsertMatrixFormula(5, 6, 5, 8, aMark
, "=SUBTOTAL(7;OFFSET(A1;ROW(1:3);0;2))");
8811 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A2:A3 failed", 1.414214, m_pDoc
->GetValue(aPos
), 1e-6);
8813 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A3:A4 failed", 2.828427, m_pDoc
->GetValue(aPos
), 1e-6);
8815 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A4:A5 failed", 5.656854, m_pDoc
->GetValue(aPos
), 1e-6);
8817 // Matrix in G7:G9, individual AVERAGE of A2:A3, A3:A4 and A4:A5
8818 // Plus two "ordinary" ranges, one before and one after.
8819 m_pDoc
->InsertMatrixFormula(6, 6, 6, 8, aMark
, "=SUBTOTAL(1;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
8821 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A2:A3,A5:A6 failed", 9.5, m_pDoc
->GetValue(aPos
));
8823 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A3:A4,A5:A6 failed", 10.5, m_pDoc
->GetValue(aPos
));
8825 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL AVERAGE for A1:A2,A4:A5,A5:A6 failed", 12.5, m_pDoc
->GetValue(aPos
));
8827 // Matrix in H7:H9, individual MAX of A2:A3, A3:A4 and A4:A5
8828 // Plus two "ordinary" ranges, one before and one after.
8829 m_pDoc
->InsertMatrixFormula(7, 6, 7, 8, aMark
, "=SUBTOTAL(4;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
8831 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A2:A3,A5:A6 failed", 32.0, m_pDoc
->GetValue(aPos
));
8833 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A3:A4,A5:A6 failed", 32.0, m_pDoc
->GetValue(aPos
));
8835 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUBTOTAL MAX for A1:A2,A4:A5,A5:A6 failed", 32.0, m_pDoc
->GetValue(aPos
));
8837 // Matrix in I7:I9, individual STDEV of A2:A3, A3:A4 and A4:A5
8838 // Plus two "ordinary" ranges, one before and one after.
8839 m_pDoc
->InsertMatrixFormula(8, 6, 8, 8, aMark
, "=SUBTOTAL(7;A1:A2;OFFSET(A1;ROW(1:3);0;2);A5:A6)");
8841 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A2:A3,A5:A6 failed", 12.35718, m_pDoc
->GetValue(aPos
), 1e-5);
8843 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A3:A4,A5:A6 failed", 11.86170, m_pDoc
->GetValue(aPos
), 1e-5);
8845 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SUBTOTAL STDEV for A1:A2,A4:A5,A5:A6 failed", 11.55422, m_pDoc
->GetValue(aPos
), 1e-5);
8847 // Empty two cells such that they affect two ranges.
8848 m_pDoc
->SetString(0,1,0, ""); // A2
8849 m_pDoc
->SetString(0,2,0, ""); // A3
8850 // Matrix in J7:J9, individual COUNTBLANK of A2:A3, A3:A4 and A4:A5
8851 m_pDoc
->InsertMatrixFormula(9, 6, 9, 8, aMark
, "=COUNTBLANK(OFFSET(A1;ROW(1:3);0;2))");
8853 CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A2:A3,A5:A6 failed", 2.0, m_pDoc
->GetValue(aPos
));
8855 CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A3:A4,A5:A6 failed", 1.0, m_pDoc
->GetValue(aPos
));
8857 CPPUNIT_ASSERT_EQUAL_MESSAGE("COUNTBLANK for A1:A2,A4:A5,A5:A6 failed", 0.0, m_pDoc
->GetValue(aPos
));
8859 // Restore these two cell values so we'd catch failures below.
8860 m_pDoc
->SetValue(0,1,0, 2.0); // A2
8861 m_pDoc
->SetValue(0,2,0, 4.0); // A3
8862 // Hide rows 2 to 4.
8863 m_pDoc
->SetRowHidden(1,3,0, true);
8864 // Matrix in K7, array of references as OFFSET result.
8865 m_pDoc
->InsertMatrixFormula(10, 6, 10, 6, aMark
, "=SUM(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
8867 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUM SUBTOTAL failed", 49.0, m_pDoc
->GetValue(aPos
));
8869 // ForceArray in K8, array of references as OFFSET result.
8870 m_pDoc
->SetString( aPos
, "=SUMPRODUCT(SUBTOTAL(109;OFFSET(A1;ROW(A1:A7)-ROW(A1);;1)))");
8871 CPPUNIT_ASSERT_EQUAL_MESSAGE("SUMPRODUCT SUBTOTAL failed", 49.0, m_pDoc
->GetValue(aPos
));
8873 m_pDoc
->DeleteTab(0);
8876 // tdf#115493 jump commands return the matrix result instead of the reference
8878 void Test::testFuncJumpMatrixArrayIF()
8880 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8881 m_pDoc
->InsertTab(0, "Test");
8883 m_pDoc
->SetString(0,0,0, "a"); // A1
8884 std::vector
<std::vector
<const char*>> aData
= {
8889 insertRangeData(m_pDoc
, ScAddress(0,6,0), aData
);
8891 ScMarkData
aMark(MAXROW
, MAXCOL
);
8892 aMark
.SelectOneTable(0);
8894 // Matrix in C10, summing B7,B9
8895 m_pDoc
->InsertMatrixFormula( 2,9, 2,9, aMark
, "=SUM(IF(EXACT(A7:A9;A$1);B7:B9;0))");
8896 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C10 failed", 5.0, m_pDoc
->GetValue(ScAddress(2,9,0)));
8898 // Matrix in C11, summing B7,B9
8899 m_pDoc
->InsertMatrixFormula( 2,10, 2,10, aMark
,
8900 "=SUM(IF(EXACT(OFFSET(A7;0;0):OFFSET(A7;2;0);A$1);OFFSET(A7;0;1):OFFSET(A7;2;1);0))");
8901 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C11 failed", 5.0, m_pDoc
->GetValue(ScAddress(2,10,0)));
8903 m_pDoc
->DeleteTab(0);
8906 // tdf#123477 OFFSET() returns the matrix result instead of the reference list
8907 // array if result is not used as ReferenceOrRefArray.
8908 void Test::testFuncJumpMatrixArrayOFFSET()
8910 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8911 m_pDoc
->InsertTab(0, "Test");
8913 std::vector
<std::vector
<const char*>> aData
= {
8918 insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
); // A1:A3
8920 ScMarkData
aMark(MAXROW
, MAXCOL
);
8921 aMark
.SelectOneTable(0);
8923 // Matrix in C5:C7, COLUMN()-3 here offsets by 0 but the entire expression
8924 // is in array/matrix context.
8925 m_pDoc
->InsertMatrixFormula( 2,4, 2,6, aMark
, "=FIND(\"c\";OFFSET(A1:A3;0;COLUMN()-3))");
8926 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C5 failed", 3.0, m_pDoc
->GetValue(ScAddress(2,4,0)));
8927 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C6 failed", 2.0, m_pDoc
->GetValue(ScAddress(2,5,0)));
8928 CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula C7 failed", 1.0, m_pDoc
->GetValue(ScAddress(2,6,0)));
8930 m_pDoc
->DeleteTab(0);
8933 // Test iterations with circular chain of references.
8934 void Test::testIterations()
8936 ScDocOptions aDocOpts
= m_pDoc
->GetDocOptions();
8937 aDocOpts
.SetIter( true );
8938 m_pDoc
->SetDocOptions( aDocOpts
);
8940 m_pDoc
->InsertTab(0, "Test");
8942 m_pDoc
->SetValue( 0, 0, 0, 0.01 ); // A1
8943 m_pDoc
->SetString( 0, 1, 0, "=A1" ); // A2
8944 m_pDoc
->SetString( 0, 2, 0, "=COS(A2)" ); // A3
8947 // Establish reference cycle for the computation of the fixed point of COS() function
8948 m_pDoc
->SetString( 0, 0, 0, "=A3" ); // A1
8951 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error", FormulaError::NONE
, m_pDoc
->GetErrCode( ScAddress( 0, 2, 0) ) );
8952 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate fixed point of cos() failed", 0.7387, m_pDoc
->GetValue(0, 2, 0), 1e-4 );
8954 // Modify the formula
8955 m_pDoc
->SetString( 0, 2, 0, "=COS(A2)+0.001" ); // A3
8958 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Cell A3 should not have any formula error after perturbation", FormulaError::NONE
, m_pDoc
->GetErrCode( ScAddress( 0, 2, 0) ) );
8959 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( "Iterations to calculate perturbed fixed point of cos() failed", 0.7399, m_pDoc
->GetValue(0, 2, 0), 1e-4 );
8961 m_pDoc
->DeleteTab(0);
8963 aDocOpts
.SetIter( false );
8964 m_pDoc
->SetDocOptions( aDocOpts
);
8967 // tdf#111428 CellStoreEvent and its counter used for quick "has a column
8968 // formula cells" must point to the correct column.
8969 void Test::testInsertColCellStoreEventSwap()
8971 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8972 m_pDoc
->InsertTab(0, "Test");
8974 m_pDoc
->SetValue( 0,0,0, 1.0 ); // A1
8975 m_pDoc
->SetString( 1,0,0, "=A1" ); // B1
8976 // Insert column left of B
8977 m_pDoc
->InsertCol( ScRange(1,0,0, 1,m_pDoc
->MaxRow(),0));
8978 ScAddress
aPos(2,0,0); // C1, new formula position
8979 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Should be formula cell having value", 1.0, m_pDoc
->GetValue(aPos
));
8980 // After having swapped in an empty column, editing or adding a formula
8981 // cell has to use the correct store context. To test this,
8982 // ScDocument::SetString() can't be used as it doesn't expose the behavior
8983 // in question, use ScDocFunc::SetFormulaCell() instead which actually is
8984 // also called when editing a cell and creating a formula cell.
8985 ScFormulaCell
* pCell
= new ScFormulaCell( m_pDoc
, aPos
, "=A1+1");
8986 ScDocFunc
& rDocFunc
= getDocShell().GetDocFunc();
8987 rDocFunc
.SetFormulaCell( aPos
, pCell
, false); // C1, change formula
8988 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Initial calculation failed", 2.0, m_pDoc
->GetValue(aPos
));
8989 m_pDoc
->SetValue( 0,0,0, 2.0 ); // A1, change value
8990 CPPUNIT_ASSERT_EQUAL_MESSAGE( "Recalculation failed", 3.0, m_pDoc
->GetValue(aPos
));
8992 m_pDoc
->DeleteTab(0);
8995 void Test::testFormulaAfterDeleteRows()
8997 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
8998 m_pDoc
->InsertTab(0, "Test");
9000 // Fill A1:A70000 with 1.0
9001 std::vector
<double> aVals(70000, 1.0);
9002 m_pDoc
->SetValues(ScAddress(0, 0, 0), aVals
);
9003 // Set A70001 with formula "=SUM(A1:A70000)"
9004 m_pDoc
->SetString(0, 70000, 0, "=SUM(A1:A70000)");
9006 // Delete rows 2:69998
9007 m_pDoc
->DeleteRow(ScRange(0, 1, 0, m_pDoc
->MaxCol(), 69997, 0));
9009 const ScAddress
aPos(0, 3, 0); // A4
9010 ASSERT_FORMULA_EQUAL(*m_pDoc
, aPos
, "SUM(A1:A3)", "Wrong formula in A4.");
9012 ASSERT_DOUBLES_EQUAL_MESSAGE("Wrong value at A4", 3.0, m_pDoc
->GetValue(aPos
));
9015 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */