/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// Copy formula cell to clipboard.
ScClipParam aClipParam(ScRange(aPos), false);
aMark.SetMarkArea(ScRange(aPos));
m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false);
// Paste it to first range.
InsertDeleteFlags nFlags = InsertDeleteFlags::CONTENTS;
ScRange aDestRange(nColumn, m_nStart1, 0, nColumn, m_nEnd1, 0);
aMark.SetMarkArea(aDestRange);
m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
// Paste it second range.
aDestRange = ScRange(nColumn, m_nStart2, 0, nColumn, m_nEnd2, 0);
aMark.SetMarkArea(aDestRange);
m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, nullptr, &aClipDoc);
// Check the formula results for passed column. for (SCROW i = 0; i < m_nTotalRows; ++i)
{ if (!((m_nStart1 <= i && i <= m_nEnd1) || (m_nStart2 <= i && i <= m_nEnd2))) continue; double fExpected = lExpected(i);
ASSERT_DOUBLES_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(nColumn, i, 0)));
}
}
};
} //namespace
class TestFormula2 : public ScUcalcTestBase
{ protected: template <size_t DataSize, size_t FormulaSize, int Type> void runTestMATCH(ScDocument* pDoc, constchar* aData[DataSize], const StrStrCheck aChecks[FormulaSize]); template <size_t DataSize, size_t FormulaSize, int Type> void runTestHorizontalMATCH(ScDocument* pDoc, constchar* aData[DataSize], const StrStrCheck aChecks[FormulaSize]);
// Test nested IF in array/matrix if the nested IF condition is a scalar.
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_pDoc->InsertMatrixFormula(0, 2, 1, 2, aMark, u"=IF({1;0};IF(1;23);42)"_ustr); // Results must be 23 and 42.
CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0, 2, 0)));
CPPUNIT_ASSERT_EQUAL(42.0, m_pDoc->GetValue(ScAddress(1, 2, 0)));
// Test nested IF in array/matrix if nested IF conditions are range // references, data in A5:C8, matrix formula in D4 so there is no // implicit intersection between formula and ranges.
{
std::vector<std::vector<constchar*>> aData
= { { "1", "1", "16" }, { "0", "1", "32" }, { "1", "0", "64" }, { "0", "0", "128" } };
ScAddress aPos(0, 4, 0);
ScRange aRange = insertRangeData(m_pDoc, aPos, aData);
CPPUNIT_ASSERT_EQUAL(aPos, aRange.aStart);
}
m_pDoc->InsertMatrixFormula(3, 3, 3, 3, aMark, u"=SUM(IF(A5:A8;IF(B5:B8;C5:C8;0);0))"_ustr); // Result must be 16, only the first row matches all criteria.
CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(3, 3, 0)));
// A11:B11 // Test nested IF in array/matrix if the nested IF has no Else path.
m_pDoc->InsertMatrixFormula(0, 10, 1, 10, aMark, u"=IF(IF({1;0};12);34;56)"_ustr); // Results must be 34 and 56.
CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(0, 10, 0)));
CPPUNIT_ASSERT_EQUAL(56.0, m_pDoc->GetValue(ScAddress(1, 10, 0)));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testFuncCHOOSE)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
m_pDoc->InsertTab(0, u"Formula"_ustr);
m_pDoc->SetString(ScAddress(0, 0, 0), u"=CHOOSE(B1;\"one\";\"two\";\"three\")"_ustr);
FormulaError nError = m_pDoc->GetErrCode(ScAddress(0, 0, 0));
CPPUNIT_ASSERT_MESSAGE("Formula result should be an error since B1 is still empty.",
nError != FormulaError::NONE);
m_pDoc->SetValue(ScAddress(1, 0, 0), 1.0);
CPPUNIT_ASSERT_EQUAL(u"one"_ustr, m_pDoc->GetString(ScAddress(0, 0, 0)));
m_pDoc->SetValue(ScAddress(1, 0, 0), 2.0);
CPPUNIT_ASSERT_EQUAL(u"two"_ustr, m_pDoc->GetString(ScAddress(0, 0, 0)));
m_pDoc->SetValue(ScAddress(1, 0, 0), 3.0);
CPPUNIT_ASSERT_EQUAL(u"three"_ustr, m_pDoc->GetString(ScAddress(0, 0, 0)));
m_pDoc->SetValue(ScAddress(1, 0, 0), 4.0);
nError = m_pDoc->GetErrCode(ScAddress(0, 0, 0));
CPPUNIT_ASSERT_MESSAGE("Formula result should be an error due to out-of-bound input..",
nError != FormulaError::NONE);
// Raw data (rows 1 through 12) constchar* aData[] = { "1", "e", "=SQRT(4)", "=SQRT(-2)", "=A4", "=1/0", "=NA()", "bar", "4", "gee", "=1/0", "23" };
SCROW nRows = SAL_N_ELEMENTS(aData); for (SCROW i = 0; i < nRows; ++i)
m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for IFERROR/IFNA");
// formulas and results staticconststruct
{ constchar* pFormula; constchar* pResult;
} aChecks[] = {
{ "=IFERROR(A1;9)", "1" },
{ "=IFERROR(A2;9)", "e" },
{ "=IFERROR(A3;9)", "2" },
{ "=IFERROR(A4;-7)", "-7" },
{ "=IFERROR(A5;-7)", "-7" },
{ "=IFERROR(A6;-7)", "-7" },
{ "=IFERROR(A7;-7)", "-7" },
{ "=IFNA(A6;9)", "#DIV/0!" },
{ "=IFNA(A7;-7)", "-7" },
{ "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)", "4" },
{ "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
{ "{=IFERROR(3*A11:A12;1998)}[0]", "1998" }, // um... this is not the correct way to insert a
{ "{=IFERROR(3*A11:A12;1998)}[1]", "69" } // matrix formula, just a place holder, see below
};
nRows = SAL_N_ELEMENTS(aChecks); for (SCROW i = 0; i < nRows - 2; ++i)
{
SCROW nRow = 20 + i;
m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
}
// Create a matrix range in last two rows of the range above, actual data // of the placeholders.
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_pDoc->InsertMatrixFormula(0, 20 + nRows - 2, 0, 20 + nRows - 1, aMark,
u"=IFERROR(3*A11:A12;1998)"_ustr);
m_pDoc->CalcAll();
for (SCROW i = 0; i < nRows; ++i)
{
SCROW nRow = 20 + i;
OUString aResult = m_pDoc->GetString(0, nRow, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE(aChecks[i].pFormula,
OUString::createFromAscii(aChecks[i].pResult), aResult);
}
CPPUNIT_ASSERT_EQUAL_MESSAGE( "result of SHEETS() should equal the number of sheets, but doesn't.", static_cast<SCTAB>(original), m_pDoc->GetTableCount());
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet",
m_pDoc->InsertTab(SC_TAB_APPEND, u"test2"_ustr));
double modified = m_pDoc->GetValue(0, 0, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
1.0, modified - original);
modified = m_pDoc->GetValue(0, 0, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("result of SHEETS() did not get updated after sheet removal.", 0.0,
modified - original);
m_pDoc->DeleteTab(--nTabCount);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testFuncNOW)
{
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(0, u"foo"_ustr));
double val = 1;
m_pDoc->SetValue(0, 0, 0, val);
m_pDoc->SetString(0, 1, 0, u"=IF(A1>0;NOW();0"_ustr); double now1 = m_pDoc->GetValue(0, 1, 0);
CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
val = 0;
m_pDoc->SetValue(0, 0, 0, val);
m_pDoc->CalcFormulaTree(false, false); double zero = m_pDoc->GetValue(0, 1, 0);
ASSERT_DOUBLES_EQUAL_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", 0.0,
zero);
val = 1;
m_pDoc->SetValue(0, 0, 0, val);
m_pDoc->CalcFormulaTree(false, false); double now2 = m_pDoc->GetValue(0, 1, 0);
CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 0, 0));
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should be a matrix origin.", ScMatrixMode::Formula,
pFC->GetMatrixFlag());
// This should be a 1x3 matrix.
SCCOL nCols = -1;
SCROW nRows = -1;
pFC->GetMatColsRows(nCols, nRows);
CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRows);
// LEN value should be 0 for an empty cell.
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1, 0, 0)));
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1, 1, 0)));
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1, 2, 0)));
// Insert raw data into A1:B3. for (SCROW i = 0; aData[i][0]; ++i)
{
m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
}
// Insert check formulas into A5:B7. for (SCROW i = 0; aData2[i][0]; ++i)
{
m_pDoc->SetString(0, i + 4, 0, OUString::createFromAscii(aData2[i][0]));
m_pDoc->SetString(1, i + 4, 0, OUString::createFromAscii(aData2[i][1]));
}
printRange(m_pDoc, ScRange(0, 4, 0, 1, 6, 0), "Data range for LOOKUP.");
// Values for B5:B7 should be 1, 2, and 3.
CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1, 4, 0))));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1, 5, 0))));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This formula should not have an error code.", 0, static_cast<int>(m_pDoc->GetErrCode(ScAddress(1, 6, 0))));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last column.", u"c"_ustr,
m_pDoc->GetString(0, 0, 0));
m_pDoc->SetString(4, 1, 0, u""_ustr); // E2
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for second last column.", u"b"_ustr,
m_pDoc->GetString(0, 0, 0));
m_pDoc->SetString(6, 1, 0, u"one"_ustr); // G2
m_pDoc->SetString(6, 5, 0, u"two"_ustr); // G6 // Creates an interim array {1,#DIV/0!,#DIV/0!,#DIV/0!,1,#DIV/0!,#DIV/0!,#DIV/0!}
m_pDoc->SetString(7, 8, 0, u"=LOOKUP(2;1/(NOT(ISBLANK(G2:G9)));G2:G9)"_ustr); // H9
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find match for last row.", u"two"_ustr,
m_pDoc->GetString(7, 8, 0));
// Lookup on empty range.
m_pDoc->SetString(9, 8, 0, u"=LOOKUP(2;1/(NOT(ISBLANK(I2:I9)));I2:I9)"_ustr); // J9
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should find no match.", u"#N/A"_ustr, m_pDoc->GetString(9, 8, 0));
// Without the fix in place, this test would have failed with // - Expected: #N/A // - Actual :
CPPUNIT_ASSERT_EQUAL(u"#N/A"_ustr, m_pDoc->GetString(4, 1, 1));
CPPUNIT_ASSERT_EQUAL(u"value2"_ustr, m_pDoc->GetString(4, 2, 1));
CPPUNIT_ASSERT_EQUAL(u"value2"_ustr, m_pDoc->GetString(4, 3, 1));
// Insert raw data into A1:B14. for (SCROW i = 0; aData[i][0]; ++i)
{
m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
}
printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
// Insert formula data into D1:E18. for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
{
m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
}
m_pDoc->CalcAll();
printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
// Verify results. for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
{ if (i == 0) // Skip the header row. continue;
OUString aRes = m_pDoc->GetString(4, i, 0); bool bGood = aRes.equalsAscii(aChecks[i].pRes); if (!bGood)
{
cerr << "row " << (i + 1) << ": lookup value='" << aChecks[i].pLookup << "' expected='"
<< aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
}
}
// Clear the sheet and start over.
clearSheet(m_pDoc, 0);
// Lookup on sorted data interspersed with empty cells.
// Set the formula in C1.
m_pDoc->SetString(ScAddress(2, 0, 0), u"=VLOOKUP(\"C\";A1:A16;1)"_ustr);
CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(2, 0, 0)));
// A21:E24, test position dependent implicit intersection as argument to a // scalar value parameter in a function that has a ReferenceOrForceArray // type parameter somewhere else and formula is not in array mode, // VLOOKUP(Value;ReferenceOrForceArray;...)
std::vector<std::vector<constchar*>> aData2
= { { "1", "one", "3", "=VLOOKUP(C21:C24;A21:B24;2;0)", "three" },
{ "2", "two", "1", "=VLOOKUP(C21:C24;A21:B24;2;0)", "one" },
{ "3", "three", "4", "=VLOOKUP(C21:C24;A21:B24;2;0)", "four" },
{ "4", "four", "2", "=VLOOKUP(C21:C24;A21:B24;2;0)", "two" } };
aPos2.SetCol(3); // column D formula results for (size_t i = 0; i < aData2.size(); ++i)
{
CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aData2[i][4]), m_pDoc->GetString(aPos2));
aPos2.IncRow();
}
m_pDoc->DeleteTab(0);
}
template <size_t DataSize, size_t FormulaSize, int Type> void TestFormula2::runTestMATCH(ScDocument* pDoc, constchar* aData[DataSize], const StrStrCheck aChecks[FormulaSize])
{
size_t nDataSize = DataSize; for (size_t i = 0; i < nDataSize; ++i)
pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
for (size_t i = 0; i < FormulaSize; ++i)
{
pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
{ constchar* pContent = "Some random text";
m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
m_pDoc->SetValue(2, 0, 0, 1.2); // Set numeric value to C1;
for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
m_pDoc->CalcAll();
for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
{
OUString aVal = m_pDoc->GetString(0, i, 0);
CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
}
}
m_pDoc->DeleteTab(0);
}
/** See also test case document fdo#44456 sheet cpearson */
CPPUNIT_TEST_FIXTURE(TestFormula2, testFuncDATEDIF)
{
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(0, u"foo"_ustr));
m_pDoc->CalcAll();
{ // Default (for new documents) is to use current formula syntax // which is Calc A1 const OUString* aChecks[] = { &aTest, &aRefErr, &aRefErr, &aTest };
for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
{
OUString aVal = m_pDoc->GetString(0, i, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
}
}
for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
{
OUString aVal = m_pDoc->GetString(0, i, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
}
}
m_pDoc->DeleteTab(0);
}
// Test case for tdf#83365 - Access across spreadsheet returns Err:504 //
CPPUNIT_TEST_FIXTURE(TestFormula2, testFuncINDIRECT2)
{
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(0, u"foo"_ustr));
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(1, u"bar"_ustr));
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(2, u"baz"_ustr));
// This case should return illegal argument error because // they reference 2 different absolute sheets // Test range triplet (absolute, absolute, absolute) : (absolute, absolute, absolute)
m_pDoc->SetString(0, 9, 2, u"=COUNTIF($bar.$A$1:INDIRECT(\"$A\"&foo.$A$3),1)"_ustr);
m_pDoc->CalcAll();
// Loop all formulas and check result = 10.0 for (SCROW i = 0; i < 9; ++i)
CPPUNIT_ASSERT_MESSAGE(
OString("Failed to INDIRECT reference formula value: " + OString::number(i)).getStr(),
m_pDoc->GetValue(0, i, 2) != 10.0);
// Check formula cell error
ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0, 9, 2));
CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This formula cell should be an error.",
pFC->GetErrCode() != FormulaError::NONE);
// Test for tdf#107724 do not propagate an array context from MATCH to INDIRECT // as INDIRECT returns ParamClass::Reference
CPPUNIT_TEST_FIXTURE(TestFormula2, testFunc_MATCH_INDIRECT)
{
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(0, u"foo"_ustr));
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
// D6: data to match, in 3rd row of named range.
m_pDoc->SetString(3, 5, 0, u"Test1"_ustr); // F15: Formula generating indirect reference of corner addresses taking // row+offset and column from named range, which are not in array context // thus don't create arrays of offsets.
m_pDoc->SetString(5, 14, 0,
u"=MATCH(\"Test1\";INDIRECT(ADDRESS(ROW(RoleAssignment)+1;COLUMN(" "RoleAssignment))&\":\"&ADDRESS(ROW(RoleAssignment)+ROWS(RoleAssignment)-1;" "COLUMN(RoleAssignment)));0)"_ustr);
// Match in 2nd row of range offset by 1 expected.
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to not propagate array context from MATCH to INDIRECT",
2.0, m_pDoc->GetValue(5, 14, 0));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testFormulaDepTracking)
{
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(0, u"foo"_ustr));
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
// B2 listens on D2.
m_pDoc->SetString(aB2, u"=D2"_ustr); double val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Referencing an empty cell should yield zero.", 0.0, val);
{ // Check the internal broadcaster state. auto aState = m_pDoc->GetBroadcasterState();
aState.dump(std::cout, m_pDoc);
CPPUNIT_ASSERT(aState.hasFormulaCellListener(aD2, aB2));
}
// Changing the value of D2 should trigger recalculation of B2.
m_pDoc->SetValue(aD2, 1.1);
val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 1.1, val);
// And again.
m_pDoc->SetValue(aD2, 2.2);
val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 2.2, val);
clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
{ // Make sure nobody is listening on anything. auto aState = m_pDoc->GetBroadcasterState();
aState.dump(std::cout, m_pDoc);
CPPUNIT_ASSERT(aState.aCellListenerStore.empty());
}
// Now, let's test the range dependency tracking.
// B2 listens on D2:E6.
m_pDoc->SetString(aB2, u"=SUM(D2:E6)"_ustr);
val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Summing an empty range should yield zero.", 0.0, val);
{ // Check the internal state to make sure it matches. auto aState = m_pDoc->GetBroadcasterState();
aState.dump(std::cout, m_pDoc);
CPPUNIT_ASSERT(aState.hasFormulaCellListener({ aD2, aE6 }, aB2));
}
// Set value to E3. This should trigger recalc on B2.
m_pDoc->SetValue(aE3, 2.4);
val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on single value change.", 2.4, val);
// Set value to D5 to trigger recalc again. Note that this causes an // addition of 1.2 + 2.4 which is subject to binary floating point // rounding error. We need to use approxEqual to assess its value.
m_pDoc->SetValue(aD5, 1.2);
val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.",
rtl::math::approxEqual(val, 3.6));
// Change the value of D2 (boundary case).
m_pDoc->SetValue(aD2, 1.0);
val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.",
rtl::math::approxEqual(val, 4.6));
// Change the value of E6 (another boundary case).
m_pDoc->SetValue(aE6, 2.0);
val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.",
rtl::math::approxEqual(val, 6.6));
// Change the value of D6 (another boundary case).
m_pDoc->SetValue(aD6, 3.0);
val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.",
rtl::math::approxEqual(val, 9.6));
// Change the value of E2 (another boundary case).
m_pDoc->SetValue(aE2, 0.4);
val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.",
rtl::math::approxEqual(val, 10.0));
// Change the existing non-empty value cell (E2).
m_pDoc->SetValue(aE2, 2.4);
val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.",
rtl::math::approxEqual(val, 12.0));
clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
// Now, column-based dependency tracking. We now switch to the R1C1 // syntax which is easier to use for repeated relative references.
val = 0.0; for (SCROW nRow = 1; nRow <= 9; ++nRow)
{ // Static value in column 1.
m_pDoc->SetValue(0, nRow, 0, ++val);
// Formula in column 2 that references cell to the left.
m_pDoc->SetString(1, nRow, 0, u"=RC[-1]"_ustr);
// Formula in column 3 that references cell to the left.
m_pDoc->SetString(2, nRow, 0, u"=RC[-1]*2"_ustr);
}
// Check formula values.
val = 0.0; for (SCROW nRow = 1; nRow <= 9; ++nRow)
{
++val;
ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val,
m_pDoc->GetValue(1, nRow, 0));
ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", val * 2.0,
m_pDoc->GetValue(2, nRow, 0));
}
// Intentionally insert a formula in column 1. This will break column 1's // uniformity of consisting only of static value cells.
m_pDoc->SetString(aA5, u"=R2C3"_ustr);
ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(aA5));
ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(aB5));
ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc->GetValue(aC5));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testFormulaDepTracking2)
{
CPPUNIT_ASSERT_MESSAGE("failed to insert sheet", m_pDoc->InsertTab(0, u"foo"_ustr));
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
double val = 2.0;
m_pDoc->SetValue(0, 0, 0, val);
val = 4.0;
m_pDoc->SetValue(1, 0, 0, val);
val = 5.0;
m_pDoc->SetValue(0, 1, 0, val);
m_pDoc->SetString(2, 0, 0, u"=A1/B1"_ustr);
m_pDoc->SetString(1, 1, 0, u"=B1*C1"_ustr);
CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
// A4 should have a broadcaster with A5 listening to it.
SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0, 4, 0));
CPPUNIT_ASSERT(pBC);
SvtBroadcaster::ListenersType* pListeners = &pBC->GetAllListeners();
CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should have one listener.", size_t(1), pListeners->size()); const SvtListener* pListener = pListeners->at(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("A6 should be listening to A5.", static_cast<const ScFormulaCell*>(pListener), pFC);
pBC = m_pDoc->GetBroadcaster(ScAddress(0, 3, 0));
CPPUNIT_ASSERT_MESSAGE("Broadcaster at A5 should have shifted to A4.", pBC);
pListeners = &pBC->GetAllListeners();
CPPUNIT_ASSERT_EQUAL_MESSAGE("A3 should have one listener.", size_t(1), pListeners->size());
pFC = m_pDoc->GetFormulaCell(ScAddress(0, 4, 0));
CPPUNIT_ASSERT(pFC);
pListener = pListeners->at(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("A5 should be listening to A4.", static_cast<const ScFormulaCell*>(pListener), pFC);
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
// Set a numeric value to A1.
m_pDoc->SetValue(ScAddress(0, 0, 0), 11.0);
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_pDoc->InsertMatrixFormula(1, 0, 1, 0, aMark, u"=A1"_ustr);
CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1, 0, 0)));
ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 0, 0));
CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC);
pFC->SetChanged( false); // Clear this flag to simulate displaying of formula cell value on screen.
// Put a new value into A1. The formula should update.
m_pDoc->SetValue(ScAddress(0, 0, 0), 13.0);
CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(1, 0, 0)));
// Test external references on the main document while the external // document is still in memory.
m_pDoc->InsertTab(0, u"Test Sheet"_ustr);
m_pDoc->SetString(0, 0, 0, u"='file:///extdata.fake'#Data1.A1"_ustr);
OUString test = m_pDoc->GetString(0, 0, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", name, test);
// After the initial access to the external document, the external ref // manager should create sheet cache entries for *all* sheets from that // document. Note that the doc may have more than 3 sheets but ensure // that the first 3 are what we expect.
ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
vector<OUString> aTabNames;
pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[0], aExtSh1Name);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[1], aExtSh2Name);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected sheet name.", aTabNames[2], aExtSh3Name);
m_pDoc->SetString(1, 0, 0, u"='file:///extdata.fake'#Data1.B1"_ustr);
test = m_pDoc->GetString(1, 0, 0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Value is different from the original", value, test);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.