update credits
[LibreOffice.git] / sc / qa / unit / ucalc.cxx
blob0bda2a4d9707df33644d646de05af3af0e68a54e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
9 #include <sal/config.h>
10 #include <test/bootstrapfixture.hxx>
12 #include <rtl/strbuf.hxx>
13 #include <osl/file.hxx>
14 #include <osl/time.h>
16 #include "scdll.hxx"
17 #include "formulacell.hxx"
18 #include "document.hxx"
19 #include "stringutil.hxx"
20 #include "scmatrix.hxx"
21 #include "drwlayer.hxx"
22 #include "scitems.hxx"
23 #include "reffind.hxx"
24 #include "markdata.hxx"
25 #include "clipparam.hxx"
26 #include "refundo.hxx"
27 #include "undoblk.hxx"
28 #include "undotab.hxx"
29 #include "queryentry.hxx"
30 #include "postit.hxx"
31 #include "attrib.hxx"
32 #include "dbdata.hxx"
33 #include "reftokenhelper.hxx"
34 #include "userdat.hxx"
36 #include "docsh.hxx"
37 #include "docfunc.hxx"
38 #include "dbdocfun.hxx"
39 #include "funcdesc.hxx"
40 #include "externalrefmgr.hxx"
42 #include "dpshttab.hxx"
43 #include "dpobject.hxx"
44 #include "dpsave.hxx"
45 #include "dpdimsave.hxx"
46 #include "dpcache.hxx"
47 #include "dpfilteredcache.hxx"
48 #include "calcconfig.hxx"
49 #include "interpre.hxx"
50 #include "columniterator.hxx"
51 #include "types.hxx"
52 #include "conditio.hxx"
53 #include "globstr.hrc"
54 #include "tokenarray.hxx"
56 #include "formula/IFunctionDescription.hxx"
58 #include <basegfx/polygon/b2dpolygon.hxx>
59 #include <editeng/boxitem.hxx>
61 #include <svx/svdograf.hxx>
62 #include <svx/svdpage.hxx>
63 #include <svx/svdocirc.hxx>
64 #include <svx/svdopath.hxx>
66 #include <sfx2/docfile.hxx>
68 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
69 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
70 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
71 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
72 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
73 #include <com/sun/star/sheet/GeneralFunction.hpp>
75 #include <iostream>
76 #include <sstream>
77 #include <vector>
79 #define CALC_DEBUG_OUTPUT 0
80 #define CALC_TEST_PERF 0
82 #include "helper/debughelper.hxx"
83 #include "helper/qahelper.hxx"
85 const int indeterminate = 2;
87 using namespace ::com::sun::star;
89 using ::std::cout;
90 using ::std::cerr;
91 using ::std::endl;
92 using ::std::vector;
94 namespace {
96 class Test : public test::BootstrapFixture {
97 public:
98 Test();
100 virtual void setUp();
101 virtual void tearDown();
104 * Basic performance regression test. Pick some actions that *should* take
105 * only a fraction of a second to complete, and make sure they stay that
106 * way. We set the threshold to 1 second for each action which should be
107 * large enough to accommodate slower machines or machines with high load.
109 void testPerf();
110 void testCollator();
111 void testRangeList();
112 void testInput();
113 void testFormulaHashAndTag();
114 void testCellFunctions();
115 void testCopyToDocument();
117 * Make sure the SHEETS function gets properly updated during sheet
118 * insertion and removal.
120 void testSheetsFunc();
121 void testVolatileFunc();
124 * Basic test for formula dependency tracking.
126 void testFormulaDepTracking();
129 * Another test for formula dependency tracking, inspired by fdo#56278.
131 void testFormulaDepTracking2();
134 * More direct test for cell broadcaster management, used to track formula
135 * dependencies.
137 void testCellBroadcaster();
139 void testFuncParam();
140 void testNamedRange();
141 void testCSV();
142 void testMatrix();
143 void testEnterMixedMatrix();
146 * Basic test for pivot tables.
148 void testPivotTable();
151 * Test against unwanted automatic format detection on field names and
152 * field members in pivot tables.
154 void testPivotTableLabels();
157 * Make sure that we set cells displaying date values numeric cells,
158 * rather than text cells. Grouping by date or number functionality
159 * depends on this.
161 void testPivotTableDateLabels();
164 * Test for pivot table's filtering functionality by page fields.
166 void testPivotTableFilters();
169 * Test for pivot table's named source range.
171 void testPivotTableNamedSource();
174 * Test for pivot table cache. Each dimension in the pivot cache stores
175 * only unique values that are sorted in ascending order.
177 void testPivotTableCache();
180 * Test for pivot table containing data fields that reference the same
181 * source field but different functions.
183 void testPivotTableDuplicateDataFields();
185 void testPivotTableNormalGrouping();
186 void testPivotTableNumberGrouping();
187 void testPivotTableDateGrouping();
188 void testPivotTableEmptyRows();
189 void testPivotTableTextNumber();
192 * Test for checking that pivot table treats strings in a case insensitive
193 * manner.
195 void testPivotTableCaseInsensitiveStrings();
198 * Test for pivot table's handling of double-precision numbers that are
199 * very close together.
201 void testPivotTableNumStability();
204 * Test for pivot table that include field with various non-default field
205 * refrences.
207 void testPivotTableFieldReference();
210 * Test pivot table functionality performed via ScDBDocFunc.
212 void testPivotTableDocFunc();
214 void testSheetCopy();
215 void testSheetMove();
216 void testExternalRef();
217 void testExternalRefFunctions();
218 void testDataArea();
219 void testAutofilter();
220 void testCopyPaste();
221 void testCopyPasteRelativeFormula();
222 void testMergedCells();
223 void testUpdateReference();
226 * Make sure the sheet streams are invalidated properly.
228 void testStreamValid();
231 * Test built-in cell functions to make sure their categories and order
232 * are correct.
234 void testFunctionLists();
236 void testGraphicsInGroup();
237 void testGraphicsOnSheetMove();
239 void testPostIts();
242 * Test toggling relative/absolute flag of cell and cell range references.
243 * This corresponds with hitting Shift-F4 while the cursor is on a formula
244 * cell.
246 void testToggleRefFlag();
249 * Test to make sure correct precedent / dependent cells are obtained when
250 * preparing to jump to them.
252 void testJumpToPrecedentsDependents();
254 void testSetBackgroundColor();
255 void testRenameTable();
257 void testAutoFill();
258 void testCopyPasteFormulas();
259 void testCopyPasteFormulasExternalDoc();
261 void testFindAreaPosRowDown();
262 void testFindAreaPosColRight();
263 void testSort();
264 void testSortWithFormulaRefs();
265 void testShiftCells();
266 void testDeleteRow();
267 void testDeleteCol();
268 void testAnchoredRotatedShape();
269 void testCellTextWidth();
272 * Test formula & formula grouping
274 void testFormulaGrouping();
275 void testCondFormatINSDEL();
277 CPPUNIT_TEST_SUITE(Test);
278 #if CALC_TEST_PERF
279 CPPUNIT_TEST(testPerf);
280 #endif
281 CPPUNIT_TEST(testCollator);
282 CPPUNIT_TEST(testRangeList);
283 CPPUNIT_TEST(testInput);
284 CPPUNIT_TEST(testFormulaHashAndTag);
285 CPPUNIT_TEST(testCellFunctions);
286 CPPUNIT_TEST(testCopyToDocument);
287 CPPUNIT_TEST(testSheetsFunc);
288 CPPUNIT_TEST(testVolatileFunc);
289 CPPUNIT_TEST(testFormulaDepTracking);
290 CPPUNIT_TEST(testFormulaDepTracking2);
291 CPPUNIT_TEST(testCellBroadcaster);
292 CPPUNIT_TEST(testFuncParam);
293 CPPUNIT_TEST(testNamedRange);
294 CPPUNIT_TEST(testCSV);
295 CPPUNIT_TEST(testMatrix);
296 CPPUNIT_TEST(testEnterMixedMatrix);
297 CPPUNIT_TEST(testPivotTable);
298 CPPUNIT_TEST(testPivotTableLabels);
299 CPPUNIT_TEST(testPivotTableDateLabels);
300 CPPUNIT_TEST(testPivotTableFilters);
301 CPPUNIT_TEST(testPivotTableNamedSource);
302 CPPUNIT_TEST(testPivotTableCache);
303 CPPUNIT_TEST(testPivotTableDuplicateDataFields);
304 CPPUNIT_TEST(testPivotTableNormalGrouping);
305 CPPUNIT_TEST(testPivotTableNumberGrouping);
306 CPPUNIT_TEST(testPivotTableDateGrouping);
307 CPPUNIT_TEST(testPivotTableEmptyRows);
308 CPPUNIT_TEST(testPivotTableTextNumber);
309 CPPUNIT_TEST(testPivotTableCaseInsensitiveStrings);
310 CPPUNIT_TEST(testPivotTableNumStability);
311 CPPUNIT_TEST(testPivotTableFieldReference);
312 CPPUNIT_TEST(testPivotTableDocFunc);
313 CPPUNIT_TEST(testSheetCopy);
314 CPPUNIT_TEST(testSheetMove);
315 CPPUNIT_TEST(testExternalRef);
316 CPPUNIT_TEST(testExternalRefFunctions);
317 CPPUNIT_TEST(testDataArea);
318 CPPUNIT_TEST(testGraphicsInGroup);
319 CPPUNIT_TEST(testGraphicsOnSheetMove);
320 CPPUNIT_TEST(testPostIts);
321 CPPUNIT_TEST(testStreamValid);
322 CPPUNIT_TEST(testFunctionLists);
323 CPPUNIT_TEST(testToggleRefFlag);
324 CPPUNIT_TEST(testAutofilter);
325 CPPUNIT_TEST(testCopyPaste);
326 CPPUNIT_TEST(testCopyPasteRelativeFormula);
327 CPPUNIT_TEST(testMergedCells);
328 CPPUNIT_TEST(testUpdateReference);
329 CPPUNIT_TEST(testJumpToPrecedentsDependents);
330 CPPUNIT_TEST(testSetBackgroundColor);
331 CPPUNIT_TEST(testRenameTable);
332 CPPUNIT_TEST(testAutoFill);
333 CPPUNIT_TEST(testCopyPasteFormulas);
334 CPPUNIT_TEST(testCopyPasteFormulasExternalDoc);
335 CPPUNIT_TEST(testFindAreaPosRowDown);
336 CPPUNIT_TEST(testFindAreaPosColRight);
337 CPPUNIT_TEST(testSort);
338 CPPUNIT_TEST(testSortWithFormulaRefs);
339 CPPUNIT_TEST(testShiftCells);
340 CPPUNIT_TEST(testDeleteRow);
341 CPPUNIT_TEST(testDeleteCol);
342 CPPUNIT_TEST(testAnchoredRotatedShape);
343 CPPUNIT_TEST(testCellTextWidth);
344 CPPUNIT_TEST(testFormulaGrouping);
345 CPPUNIT_TEST(testCondFormatINSDEL);
346 CPPUNIT_TEST_SUITE_END();
348 private:
349 ScDocument *m_pDoc;
350 ScDocShellRef m_xDocShRef;
353 void clearRange(ScDocument* pDoc, const ScRange& rRange)
355 ScMarkData aMarkData;
356 aMarkData.SetMarkArea(rRange);
357 pDoc->DeleteArea(
358 rRange.aStart.Col(), rRange.aStart.Row(),
359 rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, IDF_CONTENTS);
362 void printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption)
364 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
365 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
366 SheetPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1);
367 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
369 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
371 OUString aVal = pDoc->GetString(nCol, nRow, rRange.aStart.Tab());
372 printer.set(nRow-nRow1, nCol-nCol1, aVal);
375 printer.print(pCaption);
378 template<size_t _Size>
379 ScRange insertRangeData(ScDocument* pDoc, const ScAddress& rPos, const char* aData[][_Size], size_t nRowCount)
381 for (size_t i = 0; i < _Size; ++i)
383 for (size_t j = 0; j < nRowCount; ++j)
385 if (!aData[j][i])
386 continue;
388 SCCOL nCol = i + rPos.Col();
389 SCROW nRow = j + rPos.Row();
390 pDoc->SetString(nCol, nRow, rPos.Tab(), OUString(aData[j][i], strlen(aData[j][i]), RTL_TEXTENCODING_UTF8));
394 ScRange aRange(rPos);
395 aRange.aEnd.SetCol(rPos.Col()+_Size-1);
396 aRange.aEnd.SetRow(rPos.Row()+nRowCount-1);
397 printRange(pDoc, aRange, "Range data content");
398 return aRange;
402 * Temporarily switch on/off auto calculation mode.
404 class AutoCalcSwitch
406 ScDocument* mpDoc;
407 bool mbOldValue;
408 public:
409 AutoCalcSwitch(ScDocument* pDoc, bool bAutoCalc) : mpDoc(pDoc), mbOldValue(pDoc->GetAutoCalc())
411 mpDoc->SetAutoCalc(bAutoCalc);
414 ~AutoCalcSwitch()
416 mpDoc->SetAutoCalc(mbOldValue);
421 * Temporarily set formula grammar.
423 class FormulaGrammarSwitch
425 ScDocument* mpDoc;
426 formula::FormulaGrammar::Grammar meOldGrammar;
427 public:
428 FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
429 mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
431 mpDoc->SetGrammar(eGrammar);
434 ~FormulaGrammarSwitch()
436 mpDoc->SetGrammar(meOldGrammar);
440 class MeasureTimeSwitch
442 double& mrDiff;
443 TimeValue maTimeBefore;
444 public:
445 MeasureTimeSwitch(double& rDiff) : mrDiff(rDiff)
447 mrDiff = 9999.0;
448 osl_getSystemTime(&maTimeBefore);
451 ~MeasureTimeSwitch()
453 TimeValue aTimeAfter;
454 osl_getSystemTime(&aTimeAfter);
455 mrDiff = getTimeDiff(aTimeAfter, maTimeBefore);
458 double getTimeDiff(const TimeValue& t1, const TimeValue& t2) const
460 double tv1 = t1.Seconds;
461 double tv2 = t2.Seconds;
462 tv1 += t1.Nanosec / 1000000000.0;
463 tv2 += t2.Nanosec / 1000000000.0;
465 return tv1 - tv2;
469 Test::Test()
470 : m_pDoc(0)
474 void Test::setUp()
476 BootstrapFixture::setUp();
478 ScDLL::Init();
479 m_xDocShRef = new ScDocShell(
480 SFXMODEL_STANDARD |
481 SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
482 SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
484 m_xDocShRef->DoInitUnitTest();
485 m_pDoc = m_xDocShRef->GetDocument();
488 void Test::tearDown()
490 m_xDocShRef.Clear();
491 BootstrapFixture::tearDown();
494 #define PERF_ASSERT(df,scale,time,message) \
495 do { \
496 double dfscaled = df / scale; \
497 if ((dfscaled) >= (time)) \
499 std::ostringstream os; \
500 os << message << " took " << dfscaled << " pseudo-cycles (" << df << " real-time seconds), expected: " << time << " pseudo-cycles."; \
501 CPPUNIT_FAIL(os.str().c_str()); \
503 } while (0)
505 void Test::testPerf()
507 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
509 // First do a set of simple operations to try to work out
510 // how fast (or not) this particular machine is:
511 double scale;
513 MeasureTimeSwitch aTime(scale);
514 for (int i = 0; i < 10000000; ++i)
516 // Bang on the allocator
517 volatile ScRange *pRange = new ScRange (ScAddress (0,0,0));
518 // Calc does quite a bit of string conversion
519 volatile double it = OUString::number ((double)i/253.0).toDouble();
520 // Do we have floating point math ?
521 volatile double another = rtl::math::sin (it);
522 (void)another;
523 delete pRange;
526 printf("CPU scale factor %g\n", scale);
528 // FIXME: we should check if this already took too long
529 // and if so not run the perf. tests to have pity on some
530 // slow ARM machines - I think.
532 // to make the numbers more round and helpful,
533 // but the calculation of scale reasonably precise.
534 scale /= 100000.0;
536 double diff;
538 // Clearing an already empty sheet should finish in a fraction of a
539 // second. Flag failure if it takes more than one second. Clearing 100
540 // columns should be large enough to flag if something goes wrong.
542 MeasureTimeSwitch aTime(diff);
543 clearRange(m_pDoc, ScRange(0,0,0,99,MAXROW,0));
545 PERF_ASSERT(diff, scale, 1.0, "Clearing an empty sheet");
548 // Switch to R1C1 to make it easier to input relative references in multiple cells.
549 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
551 // Insert formulas in B1:B100000. This shouldn't take long, but may take
552 // close to a second on a slower machine. We don't measure this yet, for
553 // now.
554 for (SCROW i = 0; i < 100000; ++i)
555 m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
557 // Now, Delete B2:B100000. This should complete in a fraction of a second
558 // (0.06 sec on my machine).
560 MeasureTimeSwitch aTime(diff);
561 clearRange(m_pDoc, ScRange(1,1,0,1,99999,0));
563 PERF_ASSERT(diff, scale, 2000, "Removal of a large array of formula cells");
566 clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
567 CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
568 CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
571 // Measure the performance of repeat-pasting a single cell to a large
572 // cell range, undoing it, and redoing it.
574 ScAddress aPos(0,0,0);
575 m_pDoc->SetString(aPos, "test");
576 ScMarkData aMark;
577 aMark.SelectOneTable(0);
579 // Copy cell A1 to clipboard.
580 ScDocument aClipDoc(SCDOCMODE_CLIP);
581 ScClipParam aParam(aPos, false);
582 m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
583 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
585 ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
586 pUndoDoc->InitUndo(m_pDoc, 0, 0);
587 m_pDoc->CopyToDocument(ScRange(aPos), IDF_CONTENTS, false, pUndoDoc, &aMark);
589 // Paste it to A2:A100000, and measure its duration.
590 ScRange aPasteRange(0,1,0,0,99999,0);
591 aMark.SetMarkArea(aPasteRange);
594 MeasureTimeSwitch aTime(diff);
595 m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
597 PERF_ASSERT(diff, scale, 1500.0, "Pasting a single cell to A2:A100000");
599 ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
600 pRedoDoc->InitUndo(m_pDoc, 0, 0);
601 m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
603 // Create an undo object for this.
604 ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
605 ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
607 // Make sure it did what it's supposed to do.
608 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
609 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
612 MeasureTimeSwitch aTime(diff);
613 aUndo.Undo();
615 PERF_ASSERT(diff, scale, 500.0, "Undoing a pasting of a cell to A2:A100000");
617 // Make sure it's really undone.
618 CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, m_pDoc->GetCellType(aPos));
619 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
620 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
622 // Now redo.
624 MeasureTimeSwitch aTime(diff);
625 aUndo.Redo();
627 PERF_ASSERT(diff, scale, 1000.0, "Redoing a pasting of a cell to A2:A100000");
629 // Make sure it's really redone.
630 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
631 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
634 clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
635 CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
636 CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
639 // Measure the performance of repeat-pasting 2 adjacent cells to a
640 // large cell range, undoing it, and redoing it. The bottom one of
641 // the two source cells is empty.
643 ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
645 ScAddress aPos(0,0,0);
646 m_pDoc->SetValue(aPos, 12);
647 ScMarkData aMark;
648 aMark.SetMarkArea(aSrcRange);
650 // Copy to clipboard.
651 ScDocument aClipDoc(SCDOCMODE_CLIP);
652 ScClipParam aParam(aSrcRange, false);
653 m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
654 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
656 ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
657 pUndoDoc->InitUndo(m_pDoc, 0, 0);
658 m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
660 // Paste it to A3:A100001, and measure its duration.
661 ScRange aPasteRange(0,2,0,0,100000,0);
662 aMark.SetMarkArea(aPasteRange);
665 MeasureTimeSwitch aTime(diff);
666 m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
668 PERF_ASSERT(diff, scale, 1000.0, "Pasting A1:A2 to A3:A100001");
670 ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
671 pRedoDoc->InitUndo(m_pDoc, 0, 0);
672 m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
674 // Create an undo object for this.
675 ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
676 ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
678 // Make sure it did what it's supposed to do.
679 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
680 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
681 ScAddress aTmp = aPasteRange.aStart;
682 aTmp.IncRow();
683 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
686 MeasureTimeSwitch aTime(diff);
687 aUndo.Undo();
689 PERF_ASSERT(diff, scale, 500.0, "Undoing");
691 // Make sure it's really undone.
692 CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(aPos));
693 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
694 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
696 // Now redo.
698 MeasureTimeSwitch aTime(diff);
699 aUndo.Redo();
701 PERF_ASSERT(diff, scale, 800.0, "Redoing");
703 // Make sure it's really redone.
704 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
705 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
708 clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
709 CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
710 CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
713 // Measure the performance of repeat-pasting 2 adjacent cells to a
714 // large cell range, undoing it, and redoing it. The bottom one of
715 // the two source cells is empty. In this scenario, the non-empty
716 // cell is a formula cell referencing a cell to the right, which
717 // inserts a broadcaster to cell it references. So it has a higher
718 // overhead than the previous scenario.
720 ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
722 ScAddress aPos(0,0,0);
723 m_pDoc->SetString(aPos, "=B1");
724 ScMarkData aMark;
725 aMark.SetMarkArea(aSrcRange);
727 // Copy to clipboard.
728 ScDocument aClipDoc(SCDOCMODE_CLIP);
729 ScClipParam aParam(aSrcRange, false);
730 m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
731 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
733 ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
734 pUndoDoc->InitUndo(m_pDoc, 0, 0);
735 m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
737 // Paste it to A3:A50001, and measure its duration.
738 ScRange aPasteRange(0,2,0,0,50000,0);
739 aMark.SetMarkArea(aPasteRange);
742 MeasureTimeSwitch aTime(diff);
743 m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
745 PERF_ASSERT(diff, scale, 2000.0, "Pasting");
747 ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
748 pRedoDoc->InitUndo(m_pDoc, 0, 0);
749 m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
751 // Create an undo object for this.
752 ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
753 ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
755 // Make sure it did what it's supposed to do.
756 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
757 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
758 ScAddress aTmp = aPasteRange.aStart;
759 aTmp.IncRow();
760 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
762 #if 0 // TODO: Undo and redo of this scenario is currently not fast enough to be tested reliably.
764 MeasureTimeSwitch aTime(diff);
765 aUndo.Undo();
767 PERF_ASSERT(diff, scale, 1.0, "Undoing");
769 // Make sure it's really undone.
770 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPos));
771 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
772 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
774 // Now redo.
776 MeasureTimeSwitch aTime(diff);
777 aUndo.Redo();
779 PERF_ASSERT(diff, scale, 1.0, "Redoing");
781 // Make sure it's really redone.
782 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
783 CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
784 #endif
787 m_pDoc->DeleteTab(0);
790 void Test::testCollator()
792 OUString s1("A");
793 OUString s2("B");
794 CollatorWrapper* p = ScGlobal::GetCollator();
795 sal_Int32 nRes = p->compareString(s1, s2);
796 CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
799 void Test::testRangeList()
801 m_pDoc->InsertTab(0, "foo");
803 ScRangeList aRL;
804 aRL.Append(ScRange(1,1,0,3,10,0));
805 CPPUNIT_ASSERT_MESSAGE("List should have one range.", aRL.size() == 1);
806 const ScRange* p = aRL[0];
807 CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
808 CPPUNIT_ASSERT_MESSAGE("Wrong range.", p->aStart == ScAddress(1,1,0) && p->aEnd == ScAddress(3,10,0));
810 // TODO: Add more tests here.
812 m_pDoc->DeleteTab(0);
815 void Test::testInput()
817 OUString aTabName("foo");
818 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
819 m_pDoc->InsertTab (0, aTabName));
821 OUString numstr("'10.5");
822 OUString str("'apple'");
823 OUString test;
825 m_pDoc->SetString(0, 0, 0, numstr);
826 test = m_pDoc->GetString(0, 0, 0);
827 bool bTest = test == "10.5";
828 CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
829 m_pDoc->SetString(0, 0, 0, str);
830 test = m_pDoc->GetString(0, 0, 0);
831 bTest = test == "'apple'";
832 CPPUNIT_ASSERT_MESSAGE("Text content should have retained the first apostrophe.", bTest);
834 // Customized string handling policy.
835 ScSetStringParam aParam;
836 aParam.setTextInput();
837 m_pDoc->SetString(0, 0, 0, "000123", &aParam);
838 test = m_pDoc->GetString(0, 0, 0);
839 CPPUNIT_ASSERT_MESSAGE("Text content should have been treated as string, not number.", test == "000123");
841 m_pDoc->DeleteTab(0);
844 void testFuncSUM(ScDocument* pDoc)
846 double val = 1;
847 double result;
848 pDoc->SetValue (0, 0, 0, val);
849 pDoc->SetValue (0, 1, 0, val);
850 pDoc->SetString (0, 2, 0, OUString("=SUM(A1:A2)"));
851 pDoc->CalcAll();
852 pDoc->GetValue (0, 2, 0, result);
853 CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 2.0);
856 void testFuncPRODUCT(ScDocument* pDoc)
858 double val = 1;
859 double result;
860 pDoc->SetValue(0, 0, 0, val);
861 val = 2;
862 pDoc->SetValue(0, 1, 0, val);
863 val = 3;
864 pDoc->SetValue(0, 2, 0, val);
865 pDoc->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
866 pDoc->CalcAll();
867 pDoc->GetValue(0, 3, 0, result);
868 CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT failed", result == 6.0);
870 pDoc->SetString(0, 4, 0, OUString("=PRODUCT({1;2;3})"));
871 pDoc->CalcAll();
872 pDoc->GetValue(0, 4, 0, result);
873 CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT with inline array failed", result == 6.0);
876 void testFuncN(ScDocument* pDoc)
878 double result;
880 // Clear the area first.
881 clearRange(pDoc, ScRange(0, 0, 0, 1, 20, 0));
883 // Put values to reference.
884 double val = 0;
885 pDoc->SetValue(0, 0, 0, val);
886 pDoc->SetString(0, 2, 0, OUString("Text"));
887 val = 1;
888 pDoc->SetValue(0, 3, 0, val);
889 val = -1;
890 pDoc->SetValue(0, 4, 0, val);
891 val = 12.3;
892 pDoc->SetValue(0, 5, 0, val);
893 pDoc->SetString(0, 6, 0, OUString("'12.3"));
895 // Cell references
896 pDoc->SetString(1, 0, 0, OUString("=N(A1)"));
897 pDoc->SetString(1, 1, 0, OUString("=N(A2)"));
898 pDoc->SetString(1, 2, 0, OUString("=N(A3)"));
899 pDoc->SetString(1, 3, 0, OUString("=N(A4)"));
900 pDoc->SetString(1, 4, 0, OUString("=N(A5)"));
901 pDoc->SetString(1, 5, 0, OUString("=N(A6)"));
902 pDoc->SetString(1, 6, 0, OUString("=N(A9)"));
904 // In-line values
905 pDoc->SetString(1, 7, 0, OUString("=N(0)"));
906 pDoc->SetString(1, 8, 0, OUString("=N(1)"));
907 pDoc->SetString(1, 9, 0, OUString("=N(-1)"));
908 pDoc->SetString(1, 10, 0, OUString("=N(123)"));
909 pDoc->SetString(1, 11, 0, OUString("=N(\"\")"));
910 pDoc->SetString(1, 12, 0, OUString("=N(\"12\")"));
911 pDoc->SetString(1, 13, 0, OUString("=N(\"foo\")"));
913 // Range references
914 pDoc->SetString(2, 2, 0, OUString("=N(A1:A8)"));
915 pDoc->SetString(2, 3, 0, OUString("=N(A1:A8)"));
916 pDoc->SetString(2, 4, 0, OUString("=N(A1:A8)"));
917 pDoc->SetString(2, 5, 0, OUString("=N(A1:A8)"));
919 // Calculate and check the results.
920 pDoc->CalcAll();
921 double checks1[] = {
922 0, 0, 0, 1, -1, 12.3, 0, // cell reference
923 0, 1, -1, 123, 0, 0, 0 // in-line values
925 for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
927 pDoc->GetValue(1, i, 0, result);
928 bool bGood = result == checks1[i];
929 if (!bGood)
931 cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
932 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
935 double checks2[] = {
936 0, 1, -1, 12.3 // range references
938 for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
940 pDoc->GetValue(1, i+2, 0, result);
941 bool bGood = result == checks2[i];
942 if (!bGood)
944 cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
945 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
950 void testFuncCOUNTIF(ScDocument* pDoc)
952 // COUNTIF (test case adopted from OOo i#36381)
954 // Empty A1:A39 first.
955 clearRange(pDoc, ScRange(0, 0, 0, 0, 40, 0));
957 // Raw data (rows 1 through 9)
958 const char* aData[] = {
959 "1999",
960 "2000",
961 "0",
962 "0",
963 "0",
964 "2002",
965 "2001",
966 "X",
967 "2002"
970 SCROW nRows = SAL_N_ELEMENTS(aData);
971 for (SCROW i = 0; i < nRows; ++i)
972 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
974 printRange(pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
976 // formulas and results
977 struct {
978 const char* pFormula; double fResult;
979 } aChecks[] = {
980 { "=COUNTIF(A1:A12;1999)", 1 },
981 { "=COUNTIF(A1:A12;2002)", 2 },
982 { "=COUNTIF(A1:A12;1998)", 0 },
983 { "=COUNTIF(A1:A12;\">=1999\")", 5 },
984 { "=COUNTIF(A1:A12;\">1999\")", 4 },
985 { "=COUNTIF(A1:A12;\"<2001\")", 5 },
986 { "=COUNTIF(A1:A12;\">0\")", 5 },
987 { "=COUNTIF(A1:A12;\">=0\")", 8 },
988 { "=COUNTIF(A1:A12;0)", 3 },
989 { "=COUNTIF(A1:A12;\"X\")", 1 },
990 { "=COUNTIF(A1:A12;)", 3 }
993 nRows = SAL_N_ELEMENTS(aChecks);
994 for (SCROW i = 0; i < nRows; ++i)
996 SCROW nRow = 20 + i;
997 pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
999 pDoc->CalcAll();
1001 for (SCROW i = 0; i < nRows; ++i)
1003 double result;
1004 SCROW nRow = 20 + i;
1005 pDoc->GetValue(0, nRow, 0, result);
1006 bool bGood = result == aChecks[i].fResult;
1007 if (!bGood)
1009 cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
1010 << " expected=" << aChecks[i].fResult << " actual=" << result << endl;
1011 CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
1015 // Don't count empty strings when searching for a number.
1017 // Clear A1:A2.
1018 clearRange(pDoc, ScRange(0, 0, 0, 0, 1, 0));
1020 pDoc->SetString(0, 0, 0, OUString("=\"\""));
1021 pDoc->SetString(0, 1, 0, OUString("=COUNTIF(A1;1)"));
1022 pDoc->CalcAll();
1024 double result = pDoc->GetValue(0, 1, 0);
1025 CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
1028 void testFuncIFERROR(ScDocument* pDoc)
1030 // IFERROR/IFNA (fdo#56124)
1032 // Empty A1:A39 first.
1033 clearRange(pDoc, ScRange(0, 0, 0, 0, 40, 0));
1035 // Raw data (rows 1 through 12)
1036 const char* aData[] = {
1037 "1",
1038 "e",
1039 "=SQRT(4)",
1040 "=SQRT(-2)",
1041 "=A4",
1042 "=1/0",
1043 "=NA()",
1044 "bar",
1045 "4",
1046 "gee",
1047 "=1/0",
1048 "23"
1051 SCROW nRows = SAL_N_ELEMENTS(aData);
1052 for (SCROW i = 0; i < nRows; ++i)
1053 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1055 printRange(pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
1057 // formulas and results
1058 struct {
1059 const char* pFormula; const char* pResult;
1060 } aChecks[] = {
1061 { "=IFERROR(A1;9)", "1" },
1062 { "=IFERROR(A2;9)", "e" },
1063 { "=IFERROR(A3;9)", "2" },
1064 { "=IFERROR(A4;-7)", "-7" },
1065 { "=IFERROR(A5;-7)", "-7" },
1066 { "=IFERROR(A6;-7)", "-7" },
1067 { "=IFERROR(A7;-7)", "-7" },
1068 { "=IFNA(A6;9)", "#DIV/0!" },
1069 { "=IFNA(A7;-7)", "-7" },
1070 { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)", "4" },
1071 { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
1072 { "{=IFERROR(3*A11:A12;1998)}[0]", "1998" }, // um.. this is not the correct way to insert a
1073 { "{=IFERROR(3*A11:A12;1998)}[1]", "69" } // matrix formula, just a place holder, see below
1076 nRows = SAL_N_ELEMENTS(aChecks);
1077 for (SCROW i = 0; i < nRows-2; ++i)
1079 SCROW nRow = 20 + i;
1080 pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
1083 // Create a matrix range in last two rows of the range above, actual data
1084 // of the placeholders.
1085 ScMarkData aMark;
1086 aMark.SelectOneTable(0);
1087 pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)", NULL);
1089 pDoc->CalcAll();
1091 for (SCROW i = 0; i < nRows; ++i)
1093 SCROW nRow = 20 + i;
1094 OUString aResult = pDoc->GetString(0, nRow, 0);
1095 CPPUNIT_ASSERT_EQUAL_MESSAGE(
1096 aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
1100 void testFuncNUMBERVALUE( ScDocument* pDoc )
1102 // NUMBERVALUE fdo#57180
1104 // Empty A1:A39 first.
1105 clearRange(pDoc, ScRange(0, 0, 0, 0, 40, 0));
1107 // Raw data (rows 1 through 6)
1108 const char* aData[] = {
1109 "1ag9a9b9",
1110 "1ag34 5g g6 78b9%%",
1111 "1 234d56E-2",
1112 "d4",
1113 "54.4",
1114 "1a2b3e1%"
1117 SCROW nRows = SAL_N_ELEMENTS(aData);
1118 for (SCROW i = 0; i < nRows; ++i)
1119 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1121 printRange(pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
1123 // formulas and results
1124 struct {
1125 const char* pFormula; const char* pResult;
1126 } aChecks[] = {
1127 { "=NUMBERVALUE(A1;\"b\";\"ag\")", "199.9" },
1128 { "=NUMBERVALUE(A2;\"b\";\"ag\")", "134.56789" },
1129 { "=NUMBERVALUE(A2;\"b\";\"g\")", "#VALUE!" },
1130 { "=NUMBERVALUE(A3;\"d\")", "12.3456" },
1131 { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
1132 { "=NUMBERVALUE(A4;)", "Err:502" },
1133 { "=NUMBERVALUE(A5;)", "Err:502" },
1134 { "=NUMBERVALUE(A6;\"b\";\"a\")", "1.23" }
1137 nRows = SAL_N_ELEMENTS(aChecks);
1138 for (SCROW i = 0; i < nRows; ++i)
1140 SCROW nRow = 20 + i;
1141 pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
1143 pDoc->CalcAll();
1145 for (SCROW i = 0; i < nRows; ++i)
1147 SCROW nRow = 20 + i;
1148 OUString aResult = pDoc->GetString(0, nRow, 0);
1149 CPPUNIT_ASSERT_EQUAL_MESSAGE(
1150 aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
1154 void testFuncVLOOKUP(ScDocument* pDoc)
1156 // VLOOKUP
1158 // Clear A1:F40.
1159 clearRange(pDoc, ScRange(0, 0, 0, 5, 39, 0));
1161 // Raw data
1162 const char* aData[][2] = {
1163 { "Key", "Val" },
1164 { "10", "3" },
1165 { "20", "4" },
1166 { "30", "5" },
1167 { "40", "6" },
1168 { "50", "7" },
1169 { "60", "8" },
1170 { "70", "9" },
1171 { "B", "10" },
1172 { "B", "11" },
1173 { "C", "12" },
1174 { "D", "13" },
1175 { "E", "14" },
1176 { "F", "15" },
1177 { 0, 0 } // terminator
1180 // Insert raw data into A1:B14.
1181 for (SCROW i = 0; aData[i][0]; ++i)
1183 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
1184 pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
1187 printRange(pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
1189 // Formula data
1190 struct {
1191 const char* pLookup; const char* pFormula; const char* pRes;
1192 } aChecks[] = {
1193 { "Lookup", "Formula", 0 },
1194 { "12", "=VLOOKUP(D2;A2:B14;2;1)", "3" },
1195 { "29", "=VLOOKUP(D3;A2:B14;2;1)", "4" },
1196 { "31", "=VLOOKUP(D4;A2:B14;2;1)", "5" },
1197 { "45", "=VLOOKUP(D5;A2:B14;2;1)", "6" },
1198 { "56", "=VLOOKUP(D6;A2:B14;2;1)", "7" },
1199 { "65", "=VLOOKUP(D7;A2:B14;2;1)", "8" },
1200 { "78", "=VLOOKUP(D8;A2:B14;2;1)", "9" },
1201 { "Andy", "=VLOOKUP(D9;A2:B14;2;1)", "#N/A" },
1202 { "Bruce", "=VLOOKUP(D10;A2:B14;2;1)", "11" },
1203 { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)", "12" },
1204 { "David", "=VLOOKUP(D12;A2:B14;2;1)", "13" },
1205 { "Edward", "=VLOOKUP(D13;A2:B14;2;1)", "14" },
1206 { "Frank", "=VLOOKUP(D14;A2:B14;2;1)", "15" },
1207 { "Henry", "=VLOOKUP(D15;A2:B14;2;1)", "15" },
1208 { "100", "=VLOOKUP(D16;A2:B14;2;1)", "9" },
1209 { "1000", "=VLOOKUP(D17;A2:B14;2;1)", "9" },
1210 { "Zena", "=VLOOKUP(D18;A2:B14;2;1)", "15" }
1213 // Insert formula data into D1:E18.
1214 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1216 pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
1217 pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
1219 pDoc->CalcAll();
1220 printRange(pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
1222 // Verify results.
1223 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1225 if (i == 0)
1226 // Skip the header row.
1227 continue;
1229 OUString aRes = pDoc->GetString(4, i, 0);
1230 bool bGood = aRes.equalsAscii(aChecks[i].pRes);
1231 if (!bGood)
1233 cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
1234 << "' expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
1235 CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
1240 struct NumStrCheck {
1241 double fVal;
1242 const char* pRes;
1245 struct StrStrCheck {
1246 const char* pVal;
1247 const char* pRes;
1250 template<size_t _DataSize, size_t _FormulaSize, int _Type>
1251 void runTestMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
1253 size_t nDataSize = _DataSize;
1254 for (size_t i = 0; i < nDataSize; ++i)
1255 pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1257 for (size_t i = 0; i < _FormulaSize; ++i)
1259 pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
1261 OUStringBuffer aBuf;
1262 aBuf.appendAscii("=MATCH(B");
1263 aBuf.append(static_cast<sal_Int32>(i+1));
1264 aBuf.appendAscii(";A1:A");
1265 aBuf.append(static_cast<sal_Int32>(nDataSize));
1266 aBuf.appendAscii(";");
1267 aBuf.append(static_cast<sal_Int32>(_Type));
1268 aBuf.appendAscii(")");
1269 OUString aFormula = aBuf.makeStringAndClear();
1270 pDoc->SetString(2, i, 0, aFormula);
1273 pDoc->CalcAll();
1274 printRange(pDoc, ScRange(0, 0, 0, 2, _FormulaSize-1, 0), "MATCH");
1276 // verify the results.
1277 for (size_t i = 0; i < _FormulaSize; ++i)
1279 OUString aStr = pDoc->GetString(2, i, 0);
1280 if (!aStr.equalsAscii(aChecks[i].pRes))
1282 cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
1283 << " criterion='" << aChecks[i].pVal << "'" << endl;
1284 CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
1289 void testFuncMATCH(ScDocument* pDoc)
1291 clearRange(pDoc, ScRange(0, 0, 0, 4, 40, 0));
1293 // Ascending in-exact match
1295 // data range (A1:A9)
1296 const char* aData[] = {
1297 "1",
1298 "2",
1299 "3",
1300 "4",
1301 "5",
1302 "6",
1303 "7",
1304 "8",
1305 "9",
1306 "B",
1307 "B",
1308 "C",
1311 // formula (B1:C12)
1312 StrStrCheck aChecks[] = {
1313 { "0.8", "#N/A" },
1314 { "1.2", "1" },
1315 { "2.3", "2" },
1316 { "3.9", "3" },
1317 { "4.1", "4" },
1318 { "5.99", "5" },
1319 { "6.1", "6" },
1320 { "7.2", "7" },
1321 { "8.569", "8" },
1322 { "9.59", "9" },
1323 { "10", "9" },
1324 { "100", "9" },
1325 { "Andy", "#N/A" },
1326 { "Bruce", "11" },
1327 { "Charlie", "12" }
1330 runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(pDoc, aData, aChecks);
1334 // Descending in-exact match
1336 // data range (A1:A9)
1337 const char* aData[] = {
1338 "D",
1339 "C",
1340 "B",
1341 "9",
1342 "8",
1343 "7",
1344 "6",
1345 "5",
1346 "4",
1347 "3",
1348 "2",
1352 // formula (B1:C12)
1353 StrStrCheck aChecks[] = {
1354 { "10", "#N/A" },
1355 { "8.9", "4" },
1356 { "7.8", "5" },
1357 { "6.7", "6" },
1358 { "5.5", "7" },
1359 { "4.6", "8" },
1360 { "3.3", "9" },
1361 { "2.2", "10" },
1362 { "1.1", "11" },
1363 { "0.8", "12" },
1364 { "0", "12" },
1365 { "-2", "12" },
1366 { "Andy", "3" },
1367 { "Bruce", "2" },
1368 { "Charlie", "1" },
1369 { "David", "#N/A" }
1372 runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(pDoc, aData, aChecks);
1376 void testFuncCELL(ScDocument* pDoc)
1378 clearRange(pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
1381 const char* pContent = "Some random text";
1382 pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
1383 double val = 1.2;
1384 pDoc->SetValue(2, 0, 0, val); // Set numeric value to C1;
1386 // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
1387 StrStrCheck aChecks[] = {
1388 { "=CELL(\"COL\";C10)", "3" },
1389 { "=CELL(\"ROW\";C10)", "10" },
1390 { "=CELL(\"SHEET\";C10)", "1" },
1391 { "=CELL(\"ADDRESS\";C10)", "$C$10" },
1392 { "=CELL(\"CONTENTS\";C10)", pContent },
1393 { "=CELL(\"COLOR\";C10)", "0" },
1394 { "=CELL(\"TYPE\";C9)", "b" },
1395 { "=CELL(\"TYPE\";C10)", "l" },
1396 { "=CELL(\"TYPE\";C1)", "v" },
1397 { "=CELL(\"PARENTHESES\";C10)", "0" }
1400 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1401 pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
1402 pDoc->CalcAll();
1404 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1406 OUString aVal = pDoc->GetString(0, i, 0);
1407 CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
1412 /** See also test case document fdo#44456 sheet cpearson */
1413 void testFuncDATEDIF( ScDocument* pDoc )
1415 const char* aData[][5] = {
1416 { "2007-01-01", "2007-01-10", "d", "9", "=DATEDIF(A1;B1;C1)" } ,
1417 { "2007-01-01", "2007-01-31", "m", "0", "=DATEDIF(A2;B2;C2)" } ,
1418 { "2007-01-01", "2007-02-01", "m", "1", "=DATEDIF(A3;B3;C3)" } ,
1419 { "2007-01-01", "2007-02-28", "m", "1", "=DATEDIF(A4;B4;C4)" } ,
1420 { "2007-01-01", "2007-12-31", "d", "364", "=DATEDIF(A5;B5;C5)" } ,
1421 { "2007-01-01", "2007-01-31", "y", "0", "=DATEDIF(A6;B6;C6)" } ,
1422 { "2007-01-01", "2008-07-01", "d", "547", "=DATEDIF(A7;B7;C7)" } ,
1423 { "2007-01-01", "2008-07-01", "m", "18", "=DATEDIF(A8;B8;C8)" } ,
1424 { "2007-01-01", "2008-07-01", "ym", "6", "=DATEDIF(A9;B9;C9)" } ,
1425 { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
1426 { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
1427 { "2007-01-01", "2007-01-31", "md", "30", "=DATEDIF(A12;B12;C12)" } ,
1428 { "2007-02-01", "2009-03-01", "md", "0", "=DATEDIF(A13;B13;C13)" } ,
1429 { "2008-02-01", "2009-03-01", "md", "0", "=DATEDIF(A14;B14;C14)" } ,
1430 { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" } // fail date1 > date2
1433 clearRange( pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
1434 ScAddress aPos(0,0,0);
1435 ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
1436 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
1438 pDoc->CalcAll();
1440 for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
1442 OUString aVal = pDoc->GetString( 4, i, 0);
1443 //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
1444 CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
1448 void testFuncINDIRECT(ScDocument* pDoc)
1450 clearRange(pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
1451 OUString aTabName;
1452 bool bGood = pDoc->GetName(0, aTabName);
1453 CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
1455 OUString aTest = "Test", aRefErr = "#REF!";
1456 pDoc->SetString(0, 10, 0, aTest);
1457 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", pDoc->GetString(0,10,0) == aTest);
1459 OUString aPrefix = "=INDIRECT(\"";
1461 OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
1462 pDoc->SetString(0, 0, 0, aFormula);
1463 aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
1464 pDoc->SetString(0, 1, 0, aFormula);
1465 aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
1466 pDoc->SetString(0, 2, 0, aFormula);
1467 aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
1468 pDoc->SetString(0, 3, 0, aFormula);
1470 pDoc->CalcAll();
1472 // Default is to use the current formula syntax, which is Calc A1.
1473 const OUString* aChecks[] = {
1474 &aTest, &aRefErr, &aRefErr, &aTest
1477 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1479 OUString aVal = pDoc->GetString(0, i, 0);
1480 CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1484 ScCalcConfig aConfig;
1485 aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_OOO;
1486 ScInterpreter::SetGlobalConfig(aConfig);
1487 pDoc->CalcAll();
1489 // Explicit Calc A1 syntax
1490 const OUString* aChecks[] = {
1491 &aTest, &aRefErr, &aRefErr, &aTest
1494 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1496 OUString aVal = pDoc->GetString(0, i, 0);
1497 CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1501 aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_A1;
1502 ScInterpreter::SetGlobalConfig(aConfig);
1503 pDoc->CalcAll();
1505 // Excel A1 syntax
1506 const OUString* aChecks[] = {
1507 &aRefErr, &aTest, &aRefErr, &aTest
1510 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1512 OUString aVal = pDoc->GetString(0, i, 0);
1513 CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1517 aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_R1C1;
1518 ScInterpreter::SetGlobalConfig(aConfig);
1519 pDoc->CalcAll();
1521 // Excel R1C1 syntax
1522 const OUString* aChecks[] = {
1523 &aRefErr, &aRefErr, &aTest, &aTest
1526 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1528 OUString aVal = pDoc->GetString(0, i, 0);
1529 CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1534 void Test::testFormulaHashAndTag()
1536 m_pDoc->InsertTab(0, "Test");
1538 ScAddress aPos1(0,0,0), aPos2(1,0,0);
1540 // Test formula hashing.
1542 struct {
1543 const char* pFormula1; const char* pFormula2; bool bEqual;
1544 } aHashTests[] = {
1545 { "=1", "=2", false }, // different constants
1546 { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
1547 { "=C2*3", "=D2*3", true }, // relative references
1548 { "=C2*3", "=D2*4", false }, // different constants
1549 { "=C2*4", "=D2*4", true }, // relative references
1550 { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
1551 { "=$C3/2", "=$C3/2", true }, // absolute column references
1552 { "=C$3/2", "=D$3/2", true }, // absolute row references
1553 { "=$E$30/2", "=$E$30/2", true }, // absolute references
1554 { "=X20", "=$X$20", false }, // absolute vs relative
1555 { "=X20", "=X$20", false }, // absolute vs relative
1556 { "=X20", "=$X20", false }, // absolute vs relative
1557 { "=X$20", "=$X20", false }, // column absolute vs row absolute
1558 // similar enough for merging ...
1559 { "=A1", "=B1", true },
1560 { "=$A$1", "=$B$1", true },
1561 { "=A1", "=C2", true },
1562 { "=SUM(A1)", "=SUM(B1)", true },
1563 { "=A1+3", "=B1+3", true },
1564 { "=A1+7", "=B1+42", false },
1567 for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
1569 m_pDoc->SetString(aPos1, OUString::createFromAscii(aHashTests[i].pFormula1));
1570 m_pDoc->SetString(aPos2, OUString::createFromAscii(aHashTests[i].pFormula2));
1571 size_t nHashVal1 = m_pDoc->GetFormulaHash(aPos1);
1572 size_t nHashVal2 = m_pDoc->GetFormulaHash(aPos2);
1574 std::ostringstream os;
1575 os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
1576 if (aHashTests[i].bEqual)
1578 os << " Error: these hashes should be equal." << endl;
1579 CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 == nHashVal2);
1581 else
1583 os << " Error: these hashes should differ." << endl;
1584 CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 != nHashVal2);
1587 aPos1.IncRow();
1588 aPos2.IncRow();
1591 // Go back to row 1.
1592 aPos1.SetRow(0);
1593 aPos2.SetRow(0);
1595 // Test formula vectorization state.
1597 struct {
1598 const char* pFormula; ScFormulaVectorState eState;
1599 } aVectorTests[] = {
1600 { "=SUM(1;2;3;4;5)", FormulaVectorEnabled },
1601 { "=NOW()", FormulaVectorDisabled },
1602 { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference },
1603 { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference },
1604 { "=MIN(10;11;22)", FormulaVectorEnabled },
1605 { "=H4", FormulaVectorCheckReference },
1608 for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
1610 m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
1611 ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
1613 if (eState != aVectorTests[i].eState)
1615 std::ostringstream os;
1616 os << "Unexpected vectorization state: expr:" << aVectorTests[i].pFormula;
1617 CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
1619 aPos1.IncRow();
1622 m_pDoc->DeleteTab(0);
1625 void Test::testCellFunctions()
1627 OUString aTabName("foo");
1628 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1629 m_pDoc->InsertTab (0, aTabName));
1631 testFuncSUM(m_pDoc);
1632 testFuncPRODUCT(m_pDoc);
1633 testFuncN(m_pDoc);
1634 testFuncCOUNTIF(m_pDoc);
1635 testFuncIFERROR(m_pDoc);
1636 testFuncNUMBERVALUE(m_pDoc);
1637 testFuncVLOOKUP(m_pDoc);
1638 testFuncMATCH(m_pDoc);
1639 testFuncCELL(m_pDoc);
1640 testFuncDATEDIF(m_pDoc);
1641 testFuncINDIRECT(m_pDoc);
1643 m_pDoc->DeleteTab(0);
1646 void Test::testCopyToDocument()
1648 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "src"));
1650 m_pDoc->SetString(0, 0, 0, "Header");
1651 m_pDoc->SetString(0, 1, 0, "1");
1652 m_pDoc->SetString(0, 2, 0, "2");
1653 m_pDoc->SetString(0, 3, 0, "3");
1654 m_pDoc->SetString(0, 4, 0, "=4/2");
1655 m_pDoc->CalcAll();
1657 // Copy statically to another document.
1659 ScDocument aDestDoc(SCDOCMODE_DOCUMENT);
1660 aDestDoc.InsertTab(0, "src");
1661 m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, &aDestDoc); // Copy A2:A4
1662 m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, &aDestDoc); // Copy A1
1663 m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, &aDestDoc); // Copy A5:A8
1665 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), aDestDoc.GetString(0,0,0));
1666 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), aDestDoc.GetString(0,1,0));
1667 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), aDestDoc.GetString(0,2,0));
1668 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), aDestDoc.GetString(0,3,0));
1669 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), aDestDoc.GetString(0,4,0));
1671 m_pDoc->DeleteTab(0);
1674 void Test::testSheetsFunc()
1676 OUString aTabName1("test1");
1677 OUString aTabName2("test2");
1678 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1679 m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
1681 m_pDoc->SetString(0, 0, 0, OUString("=SHEETS()"));
1682 m_pDoc->CalcFormulaTree(false, false);
1683 double original;
1684 m_pDoc->GetValue(0, 0, 0, original);
1686 CPPUNIT_ASSERT_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
1687 static_cast<SCTAB>(original) == m_pDoc->GetTableCount());
1689 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1690 m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
1692 double modified;
1693 m_pDoc->GetValue(0, 0, 0, modified);
1694 CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
1695 modified - original == 1.0);
1697 SCTAB nTabCount = m_pDoc->GetTableCount();
1698 m_pDoc->DeleteTab(--nTabCount);
1700 m_pDoc->GetValue(0, 0, 0, modified);
1701 CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
1702 modified - original == 0.0);
1704 m_pDoc->DeleteTab(--nTabCount);
1707 void Test::testVolatileFunc()
1709 OUString aTabName("foo");
1710 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1711 m_pDoc->InsertTab (0, aTabName));
1713 double val = 1;
1714 m_pDoc->SetValue(0, 0, 0, val);
1715 m_pDoc->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
1716 double now1;
1717 m_pDoc->GetValue(0, 1, 0, now1);
1718 CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
1720 val = 0;
1721 m_pDoc->SetValue(0, 0, 0, val);
1722 m_pDoc->CalcFormulaTree(false, false);
1723 double zero;
1724 m_pDoc->GetValue(0, 1, 0, zero);
1725 CPPUNIT_ASSERT_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", zero == 0.0);
1727 val = 1;
1728 m_pDoc->SetValue(0, 0, 0, val);
1729 m_pDoc->CalcFormulaTree(false, false);
1730 double now2;
1731 m_pDoc->GetValue(0, 1, 0, now2);
1732 CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
1734 m_pDoc->DeleteTab(0);
1737 void Test::testFormulaDepTracking()
1739 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1741 AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation.
1743 // B2 listens on D2.
1744 m_pDoc->SetString(1, 1, 0, "=D2");
1745 double val = -999.0; // dummy initial value
1746 m_pDoc->GetValue(1, 1, 0, val);
1747 CPPUNIT_ASSERT_MESSAGE("Referencing an empty cell should yield zero.", val == 0.0);
1749 // Changing the value of D2 should trigger recalculation of B2.
1750 m_pDoc->SetValue(3, 1, 0, 1.1);
1751 m_pDoc->GetValue(1, 1, 0, val);
1752 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 1.1);
1754 // And again.
1755 m_pDoc->SetValue(3, 1, 0, 2.2);
1756 m_pDoc->GetValue(1, 1, 0, val);
1757 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 2.2);
1759 clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1761 // Now, let's test the range dependency tracking.
1763 // B2 listens on D2:E6.
1764 m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
1765 m_pDoc->GetValue(1, 1, 0, val);
1766 CPPUNIT_ASSERT_MESSAGE("Summing an empty range should yield zero.", val == 0.0);
1768 // Set value to E3. This should trigger recalc on B2.
1769 m_pDoc->SetValue(4, 2, 0, 2.4);
1770 m_pDoc->GetValue(1, 1, 0, val);
1771 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", val == 2.4);
1773 // Set value to D5 to trigger recalc again. Note that this causes an
1774 // addition of 1.2 + 2.4 which is subject to binary floating point
1775 // rounding error. We need to use approxEqual to assess its value.
1777 m_pDoc->SetValue(3, 4, 0, 1.2);
1778 m_pDoc->GetValue(1, 1, 0, val);
1779 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
1781 // Change the value of D2 (boundary case).
1782 m_pDoc->SetValue(3, 1, 0, 1.0);
1783 m_pDoc->GetValue(1, 1, 0, val);
1784 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
1786 // Change the value of E6 (another boundary case).
1787 m_pDoc->SetValue(4, 5, 0, 2.0);
1788 m_pDoc->GetValue(1, 1, 0, val);
1789 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
1791 // Change the value of D6 (another boundary case).
1792 m_pDoc->SetValue(3, 5, 0, 3.0);
1793 m_pDoc->GetValue(1, 1, 0, val);
1794 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
1796 // Change the value of E2 (another boundary case).
1797 m_pDoc->SetValue(4, 1, 0, 0.4);
1798 m_pDoc->GetValue(1, 1, 0, val);
1799 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
1801 // Change the existing non-empty value cell (E2).
1802 m_pDoc->SetValue(4, 1, 0, 2.4);
1803 m_pDoc->GetValue(1, 1, 0, val);
1804 CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
1806 clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1808 // Now, column-based dependency tracking. We now switch to the R1C1
1809 // syntax which is easier to use for repeated relative references.
1811 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1813 val = 0.0;
1814 for (SCROW nRow = 1; nRow <= 9; ++nRow)
1816 // Static value in column 1.
1817 m_pDoc->SetValue(0, nRow, 0, ++val);
1819 // Formula in column 2 that references cell to the left.
1820 m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
1822 // Formula in column 3 that references cell to the left.
1823 m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
1826 // Check formula values.
1827 val = 0.0;
1828 for (SCROW nRow = 1; nRow <= 9; ++nRow)
1830 ++val;
1831 CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, nRow, 0) == val);
1832 CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, nRow, 0) == val*2.0);
1835 // Intentionally insert a formula in column 1. This will break column 1's
1836 // uniformity of consisting only of static value cells.
1837 m_pDoc->SetString(0, 4, 0, "=R2C3");
1838 CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(0, 4, 0) == 2.0);
1839 CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, 4, 0) == 2.0);
1840 CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, 4, 0) == 4.0);
1842 m_pDoc->DeleteTab(0);
1845 void Test::testFormulaDepTracking2()
1847 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1849 AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation.
1851 double val = 2.0;
1852 m_pDoc->SetValue(0, 0, 0, val);
1853 val = 4.0;
1854 m_pDoc->SetValue(1, 0, 0, val);
1855 val = 5.0;
1856 m_pDoc->SetValue(0, 1, 0, val);
1857 m_pDoc->SetString(2, 0, 0, "=A1/B1");
1858 m_pDoc->SetString(1, 1, 0, "=B1*C1");
1860 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
1862 clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
1864 CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
1866 m_pDoc->DeleteTab(0);
1869 namespace {
1871 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
1873 const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
1874 if (pBC)
1876 cerr << "Broadcaster shouldn't be here." << endl;
1877 return false;
1880 pBC = rDoc.GetBroadcaster(rTo);
1881 if (!pBC)
1883 cerr << "Broadcaster should be here." << endl;
1884 return false;
1886 return true;
1889 ScToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
1891 ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
1892 if (!pFC)
1894 cerr << "Formula cell expected, but not found." << endl;
1895 return NULL;
1898 ScTokenArray* pTokens = pFC->GetCode();
1899 if (!pTokens)
1901 cerr << "Token array is not present." << endl;
1902 return NULL;
1905 ScToken* pToken = static_cast<ScToken*>(pTokens->First());
1906 if (!pToken || pToken->GetType() != formula::svSingleRef)
1908 cerr << "Not a single reference token." << endl;
1909 return NULL;
1912 return pToken;
1915 bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCsCOL nRelCol, SCsROW nRelRow)
1917 ScToken* pToken = getSingleRefToken(rDoc, rPos);
1918 if (!pToken)
1919 return false;
1921 ScSingleRefData& rRef = pToken->GetSingleRef();
1922 if (!rRef.IsColRel() || rRef.nRelCol != nRelCol)
1924 cerr << "Unexpected relative column address." << endl;
1925 return false;
1928 if (!rRef.IsRowRel() || rRef.nRelRow != nRelRow)
1930 cerr << "Unexpected relative row address." << endl;
1931 return false;
1934 return true;
1937 bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
1939 ScToken* pToken = getSingleRefToken(rDoc, rPos);
1940 if (!pToken)
1941 return false;
1943 ScSingleRefData& rRef = pToken->GetSingleRef();
1944 if (!rRef.IsDeleted())
1946 cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
1947 return false;
1950 return true;
1955 void Test::testCellBroadcaster()
1957 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1959 AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation.
1960 m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
1961 double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
1962 CPPUNIT_ASSERT_EQUAL(0.0, val);
1964 const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
1965 CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
1967 // Change the value of A1 and make sure that B1 follows.
1968 m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
1969 val = m_pDoc->GetValue(ScAddress(1,0,0));
1970 CPPUNIT_ASSERT_EQUAL(1.23, val);
1972 // Move column A down 5 cells. Make sure B1 now references A6, not A1.
1973 m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
1974 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1975 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
1977 // Make sure the broadcaster has also moved.
1978 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1979 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
1981 // Set new value to A6 and make sure B1 gets updated.
1982 m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
1983 val = m_pDoc->GetValue(ScAddress(1,0,0));
1984 CPPUNIT_ASSERT_EQUAL(45.6, val);
1986 // Move column A up 3 cells, and make sure B1 now references A3, not A6.
1987 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
1988 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1989 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
1991 // The broadcaster should also have been relocated from A6 to A3.
1992 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
1993 broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
1995 // Insert cells over A1:A10 and shift cells to right.
1996 m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
1997 CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
1998 checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
1999 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2000 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
2002 // Delete formula in C2, which should remove the broadcaster in B3.
2003 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
2004 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
2005 clearRange(m_pDoc, ScAddress(2,0,0));
2006 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
2007 pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
2008 CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
2010 // Clear everything and start over.
2011 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2013 m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
2014 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
2015 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
2017 // While column A is still empty, move column A down 2 cells. This should
2018 // move the broadcaster from A1 to A3.
2019 m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
2020 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2021 broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
2023 // Move it back while column A is still empty.
2024 m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
2025 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2026 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
2028 // Clear everything again
2029 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2031 // B1:B3 depends on A1:A3
2032 m_pDoc->SetString(ScAddress(1,0,0), "=A1");
2033 m_pDoc->SetString(ScAddress(1,1,0), "=A2");
2034 m_pDoc->SetString(ScAddress(1,2,0), "=A3");
2035 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
2036 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
2037 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
2038 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
2039 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
2040 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
2041 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
2042 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
2043 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
2045 // Insert Rows at row 2, down 5 rows.
2046 m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
2047 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
2048 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
2049 checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
2051 // Broadcasters in A2 and A3 should shift down by 5 rows.
2052 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2053 broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
2054 CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2055 broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
2057 // B2 and B3 should reference shifted cells.
2058 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
2059 checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
2060 CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
2061 checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
2063 // Delete cells with broadcasters.
2064 m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
2065 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
2066 CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
2068 // References in B2 and B3 should be invalid.
2069 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
2070 checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
2071 CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
2072 checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
2074 // Clear everything again
2075 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2078 // Switch to R1C1 to make it easier to input relative references in multiple cells.
2079 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
2081 // Have B1:B20 reference A1:A20.
2082 val = 0.0;
2083 for (SCROW i = 0; i < 20; ++i)
2085 m_pDoc->SetValue(ScAddress(0,i,0), val++);
2086 m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
2090 // Ensure that the formula cells show correct values, and the referenced
2091 // cells have broadcasters.
2092 val = 0.0;
2093 for (SCROW i = 0; i < 20; ++i)
2095 CPPUNIT_ASSERT_EQUAL(val++, m_pDoc->GetValue(ScAddress(1,i,0)));
2096 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
2097 CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
2100 // Delete formula cells in B2:B19.
2101 clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
2102 // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
2103 CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
2104 CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
2105 for (SCROW i = 1; i <= 18; ++i)
2107 pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
2108 CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
2111 // Clear everything again
2112 clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2114 m_pDoc->SetValue(ScAddress(0,0,0), 2.0);
2115 m_pDoc->SetString(ScAddress(1,0,0), "=A1");
2116 m_pDoc->SetString(ScAddress(2,0,0), "=B1");
2117 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,0,0));
2118 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,0,0));
2119 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(2,0,0));
2121 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
2122 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
2123 pBC = m_pDoc->GetBroadcaster(ScAddress(1,0,0));
2124 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
2126 // Change the value of A1 and make sure everyone follows suit.
2127 m_pDoc->SetValue(ScAddress(0,0,0), 3.5);
2128 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(0,0,0));
2129 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(1,0,0));
2130 CPPUNIT_ASSERT_EQUAL(3.5, m_pDoc->GetValue(2,0,0));
2132 // Insert a column at column B.
2133 m_pDoc->InsertCol(ScRange(1,0,0,1,MAXROW,0));
2134 pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
2135 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
2136 pBC = m_pDoc->GetBroadcaster(ScAddress(2,0,0));
2137 CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist here.", pBC);
2139 // Change the value of A1 again.
2140 m_pDoc->SetValue(ScAddress(0,0,0), 5.5);
2141 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(0,0,0));
2142 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(2,0,0));
2143 CPPUNIT_ASSERT_EQUAL(5.5, m_pDoc->GetValue(3,0,0));
2145 m_pDoc->DeleteTab(0);
2148 void Test::testFuncParam()
2150 OUString aTabName("foo");
2151 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2152 m_pDoc->InsertTab (0, aTabName));
2154 // First, the normal case, with no missing parameters.
2155 m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;2;3)"));
2156 m_pDoc->CalcFormulaTree(false, false);
2157 double val;
2158 m_pDoc->GetValue(0, 0, 0, val);
2159 CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 2);
2161 // Now function with missing parameters. Missing values should be treated
2162 // as zeros.
2163 m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;;;)"));
2164 m_pDoc->CalcFormulaTree(false, false);
2165 m_pDoc->GetValue(0, 0, 0, val);
2166 CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 0.25);
2168 // Conversion of string to numeric argument.
2169 m_pDoc->SetString(0, 0, 0, OUString("=\"\"+3")); // empty string
2170 m_pDoc->SetString(0, 1, 0, OUString("=\" \"+3")); // only blank
2171 m_pDoc->SetString(0, 2, 0, OUString("=\" 4 \"+3")); // number in blanks
2172 m_pDoc->SetString(0, 3, 0, OUString("=\" x \"+3")); // non-numeric => #VALUE! error
2174 OUString aVal;
2175 ScCalcConfig aConfig;
2177 // With "Empty string as zero" option.
2178 aConfig.mbEmptyStringAsZero = true;
2179 ScInterpreter::SetGlobalConfig(aConfig);
2180 m_pDoc->CalcAll();
2181 m_pDoc->GetValue(0, 0, 0, val);
2182 CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
2183 m_pDoc->GetValue(0, 1, 0, val);
2184 CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
2185 m_pDoc->GetValue(0, 2, 0, val);
2186 CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
2187 aVal = m_pDoc->GetString( 0, 3, 0);
2188 CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2190 // Without "Empty string as zero" option.
2191 aConfig.mbEmptyStringAsZero = false;
2192 ScInterpreter::SetGlobalConfig(aConfig);
2193 m_pDoc->CalcAll();
2194 aVal = m_pDoc->GetString( 0, 0, 0);
2195 CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2196 aVal = m_pDoc->GetString( 0, 1, 0);
2197 CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2198 m_pDoc->GetValue(0, 2, 0, val);
2199 CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
2200 aVal = m_pDoc->GetString( 0, 3, 0);
2201 CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2203 m_pDoc->DeleteTab(0);
2206 void Test::testNamedRange()
2208 struct {
2209 const char* pName; const char* pExpr; sal_uInt16 nIndex;
2210 } aNames[] = {
2211 { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
2212 { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
2213 { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
2214 { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
2217 CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "Sheet1"));
2219 m_pDoc->SetValue (0, 0, 0, 101);
2221 ScAddress aA1(0, 0, 0);
2222 ScRangeName* pNewRanges = new ScRangeName();
2223 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
2225 ScRangeData* pNew = new ScRangeData(
2226 m_pDoc,
2227 OUString::createFromAscii(aNames[i].pName),
2228 OUString::createFromAscii(aNames[i].pExpr),
2229 aA1, 0, formula::FormulaGrammar::GRAM_ENGLISH);
2230 pNew->SetIndex(aNames[i].nIndex);
2231 bool bSuccess = pNewRanges->insert(pNew);
2232 CPPUNIT_ASSERT_MESSAGE ("insertion failed", bSuccess);
2235 // Make sure the index lookup does the right thing.
2236 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
2238 const ScRangeData* p = pNewRanges->findByIndex(aNames[i].nIndex);
2239 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
2240 OUString aName = p->GetName();
2241 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].pName));
2244 // Test usage in formula expression.
2245 m_pDoc->SetRangeName(pNewRanges);
2246 m_pDoc->SetString (1, 0, 0, OUString("=A1/Divisor"));
2247 m_pDoc->CalcAll();
2249 double result;
2250 m_pDoc->GetValue (1, 0, 0, result);
2251 CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 1.0);
2253 // Test copy-ability of range names.
2254 ScRangeName* pCopiedRanges = new ScRangeName(*pNewRanges);
2255 m_pDoc->SetRangeName(pCopiedRanges);
2256 // Make sure the index lookup still works.
2257 for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
2259 const ScRangeData* p = pCopiedRanges->findByIndex(aNames[i].nIndex);
2260 CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
2261 OUString aName = p->GetName();
2262 CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].pName));
2265 m_pDoc->SetRangeName(NULL); // Delete the names.
2266 m_pDoc->DeleteTab(0);
2269 void Test::testCSV()
2271 const int English = 0, European = 1;
2272 struct {
2273 const char *pStr; int eSep; bool bResult; double nValue;
2274 } aTests[] = {
2275 { "foo", English, false, 0.0 },
2276 { "1.0", English, true, 1.0 },
2277 { "1,0", English, false, 0.0 },
2278 { "1.0", European, false, 0.0 },
2279 { "1.000", European, true, 1000.0 },
2280 { "1,000", European, true, 1.0 },
2281 { "1.000", English, true, 1.0 },
2282 { "1,000", English, true, 1000.0 },
2283 { " 1.0", English, true, 1.0 },
2284 { " 1.0 ", English, true, 1.0 },
2285 { "1.0 ", European, false, 0.0 },
2286 { "1.000", European, true, 1000.0 },
2287 { "1137.999", English, true, 1137.999 },
2288 { "1.000.00", European, false, 0.0 }
2290 for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
2291 OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
2292 double nValue = 0.0;
2293 bool bResult = ScStringUtil::parseSimpleNumber
2294 (aStr, aTests[i].eSep == English ? '.' : ',',
2295 aTests[i].eSep == English ? ',' : '.',
2296 nValue);
2297 CPPUNIT_ASSERT_MESSAGE ("CSV numeric detection failure", bResult == aTests[i].bResult);
2298 CPPUNIT_ASSERT_MESSAGE ("CSV numeric value failure", nValue == aTests[i].nValue);
2302 template<typename Evaluator>
2303 void checkMatrixElements(const ScMatrix& rMat)
2305 SCSIZE nC, nR;
2306 rMat.GetDimensions(nC, nR);
2307 Evaluator aEval;
2308 for (SCSIZE i = 0; i < nC; ++i)
2310 for (SCSIZE j = 0; j < nR; ++j)
2312 aEval(i, j, rMat.Get(i, j));
2317 struct AllZeroMatrix
2319 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2321 CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
2322 CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
2326 struct PartiallyFilledZeroMatrix
2328 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2330 CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
2331 if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
2333 CPPUNIT_ASSERT_MESSAGE("element value must be 3.0", rVal.fVal == 3.0);
2335 else
2337 CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
2342 struct AllEmptyMatrix
2344 void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2346 CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
2347 CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
2351 struct PartiallyFilledEmptyMatrix
2353 void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2355 if (nCol == 1 && nRow == 1)
2357 CPPUNIT_ASSERT_MESSAGE("element is not of boolean type", rVal.nType == SC_MATVAL_BOOLEAN);
2358 CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == 1.0);
2360 else if (nCol == 4 && nRow == 5)
2362 CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_VALUE);
2363 CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == -12.5);
2365 else if (nCol == 8 && nRow == 2)
2367 CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING);
2368 CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr == "Test");
2370 else if (nCol == 8 && nRow == 11)
2372 CPPUNIT_ASSERT_MESSAGE("element is not of empty path type", rVal.nType == SC_MATVAL_EMPTYPATH);
2373 CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
2375 else
2377 CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
2378 CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
2383 void Test::testMatrix()
2385 ScMatrixRef pMat;
2387 // First, test the zero matrix type.
2388 pMat = new ScMatrix(0, 0, 0.0);
2389 SCSIZE nC, nR;
2390 pMat->GetDimensions(nC, nR);
2391 CPPUNIT_ASSERT_MESSAGE("matrix is not empty", nC == 0 && nR == 0);
2392 pMat->Resize(4, 10, 0.0);
2393 pMat->GetDimensions(nC, nR);
2394 CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 4 && nR == 10);
2395 CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2396 !pMat->And() && !pMat->Or());
2398 // Resizing into a larger matrix should fill the void space with zeros.
2399 checkMatrixElements<AllZeroMatrix>(*pMat);
2401 pMat->FillDouble(3.0, 1, 2, 2, 8);
2402 checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
2403 CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
2404 CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2405 !pMat->And() && pMat->Or());
2406 pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
2407 CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2408 pMat->And() && pMat->Or());
2410 // Test the AND and OR evaluations.
2411 pMat = new ScMatrix(2, 2, 0.0);
2413 // Only some of the elements are non-zero.
2414 pMat->PutBoolean(true, 0, 0);
2415 pMat->PutDouble(1.0, 1, 1);
2416 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2417 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
2419 // All of the elements are non-zero.
2420 pMat->PutBoolean(true, 0, 1);
2421 pMat->PutDouble(2.3, 1, 0);
2422 CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2423 CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
2425 // Now test the emtpy matrix type.
2426 pMat = new ScMatrix(10, 20);
2427 pMat->GetDimensions(nC, nR);
2428 CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 10 && nR == 20);
2429 checkMatrixElements<AllEmptyMatrix>(*pMat);
2431 pMat->PutBoolean(true, 1, 1);
2432 pMat->PutDouble(-12.5, 4, 5);
2433 OUString aStr("Test");
2434 pMat->PutString(aStr, 8, 2);
2435 pMat->PutEmptyPath(8, 11);
2436 checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
2438 // Test resizing.
2439 pMat = new ScMatrix(0, 0);
2440 pMat->Resize(2, 2, 1.5);
2441 pMat->PutEmpty(1, 1);
2443 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
2444 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
2445 CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
2446 CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
2449 void Test::testEnterMixedMatrix()
2451 m_pDoc->InsertTab(0, "foo");
2453 // Insert the source values in A1:B2.
2454 m_pDoc->SetString(0, 0, 0, "A");
2455 m_pDoc->SetString(1, 0, 0, "B");
2456 double val = 1.0;
2457 m_pDoc->SetValue(0, 1, 0, val);
2458 val = 2.0;
2459 m_pDoc->SetValue(1, 1, 0, val);
2461 // Create a matrix range in A4:B5 referencing A1:B2.
2462 ScMarkData aMark;
2463 aMark.SelectOneTable(0);
2464 m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, "=A1:B2", NULL);
2466 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
2467 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
2468 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
2469 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
2471 m_pDoc->DeleteTab(0);
2474 namespace {
2476 struct DPFieldDef
2478 const char* pName;
2479 sheet::DataPilotFieldOrientation eOrient;
2482 * Function for data field. It's used only for data field. When 0, the
2483 * default function (SUM) is used.
2485 int eFunc;
2488 template<size_t _Size>
2489 ScRange insertDPSourceData(ScDocument* pDoc, DPFieldDef aFields[], size_t nFieldCount, const char* aData[][_Size], size_t nDataCount)
2491 // Insert field names in row 0.
2492 for (size_t i = 0; i < nFieldCount; ++i)
2493 pDoc->SetString(static_cast<SCCOL>(i), 0, 0, OUString(aFields[i].pName, strlen(aFields[i].pName), RTL_TEXTENCODING_UTF8));
2495 // Insert data into row 1 and downward.
2496 for (size_t i = 0; i < nDataCount; ++i)
2498 SCROW nRow = static_cast<SCROW>(i) + 1;
2499 for (size_t j = 0; j < nFieldCount; ++j)
2501 SCCOL nCol = static_cast<SCCOL>(j);
2502 pDoc->SetString(
2503 nCol, nRow, 0, OUString(aData[i][j], strlen(aData[i][j]), RTL_TEXTENCODING_UTF8));
2507 SCROW nRow1 = 0, nRow2 = 0;
2508 SCCOL nCol1 = 0, nCol2 = 0;
2509 pDoc->GetDataArea(0, nCol1, nRow1, nCol2, nRow2, true, false);
2510 CPPUNIT_ASSERT_MESSAGE("Data is expected to start from (col=0,row=0).", nCol1 == 0 && nRow1 == 0);
2511 CPPUNIT_ASSERT_MESSAGE("Unexpected data range.",
2512 nCol2 == static_cast<SCCOL>(nFieldCount - 1) && nRow2 == static_cast<SCROW>(nDataCount));
2514 ScRange aSrcRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
2515 printRange(pDoc, aSrcRange, "Data sheet content");
2516 return aSrcRange;
2519 template<size_t _Size>
2520 bool checkDPTableOutput(ScDocument* pDoc, const ScRange& aOutRange, const char* aOutputCheck[][_Size], const char* pCaption)
2522 bool bResult = true;
2523 const ScAddress& s = aOutRange.aStart;
2524 const ScAddress& e = aOutRange.aEnd;
2525 SheetPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1);
2526 SCROW nOutRowSize = e.Row() - s.Row() + 1;
2527 SCCOL nOutColSize = e.Col() - s.Col() + 1;
2528 for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
2530 for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
2532 OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
2533 printer.set(nRow, nCol, aVal);
2534 const char* p = aOutputCheck[nRow][nCol];
2535 if (p)
2537 OUString aCheckVal = OUString::createFromAscii(p);
2538 bool bEqual = aCheckVal.equals(aVal);
2539 if (!bEqual)
2541 cout << "Expected: " << aCheckVal << " Actual: " << aVal << endl;
2542 bResult = false;
2545 else if (!aVal.isEmpty())
2547 cout << "Empty cell expected" << endl;
2548 bResult = false;
2552 printer.print(pCaption);
2553 return bResult;
2556 ScDPObject* createDPFromSourceDesc(
2557 ScDocument* pDoc, const ScSheetSourceDesc& rDesc, DPFieldDef aFields[], size_t nFieldCount,
2558 bool bFilterButton)
2560 ScDPObject* pDPObj = new ScDPObject(pDoc);
2561 pDPObj->SetSheetDesc(rDesc);
2562 pDPObj->SetOutRange(ScAddress(0, 0, 1));
2564 ScDPSaveData aSaveData;
2565 // Set data pilot table output options.
2566 aSaveData.SetIgnoreEmptyRows(false);
2567 aSaveData.SetRepeatIfEmpty(false);
2568 aSaveData.SetColumnGrand(true);
2569 aSaveData.SetRowGrand(true);
2570 aSaveData.SetFilterButton(bFilterButton);
2571 aSaveData.SetDrillDown(true);
2573 // Check the sanity of the source range.
2574 const ScRange& rSrcRange = rDesc.GetSourceRange();
2575 SCROW nRow1 = rSrcRange.aStart.Row();
2576 SCROW nRow2 = rSrcRange.aEnd.Row();
2577 CPPUNIT_ASSERT_MESSAGE("source range contains no data!", nRow2 - nRow1 > 1);
2579 // Set the dimension information.
2580 for (size_t i = 0; i < nFieldCount; ++i)
2582 OUString aDimName = OUString::createFromAscii(aFields[i].pName);
2583 ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(aDimName);
2584 pDim->SetOrientation(static_cast<sal_uInt16>(aFields[i].eOrient));
2585 pDim->SetUsedHierarchy(0);
2587 if (aFields[i].eOrient == sheet::DataPilotFieldOrientation_DATA)
2589 sheet::GeneralFunction eFunc = sheet::GeneralFunction_SUM;
2590 if (aFields[i].eFunc)
2591 eFunc = static_cast<sheet::GeneralFunction>(aFields[i].eFunc);
2593 pDim->SetFunction(eFunc);
2594 pDim->SetReferenceValue(NULL);
2596 else
2598 sheet::DataPilotFieldSortInfo aSortInfo;
2599 aSortInfo.IsAscending = true;
2600 aSortInfo.Mode = 2;
2601 pDim->SetSortInfo(&aSortInfo);
2603 sheet::DataPilotFieldLayoutInfo aLayInfo;
2604 aLayInfo.LayoutMode = 0;
2605 aLayInfo.AddEmptyLines = false;
2606 pDim->SetLayoutInfo(&aLayInfo);
2607 sheet::DataPilotFieldAutoShowInfo aShowInfo;
2608 aShowInfo.IsEnabled = false;
2609 aShowInfo.ShowItemsMode = 0;
2610 aShowInfo.ItemCount = 0;
2611 pDim->SetAutoShowInfo(&aShowInfo);
2615 // Don't forget the data layout dimension.
2616 ScDPSaveDimension* pDim = aSaveData.GetDataLayoutDimension();
2617 pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
2618 pDim->SetShowEmpty(true);
2620 pDPObj->SetSaveData(aSaveData);
2621 pDPObj->InvalidateData();
2623 return pDPObj;
2626 ScDPObject* createDPFromRange(
2627 ScDocument* pDoc, const ScRange& rRange, DPFieldDef aFields[], size_t nFieldCount,
2628 bool bFilterButton)
2630 ScSheetSourceDesc aSheetDesc(pDoc);
2631 aSheetDesc.SetSourceRange(rRange);
2632 return createDPFromSourceDesc(pDoc, aSheetDesc, aFields, nFieldCount, bFilterButton);
2635 ScRange refresh(ScDPObject* pDPObj)
2637 bool bOverFlow = false;
2638 ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
2639 CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
2641 pDPObj->Output(aOutRange.aStart);
2642 aOutRange = pDPObj->GetOutRange();
2643 return aOutRange;
2646 ScRange refreshGroups(ScDPCollection* pDPs, ScDPObject* pDPObj)
2648 // We need to first create group data in the cache, then the group data in
2649 // the object.
2650 std::set<ScDPObject*> aRefs;
2651 bool bSuccess = pDPs->ReloadGroupsInCache(pDPObj, aRefs);
2652 CPPUNIT_ASSERT_MESSAGE("Failed to reload group data in cache.", bSuccess);
2653 CPPUNIT_ASSERT_MESSAGE("There should be only one table linked to this cache.", aRefs.size() == 1);
2654 pDPObj->ReloadGroupTableData();
2656 return refresh(pDPObj);
2661 void Test::testPivotTable()
2663 m_pDoc->InsertTab(0, OUString("Data"));
2664 m_pDoc->InsertTab(1, OUString("Table"));
2666 // Dimension definition
2667 DPFieldDef aFields[] = {
2668 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
2669 { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
2670 { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
2673 // Raw data
2674 const char* aData[][3] = {
2675 { "Andy", "A", "30" },
2676 { "Bruce", "A", "20" },
2677 { "Charlie", "B", "45" },
2678 { "David", "B", "12" },
2679 { "Edward", "C", "8" },
2680 { "Frank", "C", "15" },
2683 size_t nFieldCount = SAL_N_ELEMENTS(aFields);
2684 size_t nDataCount = SAL_N_ELEMENTS(aData);
2686 ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
2687 SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
2688 SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
2690 ScDPObject* pDPObj = createDPFromRange(
2691 m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2693 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2694 bool bSuccess = pDPs->InsertNewTable(pDPObj);
2695 CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2696 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2697 pDPs->GetCount() == 1);
2698 pDPObj->SetName(pDPs->CreateNewName());
2700 bool bOverFlow = false;
2701 ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
2702 CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
2704 pDPObj->Output(aOutRange.aStart);
2705 aOutRange = pDPObj->GetOutRange();
2707 // Expected output table content. 0 = empty cell
2708 const char* aOutputCheck[][5] = {
2709 { "Sum - Score", "Group", 0, 0, 0 },
2710 { "Name", "A", "B", "C", "Total Result" },
2711 { "Andy", "30", 0, 0, "30" },
2712 { "Bruce", "20", 0, 0, "20" },
2713 { "Charlie", 0, "45", 0, "45" },
2714 { "David", 0, "12", 0, "12" },
2715 { "Edward", 0, 0, "8", "8" },
2716 { "Frank", 0, 0, "15", "15" },
2717 { "Total Result", "50", "57", "23", "130" }
2720 bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2721 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2723 CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
2725 // Update the cell values.
2726 double aData2[] = { 100, 200, 300, 400, 500, 600 };
2727 for (size_t i = 0; i < SAL_N_ELEMENTS(aData2); ++i)
2729 SCROW nRow = i + 1;
2730 m_pDoc->SetValue(2, nRow, 0, aData2[i]);
2733 printRange(m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), "Data sheet content (modified)");
2735 // Now, create a copy of the datapilot object for the updated table, but
2736 // don't reload the cache which should force the copy to use the old data
2737 // from the cache.
2738 ScDPObject* pDPObj2 = new ScDPObject(*pDPObj);
2739 pDPs->InsertNewTable(pDPObj2);
2741 aOutRange = pDPObj2->GetOutRange();
2742 pDPObj2->ClearTableData();
2743 pDPObj2->Output(aOutRange.aStart);
2745 // Expected output table content. 0 = empty cell
2746 const char* aOutputCheck[][5] = {
2747 { "Sum - Score", "Group", 0, 0, 0 },
2748 { "Name", "A", "B", "C", "Total Result" },
2749 { "Andy", "30", 0, 0, "30" },
2750 { "Bruce", "20", 0, 0, "20" },
2751 { "Charlie", 0, "45", 0, "45" },
2752 { "David", 0, "12", 0, "12" },
2753 { "Edward", 0, 0, "8", "8" },
2754 { "Frank", 0, 0, "15", "15" },
2755 { "Total Result", "50", "57", "23", "130" }
2758 bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (from old cache)");
2759 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2762 CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
2764 // Free the first datapilot object after the 2nd one gets reloaded, to
2765 // prevent the data cache from being deleted before the reload.
2766 pDPs->FreeTable(pDPObj);
2768 CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
2770 // This time clear the cache to refresh the data from the source range.
2771 CPPUNIT_ASSERT_MESSAGE("This datapilot should be based on sheet data.", pDPObj2->IsSheetData());
2772 std::set<ScDPObject*> aRefs;
2773 sal_uLong nErrId = pDPs->ReloadCache(pDPObj2, aRefs);
2774 CPPUNIT_ASSERT_MESSAGE("Cache reload failed.", nErrId == 0);
2775 CPPUNIT_ASSERT_MESSAGE("Reloading a cache shouldn't remove any cache.",
2776 pDPs->GetSheetCaches().size() == 1);
2778 pDPObj2->ClearTableData();
2779 pDPObj2->Output(aOutRange.aStart);
2782 // Expected output table content. 0 = empty cell
2783 const char* aOutputCheck[][5] = {
2784 { "Sum - Score", "Group", 0, 0, 0 },
2785 { "Name", "A", "B", "C", "Total Result" },
2786 { "Andy", "100", 0, 0, "100" },
2787 { "Bruce", "200", 0, 0, "200" },
2788 { "Charlie", 0, "300", 0, "300" },
2789 { "David", 0, "400", 0, "400" },
2790 { "Edward", 0, 0, "500", "500" },
2791 { "Frank", 0, 0, "600", "600" },
2792 { "Total Result", "300", "700", "1100", "2100" }
2795 bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (refreshed)");
2796 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2799 CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
2801 // Swap the two sheets.
2802 m_pDoc->MoveTab(1, 0);
2803 CPPUNIT_ASSERT_MESSAGE("Swapping the sheets shouldn't remove the cache.",
2804 pDPs->GetSheetCaches().size() == 1);
2805 CPPUNIT_ASSERT_MESSAGE("Cache should have moved.", !pDPs->GetSheetCaches().hasCache(aSrcRange));
2806 aSrcRange.aStart.SetTab(1);
2807 aSrcRange.aEnd.SetTab(1);
2808 CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
2810 pDPs->FreeTable(pDPObj2);
2811 CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
2812 pDPs->GetCount() == 0);
2814 CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more data cache.",
2815 pDPs->GetSheetCaches().size() == 0);
2817 // Insert a brand new pivot table object once again, but this time, don't
2818 // create the output to avoid creating a data cache.
2819 m_pDoc->DeleteTab(1);
2820 m_pDoc->InsertTab(1, OUString("Table"));
2822 pDPObj = createDPFromRange(
2823 m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2824 bSuccess = pDPs->InsertNewTable(pDPObj);
2825 CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2826 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2827 pDPs->GetCount() == 1);
2828 pDPObj->SetName(pDPs->CreateNewName());
2829 CPPUNIT_ASSERT_MESSAGE("Data cache shouldn't exist yet before creating the table output.",
2830 pDPs->GetSheetCaches().size() == 0);
2832 // Now, "refresh" the table. This should still return a reference to self
2833 // even with the absence of data cache.
2834 aRefs.clear();
2835 pDPs->ReloadCache(pDPObj, aRefs);
2836 CPPUNIT_ASSERT_MESSAGE("It should return the same object as a reference.",
2837 aRefs.size() == 1 && *aRefs.begin() == pDPObj);
2839 pDPs->FreeTable(pDPObj);
2841 m_pDoc->DeleteTab(1);
2842 m_pDoc->DeleteTab(0);
2845 void Test::testPivotTableLabels()
2847 m_pDoc->InsertTab(0, OUString("Data"));
2848 m_pDoc->InsertTab(1, OUString("Table"));
2850 // Dimension definition
2851 DPFieldDef aFields[] = {
2852 { "Software", sheet::DataPilotFieldOrientation_ROW, 0 },
2853 { "Version", sheet::DataPilotFieldOrientation_COLUMN, 0 },
2854 { "1.2.3", sheet::DataPilotFieldOrientation_DATA, 0 }
2857 // Raw data
2858 const char* aData[][3] = {
2859 { "LibreOffice", "3.3.0", "30" },
2860 { "LibreOffice", "3.3.1", "20" },
2861 { "LibreOffice", "3.4.0", "45" },
2864 size_t nFieldCount = SAL_N_ELEMENTS(aFields);
2865 size_t nDataCount = SAL_N_ELEMENTS(aData);
2867 ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
2868 SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
2869 SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
2871 ScDPObject* pDPObj = createDPFromRange(
2872 m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2874 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2875 bool bSuccess = pDPs->InsertNewTable(pDPObj);
2876 CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2877 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2878 pDPs->GetCount() == 1);
2879 pDPObj->SetName(pDPs->CreateNewName());
2881 ScRange aOutRange = refresh(pDPObj);
2883 // Expected output table content. 0 = empty cell
2884 const char* aOutputCheck[][5] = {
2885 { "Sum - 1.2.3", "Version", 0, 0, 0 },
2886 { "Software", "3.3.0", "3.3.1", "3.4.0", "Total Result" },
2887 { "LibreOffice", "30", "20", "45", "95" },
2888 { "Total Result", "30", "20", "45", "95" }
2891 bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2892 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2895 pDPs->FreeTable(pDPObj);
2897 m_pDoc->DeleteTab(1);
2898 m_pDoc->DeleteTab(0);
2901 void Test::testPivotTableDateLabels()
2903 m_pDoc->InsertTab(0, OUString("Data"));
2904 m_pDoc->InsertTab(1, OUString("Table"));
2906 // Dimension definition
2907 DPFieldDef aFields[] = {
2908 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
2909 { "Date", sheet::DataPilotFieldOrientation_COLUMN, 0 },
2910 { "Value", sheet::DataPilotFieldOrientation_DATA, 0 }
2913 // Raw data
2914 const char* aData[][3] = {
2915 { "Zena", "2011-1-1", "30" },
2916 { "Yodel", "2011-1-2", "20" },
2917 { "Xavior", "2011-1-3", "45" }
2920 size_t nFieldCount = SAL_N_ELEMENTS(aFields);
2921 size_t nDataCount = SAL_N_ELEMENTS(aData);
2923 ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
2924 SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
2925 SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
2927 ScDPObject* pDPObj = createDPFromRange(
2928 m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2930 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2931 bool bSuccess = pDPs->InsertNewTable(pDPObj);
2932 CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2933 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2934 pDPs->GetCount() == 1);
2935 pDPObj->SetName(pDPs->CreateNewName());
2937 ScRange aOutRange = refresh(pDPObj);
2939 // Expected output table content. 0 = empty cell
2940 const char* aOutputCheck[][5] = {
2941 { "Sum - Value", "Date", 0, 0, 0 },
2942 { "Name", "2011-01-01", "2011-01-02", "2011-01-03", "Total Result" },
2943 { "Xavior", 0, 0, "45", "45" },
2944 { "Yodel", 0, "20", 0, "20" },
2945 { "Zena", "30", 0, 0, "30" },
2946 { "Total Result", "30", "20", "45", "95" }
2949 bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2950 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2954 const char* aChecks[] = {
2955 "2011-01-01", "2011-01-02", "2011-01-03"
2958 // Make sure those cells that contain dates are numeric.
2959 SCROW nRow = aOutRange.aStart.Row() + 1;
2960 nCol1 = aOutRange.aStart.Col() + 1;
2961 nCol2 = nCol1 + 2;
2962 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2964 OUString aVal = m_pDoc->GetString(nCol, nRow, 1);
2965 CPPUNIT_ASSERT_MESSAGE("Cell value is not as expected.", aVal.equalsAscii(aChecks[nCol-nCol1]));
2966 CPPUNIT_ASSERT_MESSAGE("This cell contains a date value and is supposed to be numeric.",
2967 m_pDoc->HasValueData(nCol, nRow, 1));
2971 pDPs->FreeTable(pDPObj);
2973 m_pDoc->DeleteTab(1);
2974 m_pDoc->DeleteTab(0);
2977 void Test::testPivotTableFilters()
2979 m_pDoc->InsertTab(0, OUString("Data"));
2980 m_pDoc->InsertTab(1, OUString("Table"));
2982 // Dimension definition
2983 DPFieldDef aFields[] = {
2984 { "Name", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
2985 { "Group1", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
2986 { "Group2", sheet::DataPilotFieldOrientation_PAGE, 0 },
2987 { "Val1", sheet::DataPilotFieldOrientation_DATA, 0 },
2988 { "Val2", sheet::DataPilotFieldOrientation_DATA, 0 }
2991 // Raw data
2992 const char* aData[][5] = {
2993 { "A", "1", "A", "1", "10" },
2994 { "B", "1", "A", "1", "10" },
2995 { "C", "1", "B", "1", "10" },
2996 { "D", "1", "B", "1", "10" },
2997 { "E", "2", "A", "1", "10" },
2998 { "F", "2", "A", "1", "10" },
2999 { "G", "2", "B", "1", "10" },
3000 { "H", "2", "B", "1", "10" }
3003 size_t nFieldCount = SAL_N_ELEMENTS(aFields);
3004 size_t nDataCount = SAL_N_ELEMENTS(aData);
3006 ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
3007 SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
3008 SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
3010 ScDPObject* pDPObj = createDPFromRange(
3011 m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, true);
3013 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3014 bool bSuccess = pDPs->InsertNewTable(pDPObj);
3015 CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
3016 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3017 pDPs->GetCount() == 1);
3018 pDPObj->SetName(pDPs->CreateNewName());
3020 ScRange aOutRange = refresh(pDPObj);
3022 // Expected output table content. 0 = empty cell
3023 const char* aOutputCheck[][2] = {
3024 { "Filter", 0 },
3025 { "Group2", "- all -" },
3026 { 0, 0 },
3027 { "Data", 0 },
3028 { "Sum - Val1", "8" },
3029 { "Sum - Val2", "80" }
3032 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (unfiltered)");
3033 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3036 AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation.
3038 ScAddress aFormulaAddr = aOutRange.aEnd;
3039 aFormulaAddr.IncRow(2);
3040 m_pDoc->SetString(aFormulaAddr.Col(), aFormulaAddr.Row(), aFormulaAddr.Tab(),
3041 OUString("=B6"));
3042 double fTest = m_pDoc->GetValue(aFormulaAddr);
3043 CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 80.0);
3045 // Set current page of 'Group2' to 'A'.
3046 pDPObj->BuildAllDimensionMembers();
3047 ScDPSaveData aSaveData(*pDPObj->GetSaveData());
3048 ScDPSaveDimension* pPageDim = aSaveData.GetDimensionByName(
3049 OUString("Group2"));
3050 CPPUNIT_ASSERT_MESSAGE("Dimension not found", pPageDim);
3051 OUString aPage("A");
3052 pPageDim->SetCurrentPage(&aPage);
3053 pDPObj->SetSaveData(aSaveData);
3054 aOutRange = refresh(pDPObj);
3056 // Expected output table content. 0 = empty cell
3057 const char* aOutputCheck[][2] = {
3058 { "Filter", 0 },
3059 { "Group2", "A" },
3060 { 0, 0 },
3061 { "Data", 0 },
3062 { "Sum - Val1", "4" },
3063 { "Sum - Val2", "40" }
3066 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
3067 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3070 fTest = m_pDoc->GetValue(aFormulaAddr);
3071 CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 40.0);
3073 // Set query filter.
3074 ScSheetSourceDesc aDesc(*pDPObj->GetSheetDesc());
3075 ScQueryParam aQueryParam(aDesc.GetQueryParam());
3076 CPPUNIT_ASSERT_MESSAGE("There should be at least one query entry.", aQueryParam.GetEntryCount() > 0);
3077 ScQueryEntry& rEntry = aQueryParam.GetEntry(0);
3078 rEntry.bDoQuery = true;
3079 rEntry.nField = 1; // Group1
3080 rEntry.GetQueryItem().mfVal = 1;
3081 aDesc.SetQueryParam(aQueryParam);
3082 pDPObj->SetSheetDesc(aDesc);
3083 aOutRange = refresh(pDPObj);
3085 // Expected output table content. 0 = empty cell
3086 const char* aOutputCheck[][2] = {
3087 { "Filter", 0 },
3088 { "Group2", "A" },
3089 { 0, 0 },
3090 { "Data", 0 },
3091 { "Sum - Val1", "2" },
3092 { "Sum - Val2", "20" }
3095 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by query)");
3096 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3099 fTest = m_pDoc->GetValue(aFormulaAddr);
3100 CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 20.0);
3102 // Set the current page of 'Group2' back to '- all -'. The query filter
3103 // should still be in effect.
3104 pPageDim->SetCurrentPage(NULL); // Remove the page.
3105 pDPObj->SetSaveData(aSaveData);
3106 aOutRange = refresh(pDPObj);
3108 // Expected output table content. 0 = empty cell
3109 const char* aOutputCheck[][2] = {
3110 { "Filter", 0 },
3111 { "Group2", "- all -" },
3112 { 0, 0 },
3113 { "Data", 0 },
3114 { "Sum - Val1", "4" },
3115 { "Sum - Val2", "40" }
3118 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
3119 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3123 pDPs->FreeTable(pDPObj);
3124 CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
3125 pDPs->GetCount() == 0);
3127 m_pDoc->DeleteTab(1);
3128 m_pDoc->DeleteTab(0);
3131 void Test::testPivotTableNamedSource()
3133 m_pDoc->InsertTab(0, OUString("Data"));
3134 m_pDoc->InsertTab(1, OUString("Table"));
3136 // Dimension definition
3137 DPFieldDef aFields[] = {
3138 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3139 { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
3140 { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
3143 // Raw data
3144 const char* aData[][3] = {
3145 { "Andy", "A", "30" },
3146 { "Bruce", "A", "20" },
3147 { "Charlie", "B", "45" },
3148 { "David", "B", "12" },
3149 { "Edward", "C", "8" },
3150 { "Frank", "C", "15" },
3153 size_t nFieldCount = SAL_N_ELEMENTS(aFields);
3154 size_t nDataCount = SAL_N_ELEMENTS(aData);
3156 // Insert the raw data.
3157 ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
3158 OUString aRangeStr;
3159 aSrcRange.Format(aRangeStr, SCR_ABS_3D, m_pDoc);
3161 // Name this range.
3162 OUString aRangeName("MyData");
3163 ScRangeName* pNames = m_pDoc->GetRangeName();
3164 CPPUNIT_ASSERT_MESSAGE("Failed to get global range name container.", pNames);
3165 ScRangeData* pName = new ScRangeData(
3166 m_pDoc, aRangeName, aRangeStr);
3167 bool bSuccess = pNames->insert(pName);
3168 CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bSuccess);
3170 ScSheetSourceDesc aSheetDesc(m_pDoc);
3171 aSheetDesc.SetRangeName(aRangeName);
3172 ScDPObject* pDPObj = createDPFromSourceDesc(m_pDoc, aSheetDesc, aFields, nFieldCount, false);
3173 CPPUNIT_ASSERT_MESSAGE("Failed to create a new pivot table object.", pDPObj);
3175 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3176 bSuccess = pDPs->InsertNewTable(pDPObj);
3177 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3178 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3179 pDPs->GetCount() == 1);
3180 pDPObj->SetName(pDPs->CreateNewName());
3182 ScRange aOutRange = refresh(pDPObj);
3184 // Expected output table content. 0 = empty cell
3185 const char* aOutputCheck[][5] = {
3186 { "Sum - Score", "Group", 0, 0, 0 },
3187 { "Name", "A", "B", "C", "Total Result" },
3188 { "Andy", "30", 0, 0, "30" },
3189 { "Bruce", "20", 0, 0, "20" },
3190 { "Charlie", 0, "45", 0, "45" },
3191 { "David", 0, "12", 0, "12" },
3192 { "Edward", 0, 0, "8", "8" },
3193 { "Frank", 0, 0, "15", "15" },
3194 { "Total Result", "50", "57", "23", "130" }
3197 bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3198 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3201 CPPUNIT_ASSERT_MESSAGE("There should be one named range data cache.",
3202 pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
3204 // Move the table with pivot table to the left of the source data sheet.
3205 m_pDoc->MoveTab(1, 0);
3206 OUString aTabName;
3207 m_pDoc->GetName(0, aTabName);
3208 CPPUNIT_ASSERT_MESSAGE( "Wrong sheet name.", aTabName == "Table" );
3209 CPPUNIT_ASSERT_MESSAGE("Pivot table output is on the wrong sheet!",
3210 pDPObj->GetOutRange().aStart.Tab() == 0);
3212 CPPUNIT_ASSERT_MESSAGE("Moving the pivot table to another sheet shouldn't have changed the cache state.",
3213 pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
3215 const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
3216 CPPUNIT_ASSERT_MESSAGE("Sheet source description doesn't exist.", pDesc);
3217 CPPUNIT_ASSERT_MESSAGE("Named source range has been altered unexpectedly!",
3218 pDesc->GetRangeName().equals(aRangeName));
3220 CPPUNIT_ASSERT_MESSAGE("Cache should exist.", pDPs->GetNameCaches().hasCache(aRangeName));
3222 pDPs->FreeTable(pDPObj);
3223 CPPUNIT_ASSERT_MESSAGE("There should be no more tables.", pDPs->GetCount() == 0);
3224 CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more cache stored.",
3225 pDPs->GetNameCaches().size() == 0);
3227 pNames->clear();
3228 m_pDoc->DeleteTab(1);
3229 m_pDoc->DeleteTab(0);
3232 void Test::testPivotTableCache()
3234 m_pDoc->InsertTab(0, OUString("Data"));
3236 // Raw data
3237 const char* aData[][3] = {
3238 { "F1", "F2", "F3" },
3239 { "Z", "A", "30" },
3240 { "R", "A", "20" },
3241 { "A", "B", "45" },
3242 { "F", "B", "12" },
3243 { "Y", "C", "8" },
3244 { "12", "C", "15" },
3247 ScAddress aPos(1,1,0);
3248 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3249 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3251 ScDPCache aCache(m_pDoc);
3252 aCache.InitFromDoc(m_pDoc, aDataRange);
3253 long nDimCount = aCache.GetColumnCount();
3254 CPPUNIT_ASSERT_MESSAGE("wrong dimension count.", nDimCount == 3);
3255 OUString aDimName = aCache.GetDimensionName(0);
3256 CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F1"));
3257 aDimName = aCache.GetDimensionName(1);
3258 CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F2"));
3259 aDimName = aCache.GetDimensionName(2);
3260 CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F3"));
3262 // In each dimension, member ID values also represent their sort order (in
3263 // source dimensions only, not in group dimensions). Value items are
3264 // sorted before string ones. Also, no duplicate dimension members should
3265 // exist.
3267 // Dimension 0 - a mix of strings and values.
3268 long nMemCount = aCache.GetDimMemberCount(0);
3269 CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
3270 const ScDPItemData* pItem = aCache.GetItemDataById(0, 0);
3271 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3272 pItem->GetType() == ScDPItemData::Value &&
3273 pItem->GetValue() == 12);
3274 pItem = aCache.GetItemDataById(0, 1);
3275 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3276 pItem->GetType() == ScDPItemData::String &&
3277 pItem->GetString().equalsAscii("A"));
3278 pItem = aCache.GetItemDataById(0, 2);
3279 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3280 pItem->GetType() == ScDPItemData::String &&
3281 pItem->GetString().equalsAscii("F"));
3282 pItem = aCache.GetItemDataById(0, 3);
3283 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3284 pItem->GetType() == ScDPItemData::String &&
3285 pItem->GetString().equalsAscii("R"));
3286 pItem = aCache.GetItemDataById(0, 4);
3287 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3288 pItem->GetType() == ScDPItemData::String &&
3289 pItem->GetString().equalsAscii("Y"));
3290 pItem = aCache.GetItemDataById(0, 5);
3291 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3292 pItem->GetType() == ScDPItemData::String &&
3293 pItem->GetString().equalsAscii("Z"));
3294 pItem = aCache.GetItemDataById(0, 6);
3295 CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
3297 // Dimension 1 - duplicate values in source.
3298 nMemCount = aCache.GetDimMemberCount(1);
3299 CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 3);
3300 pItem = aCache.GetItemDataById(1, 0);
3301 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3302 pItem->GetType() == ScDPItemData::String &&
3303 pItem->GetString().equalsAscii("A"));
3304 pItem = aCache.GetItemDataById(1, 1);
3305 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3306 pItem->GetType() == ScDPItemData::String &&
3307 pItem->GetString().equalsAscii("B"));
3308 pItem = aCache.GetItemDataById(1, 2);
3309 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3310 pItem->GetType() == ScDPItemData::String &&
3311 pItem->GetString().equalsAscii("C"));
3312 pItem = aCache.GetItemDataById(1, 3);
3313 CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
3315 // Dimension 2 - values only.
3316 nMemCount = aCache.GetDimMemberCount(2);
3317 CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
3318 pItem = aCache.GetItemDataById(2, 0);
3319 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3320 pItem->GetType() == ScDPItemData::Value &&
3321 pItem->GetValue() == 8);
3322 pItem = aCache.GetItemDataById(2, 1);
3323 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3324 pItem->GetType() == ScDPItemData::Value &&
3325 pItem->GetValue() == 12);
3326 pItem = aCache.GetItemDataById(2, 2);
3327 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3328 pItem->GetType() == ScDPItemData::Value &&
3329 pItem->GetValue() == 15);
3330 pItem = aCache.GetItemDataById(2, 3);
3331 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3332 pItem->GetType() == ScDPItemData::Value &&
3333 pItem->GetValue() == 20);
3334 pItem = aCache.GetItemDataById(2, 4);
3335 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3336 pItem->GetType() == ScDPItemData::Value &&
3337 pItem->GetValue() == 30);
3338 pItem = aCache.GetItemDataById(2, 5);
3339 CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3340 pItem->GetType() == ScDPItemData::Value &&
3341 pItem->GetValue() == 45);
3342 pItem = aCache.GetItemDataById(2, 6);
3343 CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
3346 // Check the integrity of the source data.
3347 ScDPItemData aTest;
3348 long nDim;
3351 // Dimension 0: Z, R, A, F, Y, 12
3352 nDim = 0;
3353 const char* aChecks[] = { "Z", "R", "A", "F", "Y" };
3354 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3356 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
3357 aTest.SetString(OUString::createFromAscii(aChecks[i]));
3358 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3361 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, 5, false));
3362 aTest.SetValue(12);
3363 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3367 // Dimension 1: A, A, B, B, C, C
3368 nDim = 1;
3369 const char* aChecks[] = { "A", "A", "B", "B", "C", "C" };
3370 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3372 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
3373 aTest.SetString(OUString::createFromAscii(aChecks[i]));
3374 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3379 // Dimension 2: 30, 20, 45, 12, 8, 15
3380 nDim = 2;
3381 double aChecks[] = { 30, 20, 45, 12, 8, 15 };
3382 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3384 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
3385 aTest.SetValue(aChecks[i]);
3386 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3391 // Now, on to testing the filtered cache.
3394 // Non-filtered cache - everything should be visible.
3395 ScDPFilteredCache aFilteredCache(aCache);
3396 aFilteredCache.fillTable();
3398 sal_Int32 nRows = aFilteredCache.getRowSize();
3399 CPPUNIT_ASSERT_MESSAGE("Wrong dimension.", nRows == 6 && aFilteredCache.getColSize() == 3);
3401 for (sal_Int32 i = 0; i < nRows; ++i)
3403 if (!aFilteredCache.isRowActive(i))
3405 std::ostringstream os;
3406 os << "Row " << i << " should be visible but it isn't.";
3407 CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
3412 // TODO : Add test for filtered caches.
3414 m_pDoc->DeleteTab(0);
3417 void Test::testPivotTableDuplicateDataFields()
3419 m_pDoc->InsertTab(0, OUString("Data"));
3420 m_pDoc->InsertTab(1, OUString("Table"));
3422 // Raw data
3423 const char* aData[][2] = {
3424 { "Name", "Value" },
3425 { "A", "45" },
3426 { "A", "5" },
3427 { "A", "41" },
3428 { "A", "49" },
3429 { "A", "4" },
3430 { "B", "33" },
3431 { "B", "84" },
3432 { "B", "74" },
3433 { "B", "8" },
3434 { "B", "68" }
3437 // Dimension definition
3438 DPFieldDef aFields[] = {
3439 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3440 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3441 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_COUNT }
3444 ScAddress aPos(2,2,0);
3445 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3446 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3448 ScDPObject* pDPObj = createDPFromRange(
3449 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3451 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3452 bool bSuccess = pDPs->InsertNewTable(pDPObj);
3454 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3455 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3456 pDPs->GetCount(), static_cast<size_t>(1));
3457 pDPObj->SetName(pDPs->CreateNewName());
3459 ScRange aOutRange = refresh(pDPObj);
3461 // Expected output table content. 0 = empty cell
3462 const char* aOutputCheck[][3] = {
3463 { "Name", "Data", 0 },
3464 { "A", "Sum - Value", "144" },
3465 { 0, "Count - Value", "5" },
3466 { "B", "Sum - Value", "267" },
3467 { 0, "Count - Value", "5" },
3468 { "Total Sum - Value", 0, "411" },
3469 { "Total Count - Value", 0, "10" },
3472 bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3473 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3476 // Move the data layout dimension from row to column.
3477 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3478 CPPUNIT_ASSERT_MESSAGE("No save data!?", pSaveData);
3479 ScDPSaveDimension* pDataLayout = pSaveData->GetDataLayoutDimension();
3480 CPPUNIT_ASSERT_MESSAGE("No data layout dimension.", pDataLayout);
3481 pDataLayout->SetOrientation(sheet::DataPilotFieldOrientation_COLUMN);
3482 pDPObj->SetSaveData(*pSaveData);
3484 // Refresh the table output.
3485 aOutRange = refresh(pDPObj);
3487 // Expected output table content. 0 = empty cell
3488 const char* aOutputCheck[][3] = {
3489 { 0, "Data", 0 },
3490 { "Name", "Sum - Value", "Count - Value" },
3491 { "A", "144", "5" },
3492 { "B", "267", "5" },
3493 { "Total Result", "411", "10" }
3496 bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3497 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3500 ScPivotParam aParam;
3501 pDPObj->FillLabelData(aParam);
3502 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 4 labels (2 original, 1 data layout, and 1 duplicate dimensions).",
3503 aParam.maLabelArray.size(), static_cast<size_t>(4));
3505 pDPs->FreeTable(pDPObj);
3506 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3507 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3508 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3510 m_pDoc->DeleteTab(1);
3511 m_pDoc->DeleteTab(0);
3514 void Test::testPivotTableNormalGrouping()
3516 m_pDoc->InsertTab(0, OUString("Data"));
3517 m_pDoc->InsertTab(1, OUString("Table"));
3519 // Raw data
3520 const char* aData[][2] = {
3521 { "Name", "Value" },
3522 { "A", "1" },
3523 { "B", "2" },
3524 { "C", "3" },
3525 { "D", "4" },
3526 { "E", "5" },
3527 { "F", "6" },
3528 { "G", "7" }
3531 // Dimension definition
3532 DPFieldDef aFields[] = {
3533 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3534 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3537 ScAddress aPos(1,1,0);
3538 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3539 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3541 ScDPObject* pDPObj = createDPFromRange(
3542 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3544 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3545 bool bSuccess = pDPs->InsertNewTable(pDPObj);
3547 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3548 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3549 pDPs->GetCount(), static_cast<size_t>(1));
3550 pDPObj->SetName(pDPs->CreateNewName());
3552 ScRange aOutRange = refresh(pDPObj);
3554 // Expected output table content. 0 = empty cell
3555 const char* aOutputCheck[][2] = {
3556 { "Name", 0 },
3557 { "A", "1" },
3558 { "B", "2" },
3559 { "C", "3" },
3560 { "D", "4" },
3561 { "E", "5" },
3562 { "F", "6" },
3563 { "G", "7" },
3564 { "Total Result", "28" }
3567 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Initial output without grouping");
3568 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3571 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3572 CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
3573 ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
3574 CPPUNIT_ASSERT_MESSAGE("Failed to create dimension data.", pDimData);
3576 OUString aGroupPrefix("Group");
3577 OUString aBaseDimName("Name");
3578 OUString aGroupDimName =
3579 pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, NULL);
3582 // Group A, B and C together.
3583 ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
3584 OUString aGroupName = aGroupDim.CreateGroupName(aGroupPrefix);
3585 CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group1"));
3587 ScDPSaveGroupItem aGroup(aGroupName);
3588 aGroup.AddElement(OUString("A"));
3589 aGroup.AddElement(OUString("B"));
3590 aGroup.AddElement(OUString("C"));
3591 aGroupDim.AddGroupItem(aGroup);
3592 pDimData->AddGroupDimension(aGroupDim);
3594 ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
3595 pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
3596 pSaveData->SetPosition(pDim, 0); // Set it before the base dimension.
3599 pDPObj->SetSaveData(*pSaveData);
3600 aOutRange = refreshGroups(pDPs, pDPObj);
3602 // Expected output table content. 0 = empty cell
3603 const char* aOutputCheck[][3] = {
3604 { "Name2", "Name", 0 },
3605 { "D", "D", "4" },
3606 { "E", "E", "5" },
3607 { "F", "F", "6" },
3608 { "G", "G", "7" },
3609 { "Group1", "A", "1" },
3610 { 0, "B", "2" },
3611 { 0, "C", "3" },
3612 { "Total Result", 0, "28" }
3615 bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "A, B, C grouped by Group1.");
3616 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3619 pSaveData = pDPObj->GetSaveData();
3620 pDimData = pSaveData->GetDimensionData();
3623 // Group D, E, F together.
3624 ScDPSaveGroupDimension* pGroupDim = pDimData->GetGroupDimAccForBase(aBaseDimName);
3625 CPPUNIT_ASSERT_MESSAGE("There should be an existing group dimension.", pGroupDim);
3626 OUString aGroupName = pGroupDim->CreateGroupName(aGroupPrefix);
3627 CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group2"));
3629 ScDPSaveGroupItem aGroup(aGroupName);
3630 aGroup.AddElement(OUString("D"));
3631 aGroup.AddElement(OUString("E"));
3632 aGroup.AddElement(OUString("F"));
3633 pGroupDim->AddGroupItem(aGroup);
3636 pDPObj->SetSaveData(*pSaveData);
3637 aOutRange = refreshGroups(pDPs, pDPObj);
3639 // Expected output table content. 0 = empty cell
3640 const char* aOutputCheck[][3] = {
3641 { "Name2", "Name", 0 },
3642 { "G", "G", "7" },
3643 { "Group1", "A", "1" },
3644 { 0, "B", "2" },
3645 { 0, "C", "3" },
3646 { "Group2", "D", "4" },
3647 { 0, "E", "5" },
3648 { 0, "F", "6" },
3649 { "Total Result", 0, "28" }
3652 bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "D, E, F grouped by Group2.");
3653 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3656 pDPs->FreeTable(pDPObj);
3657 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3658 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3659 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3661 m_pDoc->DeleteTab(1);
3662 m_pDoc->DeleteTab(0);
3665 void Test::testPivotTableNumberGrouping()
3667 m_pDoc->InsertTab(0, OUString("Data"));
3668 m_pDoc->InsertTab(1, OUString("Table"));
3670 // Raw data
3671 const char* aData[][2] = {
3672 { "Order", "Score" },
3673 { "43", "171" },
3674 { "18", "20" },
3675 { "69", "159" },
3676 { "95", "19" },
3677 { "96", "163" },
3678 { "46", "70" },
3679 { "22", "36" },
3680 { "81", "49" },
3681 { "54", "61" },
3682 { "39", "62" },
3683 { "86", "17" },
3684 { "34", "0" },
3685 { "30", "25" },
3686 { "24", "103" },
3687 { "16", "59" },
3688 { "24", "119" },
3689 { "15", "86" },
3690 { "69", "170" }
3693 // Dimension definition
3694 DPFieldDef aFields[] = {
3695 { "Order", sheet::DataPilotFieldOrientation_ROW, 0 },
3696 { "Score", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3699 ScAddress aPos(1,1,0);
3700 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3701 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3703 ScDPObject* pDPObj = createDPFromRange(
3704 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3706 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3707 bool bSuccess = pDPs->InsertNewTable(pDPObj);
3709 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3710 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3711 pDPs->GetCount(), static_cast<size_t>(1));
3712 pDPObj->SetName(pDPs->CreateNewName());
3714 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3715 CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
3716 ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
3717 CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
3720 ScDPNumGroupInfo aInfo;
3721 aInfo.mbEnable = true;
3722 aInfo.mbAutoStart = false;
3723 aInfo.mbAutoEnd = false;
3724 aInfo.mbDateValues = false;
3725 aInfo.mbIntegerOnly = true;
3726 aInfo.mfStart = 30;
3727 aInfo.mfEnd = 60;
3728 aInfo.mfStep = 10;
3729 ScDPSaveNumGroupDimension aGroup(OUString("Order"), aInfo);
3730 pDimData->AddNumGroupDimension(aGroup);
3733 pDPObj->SetSaveData(*pSaveData);
3734 ScRange aOutRange = refreshGroups(pDPs, pDPObj);
3736 // Expected output table content. 0 = empty cell
3737 const char* aOutputCheck[][2] = {
3738 { "Order", 0 },
3739 { "<30", "423" },
3740 { "30-39", "87" },
3741 { "40-49", "241" },
3742 { "50-60", "61" },
3743 { ">60", "577" },
3744 { "Total Result", "1389" }
3747 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Order grouped by numbers");
3748 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3751 pDPs->FreeTable(pDPObj);
3752 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3753 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3754 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3756 m_pDoc->DeleteTab(1);
3757 m_pDoc->DeleteTab(0);
3760 void Test::testPivotTableDateGrouping()
3762 m_pDoc->InsertTab(0, OUString("Data"));
3763 m_pDoc->InsertTab(1, OUString("Table"));
3765 // Raw data
3766 const char* aData[][2] = {
3767 { "Date", "Value" },
3768 { "2011-01-01", "1" },
3769 { "2011-03-02", "2" },
3770 { "2012-01-04", "3" },
3771 { "2012-02-23", "4" },
3772 { "2012-02-24", "5" },
3773 { "2012-03-15", "6" },
3774 { "2011-09-03", "7" },
3775 { "2012-12-25", "8" }
3778 // Dimension definition
3779 DPFieldDef aFields[] = {
3780 { "Date", sheet::DataPilotFieldOrientation_ROW, 0 },
3781 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3784 ScAddress aPos(1,1,0);
3785 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3786 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3788 ScDPObject* pDPObj = createDPFromRange(
3789 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3791 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3792 bool bSuccess = pDPs->InsertNewTable(pDPObj);
3794 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3795 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3796 pDPs->GetCount() == 1);
3797 pDPObj->SetName(pDPs->CreateNewName());
3799 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3800 CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
3801 ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
3802 CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
3804 OUString aBaseDimName("Date");
3806 ScDPNumGroupInfo aInfo;
3807 aInfo.mbEnable = true;
3808 aInfo.mbAutoStart = true;
3809 aInfo.mbAutoEnd = true;
3811 // Turn the Date dimension into months. The first of the date
3812 // dimensions is always a number-group dimension which replaces the
3813 // original dimension.
3814 ScDPSaveNumGroupDimension aGroup(aBaseDimName, aInfo, sheet::DataPilotFieldGroupBy::MONTHS);
3815 pDimData->AddNumGroupDimension(aGroup);
3819 // Add quarter dimension. This will be an additional dimension.
3820 OUString aGroupDimName =
3821 pDimData->CreateDateGroupDimName(
3822 sheet::DataPilotFieldGroupBy::QUARTERS, *pDPObj, true, NULL);
3823 ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
3824 aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::QUARTERS);
3825 pDimData->AddGroupDimension(aGroupDim);
3827 // Set orientation.
3828 ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
3829 pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
3830 pSaveData->SetPosition(pDim, 0); // set it to the left end.
3834 // Add year dimension. This is a new dimension also.
3835 OUString aGroupDimName =
3836 pDimData->CreateDateGroupDimName(
3837 sheet::DataPilotFieldGroupBy::YEARS, *pDPObj, true, NULL);
3838 ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
3839 aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::YEARS);
3840 pDimData->AddGroupDimension(aGroupDim);
3842 // Set orientation.
3843 ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
3844 pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
3845 pSaveData->SetPosition(pDim, 0); // set it to the left end.
3848 pDPObj->SetSaveData(*pSaveData);
3849 ScRange aOutRange = refreshGroups(pDPs, pDPObj);
3851 // Expected output table content. 0 = empty cell
3852 const char* aOutputCheck[][4] = {
3853 { "Years", "Quarters", "Date", 0 },
3854 { "2011", "Q1", "Jan", "1" },
3855 { 0, 0, "Mar", "2" },
3856 { 0, "Q3", "Sep", "7" },
3857 { "2012", "Q1", "Jan", "3" },
3858 { 0, 0, "Feb", "9" },
3859 { 0, 0, "Mar", "6" },
3860 { 0, "Q4", "Dec", "8" },
3861 { "Total Result", 0, 0, "36" },
3864 bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Years, quarters and months date groups.");
3865 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3869 // Let's hide year 2012.
3870 pSaveData = pDPObj->GetSaveData();
3871 ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(OUString("Years"));
3872 CPPUNIT_ASSERT_MESSAGE("Years dimension should exist.", pDim);
3873 ScDPSaveMember* pMem = pDim->GetMemberByName(OUString("2012"));
3874 CPPUNIT_ASSERT_MESSAGE("Member should exist.", pMem);
3875 pMem->SetIsVisible(false);
3877 pDPObj->SetSaveData(*pSaveData);
3878 pDPObj->ReloadGroupTableData();
3879 pDPObj->InvalidateData();
3881 aOutRange = refresh(pDPObj);
3883 // Expected output table content. 0 = empty cell
3884 const char* aOutputCheck[][4] = {
3885 { "Years", "Quarters", "Date", 0 },
3886 { "2011", "Q1", "Jan", "1" },
3887 { 0, 0, "Mar", "2" },
3888 { 0, "Q3", "Sep", "7" },
3889 { "Total Result", 0, 0, "10" },
3892 bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Year 2012 data now hidden");
3893 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3896 // Remove all date grouping. The source dimension "Date" has two
3897 // external dimensions ("Years" and "Quarters") and one internal ("Date"
3898 // the same name but different hierarchy). Remove all of them.
3899 pSaveData = pDPObj->GetSaveData();
3900 pSaveData->RemoveAllGroupDimensions(aBaseDimName);
3901 pDPObj->SetSaveData(*pSaveData);
3902 pDPObj->ReloadGroupTableData();
3903 pDPObj->InvalidateData();
3905 aOutRange = refresh(pDPObj);
3907 // Expected output table content. 0 = empty cell
3908 const char* aOutputCheck[][2] = {
3909 { "Date", 0 },
3910 { "2011-01-01", "1" },
3911 { "2011-03-02", "2" },
3912 { "2011-09-03", "7" },
3913 { "2012-01-04", "3" },
3914 { "2012-02-23", "4" },
3915 { "2012-02-24", "5" },
3916 { "2012-03-15", "6" },
3917 { "2012-12-25", "8" },
3918 { "Total Result", "36" }
3921 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Remove all date grouping.");
3922 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3925 pDPs->FreeTable(pDPObj);
3926 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3927 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3928 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3930 m_pDoc->DeleteTab(1);
3931 m_pDoc->DeleteTab(0);
3934 void Test::testPivotTableEmptyRows()
3936 m_pDoc->InsertTab(0, OUString("Data"));
3937 m_pDoc->InsertTab(1, OUString("Table"));
3939 // Raw data
3940 const char* aData[][2] = {
3941 { "Name", "Value" },
3942 { "A", "1" },
3943 { "B", "2" },
3944 { "C", "3" },
3945 { "D", "4" },
3948 // Dimension definition
3949 DPFieldDef aFields[] = {
3950 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3951 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3954 ScAddress aPos(1,1,0);
3955 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3956 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3958 // Extend the range downward to include some trailing empty rows.
3959 aDataRange.aEnd.IncRow(2);
3961 ScDPObject* pDPObj = createDPFromRange(
3962 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3964 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3965 bool bSuccess = pDPs->InsertNewTable(pDPObj);
3967 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3968 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3969 pDPs->GetCount() == 1);
3970 pDPObj->SetName(pDPs->CreateNewName());
3972 ScRange aOutRange = refresh(pDPObj);
3975 // Expected output table content. 0 = empty cell
3976 const char* aOutputCheck[][2] = {
3977 { "Name", 0 },
3978 { "A", "1" },
3979 { "B", "2" },
3980 { "C", "3" },
3981 { "D", "4" },
3982 { "(empty)", 0 },
3983 { "Total Result", "10" },
3986 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Include empty rows");
3987 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3990 // This time, ignore empty rows.
3991 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3992 CPPUNIT_ASSERT_MESSAGE("Save data doesn't exist.", pSaveData);
3993 pSaveData->SetIgnoreEmptyRows(true);
3994 pDPObj->ClearTableData();
3995 aOutRange = refresh(pDPObj);
3998 // Expected output table content. 0 = empty cell
3999 const char* aOutputCheck[][2] = {
4000 { "Name", 0 },
4001 { "A", "1" },
4002 { "B", "2" },
4003 { "C", "3" },
4004 { "D", "4" },
4005 { "Total Result", "10" },
4008 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
4009 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4012 // Modify the source to remove member 'A', then refresh the table.
4013 m_pDoc->SetString(1, 2, 0, "B");
4015 std::set<ScDPObject*> aRefs;
4016 sal_uLong nErr = pDPs->ReloadCache(pDPObj, aRefs);
4017 CPPUNIT_ASSERT_MESSAGE("Failed to reload cache.", !nErr);
4018 CPPUNIT_ASSERT_MESSAGE("There should only be one pivot table linked to this cache.",
4019 aRefs.size() == 1 && *aRefs.begin() == pDPObj);
4021 pDPObj->ClearTableData();
4022 aOutRange = refresh(pDPObj);
4025 // Expected output table content. 0 = empty cell
4026 const char* aOutputCheck[][2] = {
4027 { "Name", 0 },
4028 { "B", "3" },
4029 { "C", "3" },
4030 { "D", "4" },
4031 { "Total Result", "10" },
4034 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
4035 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4038 pDPs->FreeTable(pDPObj);
4039 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4040 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4041 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4043 m_pDoc->DeleteTab(1);
4044 m_pDoc->DeleteTab(0);
4047 void Test::testPivotTableTextNumber()
4049 m_pDoc->InsertTab(0, OUString("Data"));
4050 m_pDoc->InsertTab(1, OUString("Table"));
4052 // Raw data
4053 const char* aData[][2] = {
4054 { "Name", "Value" },
4055 { "0001", "1" },
4056 { "0002", "2" },
4057 { "0003", "3" },
4058 { "0004", "4" },
4061 // Dimension definition
4062 DPFieldDef aFields[] = {
4063 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4064 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4067 // Insert raw data such that the first column values are entered as text.
4068 size_t nRowCount = SAL_N_ELEMENTS(aData);
4069 for (size_t nRow = 0; nRow < nRowCount; ++nRow)
4071 ScSetStringParam aParam;
4072 aParam.mbDetectNumberFormat = false;
4073 aParam.meSetTextNumFormat = ScSetStringParam::Always;
4074 m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aData[nRow][0]), &aParam);
4075 aParam.meSetTextNumFormat = ScSetStringParam::Never;
4076 m_pDoc->SetString(1, nRow, 0, OUString::createFromAscii(aData[nRow][1]), &aParam);
4078 if (nRow == 0)
4079 // Don't check the header row.
4080 continue;
4082 // Check the data rows.
4083 CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be text.", m_pDoc->HasStringData(0, nRow, 0));
4084 CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be numeric.", m_pDoc->HasValueData(1, nRow, 0));
4087 ScRange aDataRange(0, 0, 0, 1, 4, 0);
4089 ScDPObject* pDPObj = createDPFromRange(
4090 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4092 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4093 bool bSuccess = pDPs->InsertNewTable(pDPObj);
4095 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4096 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4097 pDPs->GetCount() == 1);
4098 pDPObj->SetName(pDPs->CreateNewName());
4100 ScRange aOutRange = refresh(pDPObj);
4103 // Expected output table content. 0 = empty cell
4104 const char* aOutputCheck[][2] = {
4105 { "Name", 0 },
4106 { "0001", "1" },
4107 { "0002", "2" },
4108 { "0003", "3" },
4109 { "0004", "4" },
4110 { "Total Result", "10" },
4113 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Text number field members");
4114 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4117 pDPs->FreeTable(pDPObj);
4118 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4119 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4120 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4122 m_pDoc->DeleteTab(1);
4123 m_pDoc->DeleteTab(0);
4126 void Test::testPivotTableCaseInsensitiveStrings()
4128 m_pDoc->InsertTab(0, OUString("Data"));
4129 m_pDoc->InsertTab(1, OUString("Table"));
4131 // Raw data
4132 const char* aData[][2] = {
4133 { "Name", "Value" },
4134 { "A", "1" },
4135 { "a", "2" },
4138 // Dimension definition
4139 DPFieldDef aFields[] = {
4140 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4141 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4144 ScAddress aPos(1,1,0);
4145 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4146 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4148 ScDPObject* pDPObj = createDPFromRange(
4149 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4151 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4152 bool bSuccess = pDPs->InsertNewTable(pDPObj);
4154 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4155 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4156 pDPs->GetCount() == 1);
4157 pDPObj->SetName(pDPs->CreateNewName());
4159 ScRange aOutRange = refresh(pDPObj);
4162 // Expected output table content. 0 = empty cell
4163 const char* aOutputCheck[][2] = {
4164 { "Name", 0 },
4165 { "A", "3" },
4166 { "Total Result", "3" },
4169 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Case insensitive strings");
4170 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4173 pDPs->FreeTable(pDPObj);
4174 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4175 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4176 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4178 m_pDoc->DeleteTab(1);
4179 m_pDoc->DeleteTab(0);
4182 void Test::testPivotTableNumStability()
4184 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
4186 // Raw Data
4187 const char* aData[][4] = {
4188 { "Name", "Time Start", "Time End", "Total" },
4189 { "Sam", "07:48 AM", "09:00 AM", "=RC[-1]-RC[-2]" },
4190 { "Sam", "09:00 AM", "10:30 AM", "=RC[-1]-RC[-2]" },
4191 { "Sam", "10:30 AM", "12:30 PM", "=RC[-1]-RC[-2]" },
4192 { "Sam", "12:30 PM", "01:00 PM", "=RC[-1]-RC[-2]" },
4193 { "Sam", "01:00 PM", "01:30 PM", "=RC[-1]-RC[-2]" },
4194 { "Sam", "01:30 PM", "02:00 PM", "=RC[-1]-RC[-2]" },
4195 { "Sam", "02:00 PM", "07:15 PM", "=RC[-1]-RC[-2]" },
4196 { "Sam", "07:47 AM", "09:00 AM", "=RC[-1]-RC[-2]" },
4197 { "Sam", "09:00 AM", "10:00 AM", "=RC[-1]-RC[-2]" },
4198 { "Sam", "10:00 AM", "11:00 AM", "=RC[-1]-RC[-2]" },
4199 { "Sam", "11:00 AM", "11:30 AM", "=RC[-1]-RC[-2]" },
4200 { "Sam", "11:30 AM", "12:45 PM", "=RC[-1]-RC[-2]" },
4201 { "Sam", "12:45 PM", "01:15 PM", "=RC[-1]-RC[-2]" },
4202 { "Sam", "01:15 PM", "02:30 PM", "=RC[-1]-RC[-2]" },
4203 { "Sam", "02:30 PM", "02:45 PM", "=RC[-1]-RC[-2]" },
4204 { "Sam", "02:45 PM", "04:30 PM", "=RC[-1]-RC[-2]" },
4205 { "Sam", "04:30 PM", "06:00 PM", "=RC[-1]-RC[-2]" },
4206 { "Sam", "06:00 PM", "07:15 PM", "=RC[-1]-RC[-2]" },
4207 { "Mike", "06:15 AM", "08:30 AM", "=RC[-1]-RC[-2]" },
4208 { "Mike", "08:30 AM", "10:03 AM", "=RC[-1]-RC[-2]" },
4209 { "Mike", "10:03 AM", "12:00 PM", "=RC[-1]-RC[-2]" },
4210 { "Dennis", "11:00 AM", "01:00 PM", "=RC[-1]-RC[-2]" },
4211 { "Dennis", "01:00 PM", "02:00 PM", "=RC[-1]-RC[-2]" }
4214 // Dimension definition
4215 DPFieldDef aFields[] = {
4216 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4217 { "Total", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4220 m_pDoc->InsertTab(0, OUString("Data"));
4221 m_pDoc->InsertTab(1, OUString("Table"));
4223 size_t nRowCount = SAL_N_ELEMENTS(aData);
4224 ScAddress aPos(1,1,0);
4225 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, nRowCount);
4227 // Insert formulas to manually calculate sums for each name.
4228 m_pDoc->SetString(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+1, aDataRange.aStart.Tab(), "=SUMIF(R[-23]C:R[-1]C;\"Dennis\";R[-23]C[3]:R[-1]C[3])");
4229 m_pDoc->SetString(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+2, aDataRange.aStart.Tab(), "=SUMIF(R[-24]C:R[-2]C;\"Mike\";R[-24]C[3]:R[-2]C[3])");
4230 m_pDoc->SetString(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+3, aDataRange.aStart.Tab(), "=SUMIF(R[-25]C:R[-3]C;\"Sam\";R[-25]C[3]:R[-3]C[3])");
4232 m_pDoc->CalcAll();
4234 // Get correct sum values.
4235 double fDennisTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+1, aDataRange.aStart.Tab());
4236 double fMikeTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+2, aDataRange.aStart.Tab());
4237 double fSamTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+3, aDataRange.aStart.Tab());
4239 ScDPObject* pDPObj = createDPFromRange(
4240 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4242 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4243 bool bSuccess = pDPs->InsertNewTable(pDPObj);
4245 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4246 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
4247 pDPs->GetCount(), static_cast<size_t>(1));
4248 pDPObj->SetName(pDPs->CreateNewName());
4250 ScRange aOutRange = refresh(pDPObj);
4252 // Manually check the total value for each name.
4254 // +--------------+----------------+
4255 // | Name | |
4256 // +--------------+----------------+
4257 // | Dennis | <Dennis total> |
4258 // +--------------+----------------+
4259 // | Mike | <Miks total> |
4260 // +--------------+----------------+
4261 // | Sam | <Sam total> |
4262 // +--------------+----------------+
4263 // | Total Result | ... |
4264 // +--------------+----------------+
4266 aPos = aOutRange.aStart;
4267 aPos.IncCol();
4268 aPos.IncRow();
4269 double fTest = m_pDoc->GetValue(aPos);
4270 CPPUNIT_ASSERT_MESSAGE("Incorrect value for Dennis.", rtl::math::approxEqual(fTest, fDennisTotal));
4271 aPos.IncRow();
4272 fTest = m_pDoc->GetValue(aPos);
4273 CPPUNIT_ASSERT_MESSAGE("Incorrect value for Mike.", rtl::math::approxEqual(fTest, fMikeTotal));
4274 aPos.IncRow();
4275 fTest = m_pDoc->GetValue(aPos);
4276 CPPUNIT_ASSERT_MESSAGE("Incorrect value for Sam.", rtl::math::approxEqual(fTest, fSamTotal));
4278 pDPs->FreeTable(pDPObj);
4279 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4280 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4281 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4283 m_pDoc->DeleteTab(1);
4284 m_pDoc->DeleteTab(0);
4287 void Test::testPivotTableFieldReference()
4289 m_pDoc->InsertTab(0, OUString("Data"));
4290 m_pDoc->InsertTab(1, OUString("Table"));
4292 // Raw data
4293 const char* aData[][2] = {
4294 { "Name", "Value" },
4295 { "A", "1" },
4296 { "B", "2" },
4297 { "C", "4" },
4298 { "D", "8" },
4301 // Dimension definition
4302 DPFieldDef aFields[] = {
4303 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4304 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4307 ScAddress aPos(1,1,0);
4308 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4309 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4311 ScDPObject* pDPObj = createDPFromRange(
4312 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4314 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4315 bool bSuccess = pDPs->InsertNewTable(pDPObj);
4317 CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4318 CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4319 pDPs->GetCount() == 1);
4320 pDPObj->SetName(pDPs->CreateNewName());
4322 ScRange aOutRange = refresh(pDPObj);
4325 // Expected output table content. 0 = empty cell
4326 const char* aOutputCheck[][2] = {
4327 { "Name", 0 },
4328 { "A", "1" },
4329 { "B", "2" },
4330 { "C", "4" },
4331 { "D", "8" },
4332 { "Total Result", "15" },
4335 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (none)");
4336 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4339 ScDPSaveData aSaveData = *pDPObj->GetSaveData();
4340 sheet::DataPilotFieldReference aFieldRef;
4341 aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE;
4342 aFieldRef.ReferenceField = "Name";
4343 aFieldRef.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NAMED;
4344 aFieldRef.ReferenceItemName = "A";
4345 ScDPSaveDimension* pDim = aSaveData.GetDimensionByName("Value");
4346 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve dimension 'Value'.", pDim);
4347 pDim->SetReferenceValue(&aFieldRef);
4348 pDPObj->SetSaveData(aSaveData);
4350 aOutRange = refresh(pDPObj);
4352 // Expected output table content. 0 = empty cell
4353 const char* aOutputCheck[][2] = {
4354 { "Name", 0 },
4355 { "A", 0 },
4356 { "B", "1" },
4357 { "C", "3" },
4358 { "D", "7" },
4359 { "Total Result", 0 },
4362 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (difference from)");
4363 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4366 aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE;
4367 pDim->SetReferenceValue(&aFieldRef);
4368 pDPObj->SetSaveData(aSaveData);
4370 aOutRange = refresh(pDPObj);
4372 // Expected output table content. 0 = empty cell
4373 const char* aOutputCheck[][2] = {
4374 { "Name", 0 },
4375 { "A", "100.00%" },
4376 { "B", "200.00%" },
4377 { "C", "400.00%" },
4378 { "D", "800.00%" },
4379 { "Total Result", 0 },
4382 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% of)");
4383 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4386 aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
4387 pDim->SetReferenceValue(&aFieldRef);
4388 pDPObj->SetSaveData(aSaveData);
4390 aOutRange = refresh(pDPObj);
4392 // Expected output table content. 0 = empty cell
4393 const char* aOutputCheck[][2] = {
4394 { "Name", 0 },
4395 { "A", 0 },
4396 { "B", "100.00%" },
4397 { "C", "300.00%" },
4398 { "D", "700.00%" },
4399 { "Total Result", 0 },
4402 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% difference from)");
4403 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4406 aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::RUNNING_TOTAL;
4407 pDim->SetReferenceValue(&aFieldRef);
4408 pDPObj->SetSaveData(aSaveData);
4410 aOutRange = refresh(pDPObj);
4412 // Expected output table content. 0 = empty cell
4413 const char* aOutputCheck[][2] = {
4414 { "Name", 0 },
4415 { "A", "1" },
4416 { "B", "3" },
4417 { "C", "7" },
4418 { "D", "15" },
4419 { "Total Result", 0 },
4422 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (Running total)");
4423 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4426 aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
4427 pDim->SetReferenceValue(&aFieldRef);
4428 pDPObj->SetSaveData(aSaveData);
4430 aOutRange = refresh(pDPObj);
4432 // Expected output table content. 0 = empty cell
4433 const char* aOutputCheck[][2] = {
4434 { "Name", 0 },
4435 { "A", "6.67%" },
4436 { "B", "13.33%" },
4437 { "C", "26.67%" },
4438 { "D", "53.33%" },
4439 { "Total Result", "100.00%" },
4442 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% of column)");
4443 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4446 pDPs->FreeTable(pDPObj);
4447 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4448 CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4449 pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4451 m_pDoc->DeleteTab(1);
4452 m_pDoc->DeleteTab(0);
4455 void Test::testPivotTableDocFunc()
4457 m_pDoc->InsertTab(0, "Data");
4458 m_pDoc->InsertTab(1, "Table");
4460 // Raw data
4461 const char* aData[][2] = {
4462 { "Name", "Value" },
4463 { "Sun", "1" },
4464 { "Oracle", "2" },
4465 { "Red Hat", "4" },
4466 { "SUSE", "8" },
4467 { "Apple", "16" },
4468 { "Microsoft", "32" },
4471 // Dimension definition
4472 DPFieldDef aFields[] = {
4473 { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4474 { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4477 ScAddress aPos(1,1,0);
4478 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4479 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4481 ScDPObject* pDPObj = createDPFromRange(
4482 m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4484 CPPUNIT_ASSERT_MESSAGE("Failed to create pivot table object.", pDPObj);
4486 // Craete a new pivot table output.
4487 ScDBDocFunc aFunc(*m_xDocShRef);
4488 bool bSuccess = aFunc.CreatePivotTable(*pDPObj, false, true);
4489 CPPUNIT_ASSERT_MESSAGE("Failed to create pivot table output via ScDBDocFunc.", bSuccess);
4490 ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4491 CPPUNIT_ASSERT_MESSAGE("Failed to get pivot table collection.", pDPs);
4492 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDPs->GetCount());
4493 pDPObj = (*pDPs)[0];
4494 CPPUNIT_ASSERT_MESSAGE("Failed to retrieve pivot table object from the collection", pDPObj);
4495 ScRange aOutRange = pDPObj->GetOutRange();
4497 // Expected output table content. 0 = empty cell
4498 const char* aOutputCheck[][2] = {
4499 { "Name", 0 },
4500 { "Apple", "16" },
4501 { "Microsoft", "32" },
4502 { "Oracle", "2" },
4503 { "Red Hat", "4" },
4504 { "Sun", "1" },
4505 { "SUSE", "8" },
4506 { "Total Result", "63" },
4509 bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Pivot table created via ScDBDocFunc");
4510 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4513 // Remove this pivot table output. This should also clear the pivot cache
4514 // it was referencing.
4515 bSuccess = aFunc.RemovePivotTable(*pDPObj, false, true);
4516 CPPUNIT_ASSERT_MESSAGE("Failed to remove pivot table output via ScDBDocFunc.", bSuccess);
4517 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pDPs->GetCount());
4518 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pDPs->GetSheetCaches().size());
4520 m_pDoc->DeleteTab(1);
4521 m_pDoc->DeleteTab(0);
4524 void Test::testSheetCopy()
4526 m_pDoc->InsertTab(0, "TestTab");
4527 m_pDoc->SetString(ScAddress(0,0,0), "copy me");
4528 CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
4529 SCROW nRow1, nRow2;
4530 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4531 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4533 // Copy and test the result.
4534 m_pDoc->CopyTab(0, 1);
4535 CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
4536 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4537 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4538 m_pDoc->DeleteTab(1);
4540 m_pDoc->SetRowHidden(5, 10, 0, true);
4541 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4542 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4543 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
4544 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4545 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
4546 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4548 // Copy the sheet once again.
4549 m_pDoc->CopyTab(0, 1);
4550 CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
4551 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4552 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4553 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
4554 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4555 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
4556 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4557 m_pDoc->DeleteTab(1);
4558 m_pDoc->DeleteTab(0);
4561 void Test::testSheetMove()
4563 OUString aTabName("TestTab1");
4564 m_pDoc->InsertTab(0, aTabName);
4565 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount(), static_cast<SCTAB>(1));
4566 SCROW nRow1, nRow2;
4567 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4568 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4570 //test if inserting before another sheet works
4571 m_pDoc->InsertTab(0, OUString("TestTab2"));
4572 CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
4573 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4574 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4576 // Move and test the result.
4577 m_pDoc->MoveTab(0, 1);
4578 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
4579 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4580 CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4581 OUString aName;
4582 m_pDoc->GetName(0, aName);
4583 CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab1" );
4585 m_pDoc->SetRowHidden(5, 10, 0, true);
4586 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4587 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4588 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
4589 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4590 bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
4591 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4593 // Move the sheet once again.
4594 m_pDoc->MoveTab(1, 0);
4595 CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
4596 bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4597 CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4598 bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
4599 CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4600 bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
4601 CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4602 m_pDoc->GetName(0, aName);
4603 CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab2" );
4604 m_pDoc->DeleteTab(1);
4605 m_pDoc->DeleteTab(0);
4608 ScDocShell* findLoadedDocShellByName(const OUString& rName)
4610 TypeId aType(TYPE(ScDocShell));
4611 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
4612 while (pShell)
4614 SfxMedium* pMedium = pShell->GetMedium();
4615 if (pMedium)
4617 OUString aName = pMedium->GetName();
4618 if (aName.equals(rName))
4619 return pShell;
4621 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
4623 return NULL;
4626 ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
4628 ScRange aRange;
4630 vector<SCROW> aRows;
4631 pCacheTab->getAllRows(aRows);
4632 vector<SCROW>::const_iterator itrRow = aRows.begin(), itrRowEnd = aRows.end();
4633 bool bFirst = true;
4634 for (; itrRow != itrRowEnd; ++itrRow)
4636 SCROW nRow = *itrRow;
4637 vector<SCCOL> aCols;
4638 pCacheTab->getAllCols(nRow, aCols);
4639 vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end();
4640 for (; itrCol != itrColEnd; ++itrCol)
4642 SCCOL nCol = *itrCol;
4643 if (bFirst)
4645 aRange.aStart = ScAddress(nCol, nRow, 0);
4646 aRange.aEnd = aRange.aStart;
4647 bFirst = false;
4649 else
4651 if (nCol < aRange.aStart.Col())
4652 aRange.aStart.SetCol(nCol);
4653 else if (aRange.aEnd.Col() < nCol)
4654 aRange.aEnd.SetCol(nCol);
4656 if (nRow < aRange.aStart.Row())
4657 aRange.aStart.SetRow(nRow);
4658 else if (aRange.aEnd.Row() < nRow)
4659 aRange.aEnd.SetRow(nRow);
4663 return aRange;
4666 void Test::testExternalRef()
4668 ScDocShellRef xExtDocSh = new ScDocShell;
4669 OUString aExtDocName("file:///extdata.fake");
4670 OUString aExtSh1Name("Data1");
4671 OUString aExtSh2Name("Data2");
4672 OUString aExtSh3Name("Data3");
4673 SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
4674 xExtDocSh->DoInitNew(pMed);
4675 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
4676 findLoadedDocShellByName(aExtDocName) != NULL);
4678 // Populate the external source document.
4679 ScDocument* pExtDoc = xExtDocSh->GetDocument();
4680 pExtDoc->InsertTab(0, aExtSh1Name);
4681 pExtDoc->InsertTab(1, aExtSh2Name);
4682 pExtDoc->InsertTab(2, aExtSh3Name);
4684 OUString name("Name");
4685 OUString value("Value");
4686 OUString andy("Andy");
4687 OUString bruce("Bruce");
4688 OUString charlie("Charlie");
4689 OUString david("David");
4690 OUString edward("Edward");
4691 OUString frank("Frank");
4692 OUString george("George");
4693 OUString henry("Henry");
4695 // Sheet 1
4696 pExtDoc->SetString(0, 0, 0, name);
4697 pExtDoc->SetString(0, 1, 0, andy);
4698 pExtDoc->SetString(0, 2, 0, bruce);
4699 pExtDoc->SetString(0, 3, 0, charlie);
4700 pExtDoc->SetString(0, 4, 0, david);
4701 pExtDoc->SetString(1, 0, 0, value);
4702 double val = 10;
4703 pExtDoc->SetValue(1, 1, 0, val);
4704 val = 11;
4705 pExtDoc->SetValue(1, 2, 0, val);
4706 val = 12;
4707 pExtDoc->SetValue(1, 3, 0, val);
4708 val = 13;
4709 pExtDoc->SetValue(1, 4, 0, val);
4711 // Sheet 2 remains empty.
4713 // Sheet 3
4714 pExtDoc->SetString(0, 0, 2, name);
4715 pExtDoc->SetString(0, 1, 2, edward);
4716 pExtDoc->SetString(0, 2, 2, frank);
4717 pExtDoc->SetString(0, 3, 2, george);
4718 pExtDoc->SetString(0, 4, 2, henry);
4719 pExtDoc->SetString(1, 0, 2, value);
4720 val = 99;
4721 pExtDoc->SetValue(1, 1, 2, val);
4722 val = 98;
4723 pExtDoc->SetValue(1, 2, 2, val);
4724 val = 97;
4725 pExtDoc->SetValue(1, 3, 2, val);
4726 val = 96;
4727 pExtDoc->SetValue(1, 4, 2, val);
4729 // Test external refernces on the main document while the external
4730 // document is still in memory.
4731 m_pDoc->InsertTab(0, OUString("Test Sheet"));
4732 m_pDoc->SetString(0, 0, 0, OUString("='file:///extdata.fake'#Data1.A1"));
4733 OUString test = m_pDoc->GetString(0, 0, 0);
4734 CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(name));
4736 // After the initial access to the external document, the external ref
4737 // manager should create sheet cache entries for *all* sheets from that
4738 // document. Note that the doc may have more than 3 sheets but ensure
4739 // that the first 3 are what we expect.
4740 ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
4741 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
4742 vector<OUString> aTabNames;
4743 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
4744 CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
4745 CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[0].equals(aExtSh1Name));
4746 CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[1].equals(aExtSh2Name));
4747 CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[2].equals(aExtSh3Name));
4749 m_pDoc->SetString(1, 0, 0, OUString("='file:///extdata.fake'#Data1.B1"));
4750 test = m_pDoc->GetString(1, 0, 0);
4751 CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(value));
4753 m_pDoc->SetString(0, 1, 0, OUString("='file:///extdata.fake'#Data1.A2"));
4754 m_pDoc->SetString(0, 2, 0, OUString("='file:///extdata.fake'#Data1.A3"));
4755 m_pDoc->SetString(0, 3, 0, OUString("='file:///extdata.fake'#Data1.A4"));
4756 m_pDoc->SetString(0, 4, 0, OUString("='file:///extdata.fake'#Data1.A5"));
4757 m_pDoc->SetString(0, 5, 0, OUString("='file:///extdata.fake'#Data1.A6"));
4760 // Referencing an empty cell should display '0'.
4761 const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
4762 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4764 test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
4765 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
4768 m_pDoc->SetString(1, 1, 0, OUString("='file:///extdata.fake'#Data1.B2"));
4769 m_pDoc->SetString(1, 2, 0, OUString("='file:///extdata.fake'#Data1.B3"));
4770 m_pDoc->SetString(1, 3, 0, OUString("='file:///extdata.fake'#Data1.B4"));
4771 m_pDoc->SetString(1, 4, 0, OUString("='file:///extdata.fake'#Data1.B5"));
4772 m_pDoc->SetString(1, 5, 0, OUString("='file:///extdata.fake'#Data1.B6"));
4774 double pChecks[] = { 10, 11, 12, 13, 0 };
4775 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4777 m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
4778 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", val == pChecks[i]);
4782 m_pDoc->SetString(2, 0, 0, OUString("='file:///extdata.fake'#Data3.A1"));
4783 m_pDoc->SetString(2, 1, 0, OUString("='file:///extdata.fake'#Data3.A2"));
4784 m_pDoc->SetString(2, 2, 0, OUString("='file:///extdata.fake'#Data3.A3"));
4785 m_pDoc->SetString(2, 3, 0, OUString("='file:///extdata.fake'#Data3.A4"));
4787 const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
4788 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4790 test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
4791 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
4795 m_pDoc->SetString(3, 0, 0, OUString("='file:///extdata.fake'#Data3.B1"));
4796 m_pDoc->SetString(3, 1, 0, OUString("='file:///extdata.fake'#Data3.B2"));
4797 m_pDoc->SetString(3, 2, 0, OUString("='file:///extdata.fake'#Data3.B3"));
4798 m_pDoc->SetString(3, 3, 0, OUString("='file:///extdata.fake'#Data3.B4"));
4800 const char* pChecks[] = { "Value", "99", "98", "97" };
4801 for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4803 test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
4804 CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
4808 // At this point, all accessed cell data from the external document should
4809 // have been cached.
4810 ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
4811 nFileId, aExtSh1Name, false);
4812 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != NULL);
4813 ScRange aCachedRange = getCachedRange(pCacheTab);
4814 CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
4815 aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
4816 aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
4818 // Sheet2 is not referenced at all; the cache table shouldn't even exist.
4819 pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
4820 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", pCacheTab.get() == NULL);
4822 // Sheet3's row 5 is not referenced; it should not be cached.
4823 pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
4824 CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != NULL);
4825 aCachedRange = getCachedRange(pCacheTab);
4826 CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
4827 aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
4828 aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
4830 // Unload the external document shell.
4831 xExtDocSh->DoClose();
4832 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
4833 findLoadedDocShellByName(aExtDocName) == NULL);
4835 m_pDoc->DeleteTab(0);
4838 void testExtRefFuncT(ScDocument* pDoc, ScDocument* pExtDoc)
4840 clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
4841 clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
4843 pExtDoc->SetString(0, 0, 0, OUString("'1.2"));
4844 pExtDoc->SetString(0, 1, 0, OUString("Foo"));
4845 pExtDoc->SetValue(0, 2, 0, 12.3);
4846 pDoc->SetString(0, 0, 0, OUString("=T('file:///extdata.fake'#Data.A1)"));
4847 pDoc->SetString(0, 1, 0, OUString("=T('file:///extdata.fake'#Data.A2)"));
4848 pDoc->SetString(0, 2, 0, OUString("=T('file:///extdata.fake'#Data.A3)"));
4849 pDoc->CalcAll();
4851 OUString aRes = pDoc->GetString(0, 0, 0);
4852 CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "1.2" );
4853 aRes = pDoc->GetString(0, 1, 0);
4854 CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "Foo" );
4855 aRes = pDoc->GetString(0, 2, 0);
4856 CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
4859 void Test::testExternalRefFunctions()
4861 ScDocShellRef xExtDocSh = new ScDocShell;
4862 OUString aExtDocName("file:///extdata.fake");
4863 SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
4864 xExtDocSh->DoInitNew(pMed);
4865 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
4866 findLoadedDocShellByName(aExtDocName) != NULL);
4868 ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
4869 CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
4870 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
4871 const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
4872 CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
4873 pFileName && pFileName->equals(aExtDocName));
4875 // Populate the external source document.
4876 ScDocument* pExtDoc = xExtDocSh->GetDocument();
4877 pExtDoc->InsertTab(0, OUString("Data"));
4878 double val = 1;
4879 pExtDoc->SetValue(0, 0, 0, val);
4880 // leave cell B1 empty.
4881 val = 2;
4882 pExtDoc->SetValue(0, 1, 0, val);
4883 pExtDoc->SetValue(1, 1, 0, val);
4884 val = 3;
4885 pExtDoc->SetValue(0, 2, 0, val);
4886 pExtDoc->SetValue(1, 2, 0, val);
4887 val = 4;
4888 pExtDoc->SetValue(0, 3, 0, val);
4889 pExtDoc->SetValue(1, 3, 0, val);
4891 m_pDoc->InsertTab(0, OUString("Test"));
4893 struct {
4894 const char* pFormula; double fResult;
4895 } aChecks[] = {
4896 { "=SUM('file:///extdata.fake'#Data.A1:A4)", 10 },
4897 { "=SUM('file:///extdata.fake'#Data.B1:B4)", 9 },
4898 { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
4899 { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
4900 { "=COUNT('file:///extdata.fake'#Data.A1:A4)", 4 },
4901 { "=COUNT('file:///extdata.fake'#Data.B1:B4)", 3 }
4904 for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
4906 m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
4907 m_pDoc->CalcAll();
4908 m_pDoc->GetValue(0, 0, 0, val);
4909 CPPUNIT_ASSERT_MESSAGE("unexpected result involving external ranges.", val == aChecks[i].fResult);
4912 pRefMgr->clearCache(nFileId);
4913 testExtRefFuncT(m_pDoc, pExtDoc);
4915 // Unload the external document shell.
4916 xExtDocSh->DoClose();
4917 CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
4918 findLoadedDocShellByName(aExtDocName) == NULL);
4920 m_pDoc->DeleteTab(0);
4923 void Test::testDataArea()
4925 m_pDoc->InsertTab(0, OUString("Data"));
4927 // Totally empty sheet should be rightfully considered empty in all accounts.
4928 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
4929 CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
4931 // Now, set borders in some cells....
4932 ::editeng::SvxBorderLine aLine(NULL, 50, table::BorderLineStyle::SOLID);
4933 SvxBoxItem aBorderItem(ATTR_BORDER);
4934 aBorderItem.SetLine(&aLine, BOX_LINE_LEFT);
4935 aBorderItem.SetLine(&aLine, BOX_LINE_RIGHT);
4936 for (SCROW i = 0; i < 100; ++i)
4937 // Set borders from row 1 to 100.
4938 m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
4940 // Now the sheet is considered non-empty for printing purposes, but still
4941 // be empty in all the other cases.
4942 CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
4943 !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
4944 CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
4945 m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
4947 // Adding a real cell content should turn the block non-empty.
4948 m_pDoc->SetString(0, 0, 0, OUString("Some text"));
4949 CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
4950 !m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
4952 // TODO: Add more tests for normal data area calculation.
4954 m_pDoc->DeleteTab(0);
4957 void Test::testStreamValid()
4959 m_pDoc->InsertTab(0, OUString("Sheet1"));
4960 m_pDoc->InsertTab(1, OUString("Sheet2"));
4961 m_pDoc->InsertTab(2, OUString("Sheet3"));
4962 m_pDoc->InsertTab(3, OUString("Sheet4"));
4963 CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", m_pDoc->GetTableCount(), static_cast<SCTAB>(4));
4965 OUString a1("A1");
4966 OUString a2("A2");
4967 OUString test;
4969 // Put values into Sheet1.
4970 m_pDoc->SetString(0, 0, 0, a1);
4971 m_pDoc->SetString(0, 1, 0, a2);
4972 test = m_pDoc->GetString(0, 0, 0);
4973 CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A1", test.equals(a1));
4974 test = m_pDoc->GetString(0, 1, 0);
4975 CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A2", test.equals(a2));
4977 // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
4978 m_pDoc->SetString(0, 0, 1, OUString("=Sheet1.A1"));
4979 m_pDoc->SetString(0, 1, 1, OUString("=Sheet1.A2"));
4980 m_pDoc->SetString(0, 0, 2, OUString("=Sheet1.A1"));
4981 m_pDoc->SetString(0, 0, 3, OUString("=Sheet1.A2"));
4983 test = m_pDoc->GetString(0, 0, 1);
4984 CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A1", test.equals(a1));
4985 test = m_pDoc->GetString(0, 1, 1);
4986 CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A2", test.equals(a2));
4987 test = m_pDoc->GetString(0, 0, 2);
4988 CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a1));
4989 test = m_pDoc->GetString(0, 0, 3);
4990 CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a2));
4992 // Set all sheet streams valid after all the initial cell values are in
4993 // place. In reality we need to have real XML streams stored in order to
4994 // claim they are valid, but we are just testing the flag values here.
4995 m_pDoc->SetStreamValid(0, true);
4996 m_pDoc->SetStreamValid(1, true);
4997 m_pDoc->SetStreamValid(2, true);
4998 m_pDoc->SetStreamValid(3, true);
4999 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
5000 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
5001 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
5002 CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
5004 // Now, insert a new row at row 2 position on Sheet1. This will move cell
5005 // A2 downward but cell A1 remains unmoved.
5006 m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 2);
5007 test = m_pDoc->GetString(0, 0, 0);
5008 CPPUNIT_ASSERT_MESSAGE("Cell A1 should not have moved.", test.equals(a1));
5009 test = m_pDoc->GetString(0, 3, 0);
5010 CPPUNIT_ASSERT_MESSAGE("the old cell A2 should now be at A4.", test.equals(a2));
5011 ScRefCellValue aCell;
5012 aCell.assign(*m_pDoc, ScAddress(0,1,0));
5013 CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
5014 aCell.assign(*m_pDoc, ScAddress(0,2,0));
5015 CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
5017 // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
5018 // invalidated, whereas Sheet3's stream should still be valid.
5019 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
5020 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
5021 CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
5022 CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
5024 m_pDoc->DeleteTab(3);
5025 m_pDoc->DeleteTab(2);
5026 m_pDoc->DeleteTab(1);
5027 m_pDoc->DeleteTab(0);
5030 void Test::testFunctionLists()
5032 const char* aDataBase[] = {
5033 "DAVERAGE",
5034 "DCOUNT",
5035 "DCOUNTA",
5036 "DGET",
5037 "DMAX",
5038 "DMIN",
5039 "DPRODUCT",
5040 "DSTDEV",
5041 "DSTDEVP",
5042 "DSUM",
5043 "DVAR",
5044 "DVARP",
5048 const char* aDateTime[] = {
5049 "DATE",
5050 "DATEDIF",
5051 "DATEVALUE",
5052 "DAY",
5053 "DAYS",
5054 "DAYS360",
5055 "EASTERSUNDAY",
5056 "HOUR",
5057 "MINUTE",
5058 "MONTH",
5059 "NOW",
5060 "SECOND",
5061 "TIME",
5062 "TIMEVALUE",
5063 "TODAY",
5064 "WEEKDAY",
5065 "WEEKNUM",
5066 "YEAR",
5070 const char* aFinancial[] = {
5071 "CUMIPMT",
5072 "CUMPRINC",
5073 "DB",
5074 "DDB",
5075 "DURATION",
5076 "EFFECTIVE",
5077 "FV",
5078 "IPMT",
5079 "IRR",
5080 "ISPMT",
5081 "MIRR",
5082 "NOMINAL",
5083 "NPER",
5084 "NPV",
5085 "PMT",
5086 "PPMT",
5087 "PV",
5088 "RATE",
5089 "RRI",
5090 "SLN",
5091 "SYD",
5092 "VDB",
5096 const char* aInformation[] = {
5097 "CELL",
5098 "CURRENT",
5099 "FORMULA",
5100 "INFO",
5101 "ISBLANK",
5102 "ISERR",
5103 "ISERROR",
5104 "ISFORMULA",
5105 "ISLOGICAL",
5106 "ISNA",
5107 "ISNONTEXT",
5108 "ISNUMBER",
5109 "ISREF",
5110 "ISTEXT",
5111 "N",
5112 "NA",
5113 "TYPE",
5117 const char* aLogical[] = {
5118 "AND",
5119 "FALSE",
5120 "IF",
5121 "IFERROR",
5122 "IFNA",
5123 "NOT",
5124 "OR",
5125 "TRUE",
5126 "XOR",
5130 const char* aMathematical[] = {
5131 "ABS",
5132 "ACOS",
5133 "ACOSH",
5134 "ACOT",
5135 "ACOTH",
5136 "ASIN",
5137 "ASINH",
5138 "ATAN",
5139 "ATAN2",
5140 "ATANH",
5141 "AVERAGEIF",
5142 "AVERAGEIFS",
5143 "BITAND",
5144 "BITLSHIFT",
5145 "BITOR",
5146 "BITRSHIFT",
5147 "BITXOR",
5148 "CEILING",
5149 "COMBIN",
5150 "COMBINA",
5151 "CONVERT",
5152 "COS",
5153 "COSH",
5154 "COT",
5155 "COTH",
5156 "COUNTBLANK",
5157 "COUNTIF",
5158 "COUNTIFS",
5159 "CSC",
5160 "CSCH",
5161 "DEGREES",
5162 "EUROCONVERT",
5163 "EVEN",
5164 "EXP",
5165 "FACT",
5166 "FLOOR",
5167 "GCD",
5168 "INT",
5169 "ISEVEN",
5170 "ISODD",
5171 "LCM",
5172 "LN",
5173 "LOG",
5174 "LOG10",
5175 "MOD",
5176 "ODD",
5177 "PI",
5178 "POWER",
5179 "PRODUCT",
5180 "RADIANS",
5181 "RAND",
5182 "ROUND",
5183 "ROUNDDOWN",
5184 "ROUNDUP",
5185 "SEC",
5186 "SECH",
5187 "SIGN",
5188 "SIN",
5189 "SINH",
5190 "SQRT",
5191 "SUBTOTAL",
5192 "SUM",
5193 "SUMIF",
5194 "SUMIFS",
5195 "SUMSQ",
5196 "TAN",
5197 "TANH",
5198 "TRUNC",
5202 const char* aArray[] = {
5203 "FREQUENCY",
5204 "GROWTH",
5205 "LINEST",
5206 "LOGEST",
5207 "MDETERM",
5208 "MINVERSE",
5209 "MMULT",
5210 "MUNIT",
5211 "SUMPRODUCT",
5212 "SUMX2MY2",
5213 "SUMX2PY2",
5214 "SUMXMY2",
5215 "TRANSPOSE",
5216 "TREND",
5220 const char* aStatistical[] = {
5221 "AVEDEV",
5222 "AVERAGE",
5223 "AVERAGEA",
5224 "B",
5225 "BETADIST",
5226 "BETAINV",
5227 "BINOMDIST",
5228 "CHIDIST",
5229 "CHIINV",
5230 "CHISQDIST",
5231 "CHISQINV",
5232 "CHITEST",
5233 "CONFIDENCE",
5234 "CORREL",
5235 "COUNT",
5236 "COUNTA",
5237 "COVAR",
5238 "CRITBINOM",
5239 "DEVSQ",
5240 "EXPONDIST",
5241 "FDIST",
5242 "FINV",
5243 "FISHER",
5244 "FISHERINV",
5245 "FORECAST",
5246 "FTEST",
5247 "GAMMA",
5248 "GAMMADIST",
5249 "GAMMAINV",
5250 "GAMMALN",
5251 "GAUSS",
5252 "GEOMEAN",
5253 "HARMEAN",
5254 "HYPGEOMDIST",
5255 "INTERCEPT",
5256 "KURT",
5257 "LARGE",
5258 "LOGINV",
5259 "LOGNORMDIST",
5260 "MAX",
5261 "MAXA",
5262 "MEDIAN",
5263 "MIN",
5264 "MINA",
5265 "MODE",
5266 "NEGBINOMDIST",
5267 "NORMDIST",
5268 "NORMINV",
5269 "NORMSDIST",
5270 "NORMSINV",
5271 "PEARSON",
5272 "PERCENTILE",
5273 "PERCENTRANK",
5274 "PERMUT",
5275 "PERMUTATIONA",
5276 "PHI",
5277 "POISSON",
5278 "PROB",
5279 "QUARTILE",
5280 "RANK",
5281 "RSQ",
5282 "SKEW",
5283 "SKEWP",
5284 "SLOPE",
5285 "SMALL",
5286 "STANDARDIZE",
5287 "STDEV",
5288 "STDEVA",
5289 "STDEVP",
5290 "STDEVPA",
5291 "STEYX",
5292 "TDIST",
5293 "TINV",
5294 "TRIMMEAN",
5295 "TTEST",
5296 "VAR",
5297 "VARA",
5298 "VARP",
5299 "VARPA",
5300 "WEIBULL",
5301 "ZTEST",
5305 const char* aSpreadsheet[] = {
5306 "ADDRESS",
5307 "AREAS",
5308 "CHOOSE",
5309 "COLUMN",
5310 "COLUMNS",
5311 "DDE",
5312 "ERRORTYPE",
5313 "GETPIVOTDATA",
5314 "HLOOKUP",
5315 "HYPERLINK",
5316 "INDEX",
5317 "INDIRECT",
5318 "LOOKUP",
5319 "MATCH",
5320 "OFFSET",
5321 "ROW",
5322 "ROWS",
5323 "SHEET",
5324 "SHEETS",
5325 "STYLE",
5326 "VLOOKUP",
5330 const char* aText[] = {
5331 "ARABIC",
5332 "ASC",
5333 "BAHTTEXT",
5334 "BASE",
5335 "CHAR",
5336 "CLEAN",
5337 "CODE",
5338 "CONCATENATE",
5339 "DECIMAL",
5340 "DOLLAR",
5341 "EXACT",
5342 "FIND",
5343 "FIXED",
5344 "JIS",
5345 "LEFT",
5346 "LEN",
5347 "LOWER",
5348 "MID",
5349 "NUMBERVALUE",
5350 "PROPER",
5351 "REPLACE",
5352 "REPT",
5353 "RIGHT",
5354 "ROMAN",
5355 "SEARCH",
5356 "SUBSTITUTE",
5357 "T",
5358 "TEXT",
5359 "TRIM",
5360 "UNICHAR",
5361 "UNICODE",
5362 "UPPER",
5363 "VALUE",
5367 struct {
5368 const char* Category; const char** Functions;
5369 } aTests[] = {
5370 { "Database", aDataBase },
5371 { "Date&Time", aDateTime },
5372 { "Financial", aFinancial },
5373 { "Information", aInformation },
5374 { "Logical", aLogical },
5375 { "Mathematical", aMathematical },
5376 { "Array", aArray },
5377 { "Statistical", aStatistical },
5378 { "Spreadsheet", aSpreadsheet },
5379 { "Text", aText },
5380 { "Add-in", 0 },
5381 { 0, 0 }
5384 ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
5385 sal_uInt32 n = pFuncMgr->getCount();
5386 for (sal_uInt32 i = 0; i < n; ++i)
5388 const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
5389 CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
5390 sal_uInt32 nFuncCount = pCat->getCount();
5391 for (sal_uInt32 j = 0; j < nFuncCount; ++j)
5393 const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
5394 CPPUNIT_ASSERT_MESSAGE("Unexpected function name", pFunc->getFunctionName().equalsAscii(aTests[i].Functions[j]));
5399 void Test::testGraphicsInGroup()
5401 OUString aTabName("TestTab");
5402 m_pDoc->InsertTab(0, aTabName);
5403 CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
5404 SCROW nRow1, nRow2;
5405 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5406 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
5408 m_pDoc->InitDrawLayer();
5409 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5410 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
5411 SdrPage* pPage = pDrawLayer->GetPage(0);
5412 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
5415 //Add a square
5416 Rectangle aOrigRect(2,2,100,100);
5417 SdrRectObj *pObj = new SdrRectObj(aOrigRect);
5418 pPage->InsertObject(pObj);
5419 const Rectangle &rNewRect = pObj->GetLogicRect();
5420 CPPUNIT_ASSERT_MESSAGE("must have equal position and size", aOrigRect == rNewRect);
5422 ScDrawLayer::SetPageAnchored(*pObj);
5424 //Use a range of rows guaranteed to include all of the square
5425 m_pDoc->ShowRows(0, 100, 0, false);
5426 m_pDoc->SetDrawPageSize(0);
5427 CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
5428 m_pDoc->ShowRows(0, 100, 0, true);
5429 m_pDoc->SetDrawPageSize(0);
5430 CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
5432 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5433 CPPUNIT_ASSERT_MESSAGE("That shouldn't change size or positioning", aOrigRect == rNewRect);
5435 m_pDoc->ShowRows(0, 100, 0, false);
5436 m_pDoc->SetDrawPageSize(0);
5437 CPPUNIT_ASSERT_MESSAGE("Left and Right should be unchanged",
5438 aOrigRect.Left() == rNewRect.Left() && aOrigRect.Right() == rNewRect.Right());
5439 CPPUNIT_ASSERT_MESSAGE("Height should be minimum allowed height",
5440 (rNewRect.Bottom() - rNewRect.Top()) <= 1);
5441 m_pDoc->ShowRows(0, 100, 0, true);
5442 m_pDoc->SetDrawPageSize(0);
5443 CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
5447 // Add a circle.
5448 Rectangle aOrigRect = Rectangle(10,10,210,210); // 200 x 200
5449 SdrCircObj* pObj = new SdrCircObj(OBJ_CIRC, aOrigRect);
5450 pPage->InsertObject(pObj);
5451 const Rectangle& rNewRect = pObj->GetLogicRect();
5452 CPPUNIT_ASSERT_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
5453 aOrigRect == rNewRect);
5455 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5456 CPPUNIT_ASSERT_MESSAGE("Size changed when cell anchored. Not good.",
5457 aOrigRect == rNewRect);
5459 // Insert 2 rows at the top. This should push the circle object down.
5460 m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
5461 m_pDoc->SetDrawPageSize(0);
5463 // Make sure the size of the circle is still identical.
5464 CPPUNIT_ASSERT_MESSAGE("Size of the circle has changed, but shouldn't!",
5465 aOrigRect.GetSize() == rNewRect.GetSize());
5467 // Delete 2 rows at the top. This should bring the circle object to its original position.
5468 m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
5469 m_pDoc->SetDrawPageSize(0);
5470 CPPUNIT_ASSERT_MESSAGE("Failed to move back to its original position.", aOrigRect == rNewRect);
5474 // Add a line.
5475 basegfx::B2DPolygon aTempPoly;
5476 Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
5477 Rectangle aOrigRect(10,200,110,300); // 100 x 100
5478 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
5479 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
5480 SdrPathObj* pObj = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
5481 pObj->NbcSetLogicRect(aOrigRect);
5482 pPage->InsertObject(pObj);
5483 const Rectangle& rNewRect = pObj->GetLogicRect();
5484 CPPUNIT_ASSERT_MESSAGE("Size differ.", aOrigRect == rNewRect);
5486 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5487 CPPUNIT_ASSERT_MESSAGE("Size changed when cell-anchored. Not good.",
5488 aOrigRect == rNewRect);
5490 // Insert 2 rows at the top and delete them immediately.
5491 m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
5492 m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
5493 m_pDoc->SetDrawPageSize(0);
5494 CPPUNIT_ASSERT_MESSAGE("Size of a line object changed after row insertion and removal.",
5495 aOrigRect == rNewRect);
5497 sal_Int32 n = pObj->GetPointCount();
5498 CPPUNIT_ASSERT_MESSAGE("There should be exactly 2 points in a line object.", n == 2);
5499 CPPUNIT_ASSERT_MESSAGE("Line shape has changed.",
5500 aStartPos == pObj->GetPoint(0) && aEndPos == pObj->GetPoint(1));
5503 m_pDoc->DeleteTab(0);
5506 void Test::testGraphicsOnSheetMove()
5508 m_pDoc->InsertTab(0, OUString("Tab1"));
5509 m_pDoc->InsertTab(1, OUString("Tab2"));
5510 CPPUNIT_ASSERT_MESSAGE("There should be only 2 sheets to begin with", m_pDoc->GetTableCount() == 2);
5512 m_pDoc->InitDrawLayer();
5513 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
5514 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
5515 SdrPage* pPage = pDrawLayer->GetPage(0);
5516 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
5518 // Insert an object.
5519 Rectangle aObjRect(2,2,100,100);
5520 SdrObject* pObj = new SdrRectObj(aObjRect);
5521 pPage->InsertObject(pObj);
5522 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5524 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(1));
5526 const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
5527 CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
5528 CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
5530 pPage = pDrawLayer->GetPage(1);
5531 CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
5532 CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(0));
5534 // Insert a new sheet at left-end, and make sure the object has moved to
5535 // the 2nd page.
5536 m_pDoc->InsertTab(0, OUString("NewTab"));
5537 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(3));
5538 pPage = pDrawLayer->GetPage(0);
5539 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
5540 pPage = pDrawLayer->GetPage(1);
5541 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5542 pPage = pDrawLayer->GetPage(2);
5543 CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage && pPage->GetObjCount() == 0);
5545 CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
5547 // Now, delete the sheet that just got inserted. The object should be back
5548 // on the 1st sheet.
5549 m_pDoc->DeleteTab(0);
5550 pPage = pDrawLayer->GetPage(0);
5551 CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5552 CPPUNIT_ASSERT_MESSAGE("Size and position of the object shouldn't change.",
5553 pObj->GetLogicRect() == aObjRect);
5555 CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
5557 // Move the 1st sheet to the last position.
5558 m_pDoc->MoveTab(0, 1);
5559 pPage = pDrawLayer->GetPage(0);
5560 CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
5561 pPage = pDrawLayer->GetPage(1);
5562 CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5563 CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
5565 // Copy the 2nd sheet, which has one drawing object to the last position.
5566 m_pDoc->CopyTab(1, 2);
5567 pPage = pDrawLayer->GetPage(2);
5568 CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5569 pObj = pPage->GetObj(0);
5570 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
5571 pData = ScDrawLayer::GetObjData(pObj);
5572 CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5573 CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 2 && pData->maEnd.Tab() == 2);
5575 m_pDoc->DeleteTab(2);
5576 m_pDoc->DeleteTab(1);
5577 m_pDoc->DeleteTab(0);
5580 void Test::testPostIts()
5582 OUString aHello("Hello world");
5583 OUString aJimBob("Jim Bob");
5584 OUString aTabName("PostIts");
5585 OUString aTabName2("Table2");
5586 m_pDoc->InsertTab(0, aTabName);
5588 ScAddress rAddr(2, 2, 0); // cell C3
5589 ScPostIt *pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
5590 pNote->SetText(rAddr, aHello);
5591 pNote->SetAuthor(aJimBob);
5593 ScPostIt *pGetNote = m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr);
5594 CPPUNIT_ASSERT_MESSAGE("note should be itself", pGetNote == pNote );
5596 // Insert one row at row 1.
5597 bool bInsertRow = m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 1);
5598 CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
5600 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
5601 rAddr.IncRow(); // cell C4
5602 CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5604 // Insert column at column A.
5605 bool bInsertCol = m_pDoc->InsertCol(0, 0, MAXROW, 0, 1, 1);
5606 CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
5608 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
5609 rAddr.IncCol(); // cell D4
5610 CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5612 // Insert a new sheet to shift the current sheet to the right.
5613 m_pDoc->InsertTab(0, aTabName2);
5614 CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
5615 rAddr.IncTab(); // Move to the next sheet.
5616 CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5618 m_pDoc->DeleteTab(0);
5619 rAddr.IncTab(-1);
5620 CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5622 // Insert cell at C4. This should NOT shift the note position.
5623 bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
5624 CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
5625 CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5627 // Delete cell at C4. Again, this should NOT shift the note position.
5628 m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
5629 CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5631 // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
5632 m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
5633 rAddr.IncRow(-1); // cell D3
5634 CPPUNIT_ASSERT_MESSAGE("Note at D4 should have shifted up to D3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5636 // Delete column C. This should shift the note one cell left.
5637 m_pDoc->DeleteCol(0, 0, MAXROW, 0, 2, 1);
5638 rAddr.IncCol(-1); // cell C3
5639 CPPUNIT_ASSERT_MESSAGE("Note at D3 should have shifted left to C3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5641 m_pDoc->DeleteTab(0);
5644 void Test::testToggleRefFlag()
5646 // In this test, there is no need to insert formula string into a cell in
5647 // the document, as ScRefFinder does not depend on the content of the
5648 // document except for the sheet names.
5650 OUString aTabName("Test");
5651 m_pDoc->InsertTab(0, aTabName);
5654 // Calc A1: basic 2D reference
5656 OUString aFormula("=B100");
5657 ScAddress aPos(1, 5, 0);
5658 ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
5660 // Original
5661 CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
5663 // column relative / row relative -> column absolute / row absolute
5664 aFinder.ToggleRel(0, aFormula.getLength());
5665 aFormula = aFinder.GetText();
5666 CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B$100" );
5668 // column absolute / row absolute -> column relative / row absolute
5669 aFinder.ToggleRel(0, aFormula.getLength());
5670 aFormula = aFinder.GetText();
5671 CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B$100" );
5673 // column relative / row absolute -> column absolute / row relative
5674 aFinder.ToggleRel(0, aFormula.getLength());
5675 aFormula = aFinder.GetText();
5676 CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B100" );
5678 // column absolute / row relative -> column relative / row relative
5679 aFinder.ToggleRel(0, aFormula.getLength());
5680 aFormula = aFinder.GetText();
5681 CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B100" );
5685 // Excel R1C1: basic 2D reference
5687 OUString aFormula("=R2C1");
5688 ScAddress aPos(3, 5, 0);
5689 ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
5691 // Original
5692 CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
5694 // column absolute / row absolute -> column relative / row absolute
5695 aFinder.ToggleRel(0, aFormula.getLength());
5696 aFormula = aFinder.GetText();
5697 CPPUNIT_ASSERT_EQUAL(OUString("=R2C[-3]"), aFormula);
5699 // column relative / row absolute - > column absolute / row relative
5700 aFinder.ToggleRel(0, aFormula.getLength());
5701 aFormula = aFinder.GetText();
5702 CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C1"), aFormula);
5704 // column absolute / row relative -> column relative / row relative
5705 aFinder.ToggleRel(0, aFormula.getLength());
5706 aFormula = aFinder.GetText();
5707 CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C[-3]"), aFormula);
5709 // column relative / row relative -> column absolute / row absolute
5710 aFinder.ToggleRel(0, aFormula.getLength());
5711 aFormula = aFinder.GetText();
5712 CPPUNIT_ASSERT_EQUAL(OUString("=R2C1"), aFormula);
5716 // Excel R1C1: Selection at the end of the formula string and does not
5717 // overlap the formula string at all (inspired by fdo#39135).
5718 OUString aFormula("=R1C1");
5719 ScAddress aPos(1, 1, 0);
5720 ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
5722 // Original
5723 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
5725 // Make the column relative.
5726 sal_Int32 n = aFormula.getLength();
5727 aFinder.ToggleRel(n, n);
5728 aFormula = aFinder.GetText();
5729 CPPUNIT_ASSERT_EQUAL(OUString("=R1C[-1]"), aFormula);
5731 // Make the row relative.
5732 n = aFormula.getLength();
5733 aFinder.ToggleRel(n, n);
5734 aFormula = aFinder.GetText();
5735 CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C1"), aFormula);
5737 // Make both relative.
5738 n = aFormula.getLength();
5739 aFinder.ToggleRel(n, n);
5740 aFormula = aFinder.GetText();
5741 CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C[-1]"), aFormula);
5743 // Back to the original.
5744 n = aFormula.getLength();
5745 aFinder.ToggleRel(n, n);
5746 aFormula = aFinder.GetText();
5747 CPPUNIT_ASSERT_EQUAL(OUString("=R1C1"), aFormula);
5751 // Calc A1:
5752 OUString aFormula("=A1+4");
5753 ScAddress aPos(1, 1, 0);
5754 ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
5756 // Original
5757 CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
5759 // Set the cursor over the 'A1' part and toggle.
5760 aFinder.ToggleRel(2, 2);
5761 aFormula = aFinder.GetText();
5762 CPPUNIT_ASSERT_EQUAL(OUString("=$A$1+4"), aFormula);
5764 aFinder.ToggleRel(2, 2);
5765 aFormula = aFinder.GetText();
5766 CPPUNIT_ASSERT_EQUAL(OUString("=A$1+4"), aFormula);
5768 aFinder.ToggleRel(2, 2);
5769 aFormula = aFinder.GetText();
5770 CPPUNIT_ASSERT_EQUAL(OUString("=$A1+4"), aFormula);
5772 aFinder.ToggleRel(2, 2);
5773 aFormula = aFinder.GetText();
5774 CPPUNIT_ASSERT_EQUAL(OUString("=A1+4"), aFormula);
5777 // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
5778 // partial selection within formula string.
5780 m_pDoc->DeleteTab(0);
5783 void Test::testAutofilter()
5785 OUString aTabName("Test");
5786 OUString aDBName("NONAME");
5788 m_pDoc->InsertTab( 0, aTabName );
5790 // cell contents (0 = empty cell)
5791 const char* aData[][3] = {
5792 { "C1", "C2", "C3" },
5793 { "0", "1", "A" },
5794 { "1", "2", 0 },
5795 { "1", "2", "B" },
5796 { "0", "2", "B" }
5799 SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
5800 SCROW nRows = SAL_N_ELEMENTS(aData);
5802 // Populate cells.
5803 for (SCROW i = 0; i < nRows; ++i)
5804 for (SCCOL j = 0; j < nCols; ++j)
5805 if (aData[i][j])
5806 m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
5808 ScDBData* pDBData = new ScDBData(aDBName, 0, 0, 0, nCols-1, nRows-1);
5809 m_pDoc->SetAnonymousDBData(0,pDBData);
5811 pDBData->SetAutoFilter(true);
5812 ScRange aRange;
5813 pDBData->GetArea(aRange);
5814 m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
5815 aRange.aEnd.Col(), aRange.aStart.Row(),
5816 aRange.aStart.Tab(), SC_MF_AUTO);
5818 //create the query param
5819 ScQueryParam aParam;
5820 pDBData->GetQueryParam(aParam);
5821 ScQueryEntry& rEntry = aParam.GetEntry(0);
5822 rEntry.bDoQuery = true;
5823 rEntry.nField = 0;
5824 rEntry.eOp = SC_EQUAL;
5825 rEntry.GetQueryItem().mfVal = 0;
5826 // add queryParam to database range.
5827 pDBData->SetQueryParam(aParam);
5829 // perform the query.
5830 m_pDoc->Query(0, aParam, true);
5832 //control output
5833 SCROW nRow1, nRow2;
5834 bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
5835 CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden && nRow1 == 2 && nRow2 == 3);
5837 // Remove filtering.
5838 rEntry.Clear();
5839 m_pDoc->Query(0, aParam, true);
5840 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5841 CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
5843 // Filter for non-empty cells by column C.
5844 rEntry.bDoQuery = true;
5845 rEntry.nField = 2;
5846 rEntry.SetQueryByNonEmpty();
5847 m_pDoc->Query(0, aParam, true);
5849 // only row 3 should be hidden. The rest should be visible.
5850 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5851 CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 1);
5852 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
5853 CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden && nRow1 == 2 && nRow2 == 2);
5854 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
5855 CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden && nRow1 == 3 && nRow2 == MAXROW);
5857 // Now, filter for empty cells by column C.
5858 rEntry.SetQueryByEmpty();
5859 m_pDoc->Query(0, aParam, true);
5861 // Now, only row 1 and 3, and 6 and down should be visible.
5862 bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5863 CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 0);
5864 bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
5865 CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden && nRow1 == 1 && nRow2 == 1);
5866 bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
5867 CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden && nRow1 == 2 && nRow2 == 2);
5868 bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
5869 CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden && nRow1 == 3 && nRow2 == 4);
5870 bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
5871 CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden && nRow1 == 5 && nRow2 == MAXROW);
5873 m_pDoc->DeleteTab(0);
5876 void Test::testCopyPaste()
5878 m_pDoc->InsertTab(0, OUString("Sheet1"));
5879 m_pDoc->InsertTab(1, OUString("Sheet2"));
5880 //test copy&paste + ScUndoPaste
5881 //copy local and global range names in formulas
5882 //string cells and value cells
5883 m_pDoc->SetValue(0, 0, 0, 1);
5884 m_pDoc->SetValue(3, 0, 0, 0);
5885 m_pDoc->SetValue(3, 1, 0, 1);
5886 m_pDoc->SetValue(3, 2, 0, 2);
5887 m_pDoc->SetValue(3, 3, 0, 3);
5888 m_pDoc->SetString(2, 0, 0, OUString("test"));
5889 ScAddress aAdr (0, 0, 0);
5891 //create some range names, local and global
5892 ScRangeData* pLocal1 = new ScRangeData(m_pDoc, OUString("local1"), aAdr);
5893 ScRangeData* pLocal2 = new ScRangeData(m_pDoc, OUString("local2"), aAdr);
5894 ScRangeData* pGlobal = new ScRangeData(m_pDoc, OUString("global"), aAdr);
5895 ScRangeName* pGlobalRangeName = new ScRangeName();
5896 pGlobalRangeName->insert(pGlobal);
5897 ScRangeName* pLocalRangeName1 = new ScRangeName();
5898 pLocalRangeName1->insert(pLocal1);
5899 pLocalRangeName1->insert(pLocal2);
5900 m_pDoc->SetRangeName(pGlobalRangeName);
5901 m_pDoc->SetRangeName(0, pLocalRangeName1);
5903 //add formula
5904 OUString aFormulaString("=local1+global+SUM($C$1:$D$4)");
5905 m_pDoc->SetString(1, 0, 0, aFormulaString);
5907 double aValue = 0;
5908 m_pDoc->GetValue(1, 0, 0, aValue);
5909 std::cout << "Value: " << aValue << std::endl;
5910 ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 8", aValue, 8);
5912 //copy Sheet1.A1:C1 to Sheet2.A2:C2
5913 ScRange aRange(0,0,0,2,0,0);
5914 ScClipParam aClipParam(aRange, false);
5915 ScMarkData aMark;
5916 aMark.SetMarkArea(aRange);
5917 ScDocument aClipDoc(SCDOCMODE_CLIP);
5918 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
5920 sal_uInt16 nFlags = IDF_ALL;
5921 aRange = ScRange(0,1,1,2,1,1);//target: Sheet2.A2:C2
5922 ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
5923 pUndoDoc->InitUndo(m_pDoc, 1, 1, true, true);
5924 ScMarkData aMarkData2;
5925 aMarkData2.SetMarkArea(aRange);
5926 ScRefUndoData* pRefUndoData= new ScRefUndoData(m_pDoc);
5927 ScUndoPaste aUndo(
5928 &m_xDocShRef, ScRange(0, 1, 1, 2, 1, 1), aMarkData2, pUndoDoc, NULL, IDF_ALL, pRefUndoData, false);
5929 m_pDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, &aClipDoc);
5931 //check values after copying
5932 OUString aString;
5933 m_pDoc->GetValue(1,1,1, aValue);
5934 m_pDoc->GetFormula(1,1,1, aString);
5935 ASSERT_DOUBLES_EQUAL_MESSAGE("copied formula should return 2", aValue, 2);
5936 CPPUNIT_ASSERT_MESSAGE("formula string was not copied correctly", aString == aFormulaString);
5937 m_pDoc->GetValue(0,1,1, aValue);
5938 CPPUNIT_ASSERT_MESSAGE("copied value should be 1", aValue == 1);
5940 //chack local range name after copying
5941 pLocal1 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL1"));
5942 CPPUNIT_ASSERT_MESSAGE("local range name 1 should be copied", pLocal1);
5943 ScRange aRangeLocal1;
5944 pLocal1->IsValidReference(aRangeLocal1);
5945 CPPUNIT_ASSERT_MESSAGE("local range 1 should still point to Sheet1.A1",aRangeLocal1 == ScRange(0,0,0,0,0,0));
5946 pLocal2 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
5947 CPPUNIT_ASSERT_MESSAGE("local2 should not be copied", pLocal2 == NULL);
5950 //check undo and redo
5951 aUndo.Undo();
5952 m_pDoc->GetValue(1,1,1, aValue);
5953 ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", aValue, 0);
5954 aString = m_pDoc->GetString(2, 1, 1);
5955 CPPUNIT_ASSERT_MESSAGE("after undo string should be removed", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("")));
5957 aUndo.Redo();
5958 m_pDoc->GetValue(1,1,1, aValue);
5959 ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 2 after redo", aValue, 2);
5960 aString = m_pDoc->GetString(2, 1, 1);
5961 CPPUNIT_ASSERT_MESSAGE("Cell Sheet2.C2 should contain: test", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("test")));
5962 m_pDoc->GetFormula(1,1,1, aString);
5963 CPPUNIT_ASSERT_MESSAGE("Formula should be correct again", aString == aFormulaString);
5965 m_pDoc->DeleteTab(1);
5966 m_pDoc->DeleteTab(0);
5969 void Test::testCopyPasteRelativeFormula()
5971 m_pDoc->InsertTab(0, "Formula");
5973 AutoCalcSwitch aACSwitch(m_pDoc, true);
5975 // Insert values to A2 and A4.
5976 m_pDoc->SetValue(ScAddress(0,1,0), 1);
5977 m_pDoc->SetValue(ScAddress(0,3,0), 2);
5979 // Insert formula to B4.
5980 m_pDoc->SetString(ScAddress(1,3,0), "=A4");
5981 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,3,0)));
5983 // Select and copy B3:B4 to the clipboard.
5984 ScRange aRange(1,2,0,1,3,0);
5985 ScClipParam aClipParam(aRange, false);
5986 ScMarkData aMark;
5987 aMark.SetMarkArea(aRange);
5988 ScDocument aClipDoc(SCDOCMODE_CLIP);
5989 m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
5991 // Paste it to B1:B2.
5992 sal_uInt16 nFlags = IDF_ALL;
5993 ScRange aDestRange(1,0,0,1,1,0);
5994 aMark.SetMarkArea(aDestRange);
5995 m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, NULL, &aClipDoc);
5997 // B2 references A2, so the value should be 1.
5998 CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,0)));
6000 m_pDoc->DeleteTab(0);
6003 void Test::testMergedCells()
6005 //test merge and unmerge
6006 //TODO: an undo/redo test for this would be a good idea
6007 m_pDoc->InsertTab(0, OUString("Sheet1"));
6008 m_pDoc->DoMerge(0, 1, 1, 3, 3, false);
6009 SCCOL nEndCol = 1;
6010 SCROW nEndRow = 1;
6011 m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0, false);
6012 CPPUNIT_ASSERT_MESSAGE("did not merge cells", nEndCol == 3 && nEndRow == 3);
6013 ScRange aRange(0,2,0,MAXCOL,2,0);
6014 ScMarkData aMark;
6015 aMark.SetMarkArea(aRange);
6016 m_xDocShRef->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS, true, true);
6017 m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0, false);
6018 cout << nEndRow << nEndCol;
6019 CPPUNIT_ASSERT_MESSAGE("did not increase merge area", nEndCol == 3 && nEndRow == 4);
6020 m_pDoc->DeleteTab(0);
6024 void Test::testRenameTable()
6026 //test set rename table
6027 //TODO: set name1 and name2 and do an undo to check if name 1 is set now
6028 //TODO: also check if new name for table is same as another table
6030 m_pDoc->InsertTab(0, "Sheet1");
6031 m_pDoc->InsertTab(1, "Sheet2");
6033 //test case 1 , rename table2 to sheet 1, it should return error
6034 OUString nameToSet = "Sheet1";
6035 ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
6036 CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
6038 //test case 2 , simple rename to check name
6039 nameToSet = "test1";
6040 m_xDocShRef->GetDocFunc().RenameTable(0,nameToSet,false,true);
6041 OUString nameJustSet;
6042 m_pDoc->GetName(0,nameJustSet);
6043 CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
6045 //test case 3 , rename again
6046 OUString anOldName;
6047 m_pDoc->GetName(0,anOldName);
6049 nameToSet = "test2";
6050 rDocFunc.RenameTable(0,nameToSet,false,true);
6051 m_pDoc->GetName(0,nameJustSet);
6052 CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
6054 //test case 4 , check if undo works
6055 SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShRef,0,anOldName,nameToSet);
6056 pUndo->Undo();
6057 m_pDoc->GetName(0,nameJustSet);
6058 CPPUNIT_ASSERT_MESSAGE("the correct name is not set after undo", nameJustSet == anOldName);
6060 pUndo->Redo();
6061 m_pDoc->GetName(0,nameJustSet);
6062 CPPUNIT_ASSERT_MESSAGE("the correct color is not set after redo", nameJustSet == nameToSet);
6064 m_pDoc->DeleteTab(0);
6065 m_pDoc->DeleteTab(1);
6070 void Test::testSetBackgroundColor()
6072 //test set background color
6073 //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
6075 m_pDoc->InsertTab(0, "Sheet1");
6076 Color aColor;
6078 //test yellow
6079 aColor=Color(COL_YELLOW);
6080 m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
6081 CPPUNIT_ASSERT_MESSAGE("the correct color is not set",
6082 m_pDoc->GetTabBgColor(0) == aColor);
6085 Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
6086 aColor.SetColor(COL_BLUE);//set BLUE
6087 m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
6088 CPPUNIT_ASSERT_MESSAGE("the correct color is not set the second time",
6089 m_pDoc->GetTabBgColor(0) == aColor);
6091 //now check for undo
6092 SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShRef,0, aOldTabBgColor, aColor);
6093 pUndo->Undo();
6094 CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aOldTabBgColor);
6095 pUndo->Redo();
6096 CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aColor);
6097 m_pDoc->DeleteTab(0);
6102 void Test::testUpdateReference()
6104 //test that formulas are correctly updated during sheet delete
6105 //TODO: add tests for relative references, updating of named ranges, ...
6106 OUString aSheet1("Sheet1");
6107 OUString aSheet2("Sheet2");
6108 OUString aSheet3("Sheet3");
6109 OUString aSheet4("Sheet4");
6110 m_pDoc->InsertTab(0, aSheet1);
6111 m_pDoc->InsertTab(1, aSheet2);
6112 m_pDoc->InsertTab(2, aSheet3);
6113 m_pDoc->InsertTab(3, aSheet4);
6115 m_pDoc->SetValue(0,0,2, 1);
6116 m_pDoc->SetValue(1,0,2, 2);
6117 m_pDoc->SetValue(1,1,3, 4);
6118 m_pDoc->SetString(2,0,2, OUString("=A1+B1"));
6119 m_pDoc->SetString(2,1,2, OUString("=Sheet4.B2+A1"));
6121 double aValue;
6122 m_pDoc->GetValue(2,0,2, aValue);
6123 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 3);
6124 m_pDoc->GetValue(2,1,2, aValue);
6125 ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 5);
6127 //test deleting both sheets: one is not directly before the sheet, the other one is
6128 m_pDoc->DeleteTab(0);
6129 m_pDoc->GetValue(2,0,1, aValue);
6130 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 3);
6131 m_pDoc->GetValue(2,1,1, aValue);
6132 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 5);
6134 m_pDoc->DeleteTab(0);
6135 m_pDoc->GetValue(2,0,0, aValue);
6136 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 3);
6137 m_pDoc->GetValue(2,1,0, aValue);
6138 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 5);
6140 //test adding two sheets
6141 m_pDoc->InsertTab(0, aSheet2);
6142 m_pDoc->GetValue(2,0,1, aValue);
6143 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 3);
6144 m_pDoc->GetValue(2,1,1, aValue);
6145 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 5);
6147 m_pDoc->InsertTab(0, aSheet1);
6148 m_pDoc->GetValue(2,0,2, aValue);
6149 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 3);
6150 m_pDoc->GetValue(2,1,2, aValue);
6151 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 5);
6153 //test new DeleteTabs/InsertTabs methods
6154 m_pDoc->DeleteTabs(0, 2);
6155 m_pDoc->GetValue(2, 0, 0, aValue);
6156 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 3);
6157 m_pDoc->GetValue(2, 1, 0, aValue);
6158 ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 5);
6160 std::vector<OUString> aSheets;
6161 aSheets.push_back(aSheet1);
6162 aSheets.push_back(aSheet2);
6163 m_pDoc->InsertTabs(0, aSheets, false, true);
6164 m_pDoc->GetValue(2, 0, 2, aValue);
6165 OUString aFormula;
6166 m_pDoc->GetFormula(2,0,2, aFormula);
6167 std::cout << "formel: " << OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
6168 std::cout << std::endl << aValue << std::endl;
6169 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 3);
6170 m_pDoc->GetValue(2, 1, 2, aValue);
6171 ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 5);
6173 m_pDoc->DeleteTab(3);
6174 m_pDoc->DeleteTab(2);
6175 m_pDoc->DeleteTab(1);
6176 m_pDoc->DeleteTab(0);
6179 namespace {
6181 bool hasRange(const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange)
6183 std::vector<ScTokenRef>::const_iterator it = rRefTokens.begin(), itEnd = rRefTokens.end();
6184 for (; it != itEnd; ++it)
6186 const ScTokenRef& p = *it;
6187 if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
6188 continue;
6190 switch (p->GetType())
6192 case formula::svSingleRef:
6194 ScSingleRefData aData = p->GetSingleRef();
6195 if (rRange.aStart != rRange.aEnd)
6196 break;
6198 ScAddress aThis(aData.nCol, aData.nRow, aData.nTab);
6199 if (aThis == rRange.aStart)
6200 return true;
6202 break;
6203 case formula::svDoubleRef:
6205 ScComplexRefData aData = p->GetDoubleRef();
6206 ScRange aThis(aData.Ref1.nCol, aData.Ref1.nRow, aData.Ref1.nTab, aData.Ref2.nCol, aData.Ref2.nRow, aData.Ref2.nTab);
6207 if (aThis == rRange)
6208 return true;
6210 break;
6211 default:
6215 return false;
6220 void Test::testJumpToPrecedentsDependents()
6222 // Precedent is another cell that the cell references, while dependent is
6223 // another cell that references it.
6224 m_pDoc->InsertTab(0, OUString("Test"));
6226 m_pDoc->SetString(2, 0, 0, OUString("=A1+A2+B3")); // C1
6227 m_pDoc->SetString(2, 1, 0, OUString("=A1")); // C2
6228 m_pDoc->CalcAll();
6230 std::vector<ScTokenRef> aRefTokens;
6231 ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
6234 // C1's precedent should be A1:A2,B3.
6235 ScRangeList aRange(ScRange(2, 0, 0));
6236 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
6237 CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
6238 hasRange(aRefTokens, ScRange(0, 0, 0, 0, 1, 0)));
6239 CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
6240 hasRange(aRefTokens, ScRange(1, 2, 0)));
6244 // C2's precedent should be A1 only.
6245 ScRangeList aRange(ScRange(2, 1, 0));
6246 rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
6247 CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
6248 aRefTokens.size(), static_cast<size_t>(1));
6249 CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
6250 hasRange(aRefTokens, ScRange(0, 0, 0)));
6254 // A1's dependent should be C1:C2.
6255 ScRangeList aRange(ScRange(0, 0, 0));
6256 rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
6257 CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
6258 aRefTokens.size() == 1 && hasRange(aRefTokens, ScRange(2, 0, 0, 2, 1, 0)));
6261 m_pDoc->DeleteTab(0);
6264 void Test::testAutoFill()
6266 m_pDoc->InsertTab(0, "test");
6268 m_pDoc->SetValue(0,0,0,1);
6270 ScMarkData aMarkData;
6271 aMarkData.SelectTable(0, true);
6273 m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 5);
6274 for (SCROW i = 0; i< 6; ++i)
6275 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
6277 // check that hidden rows are not affected by autofill
6278 // set values for hidden rows
6279 m_pDoc->SetValue(0,1,0,10);
6280 m_pDoc->SetValue(0,2,0,10);
6282 m_pDoc->SetRowHidden(1, 2, 0, true);
6283 m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 8);
6285 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
6286 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
6287 for (SCROW i = 3; i< 8; ++i)
6288 ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
6290 m_pDoc->Fill( 0, 0, 0, 8, NULL, aMarkData, 5, FILL_TO_RIGHT );
6291 for (SCCOL i = 0; i < 5; ++i)
6293 for(SCROW j = 0; j < 8; ++j)
6295 if (j > 2)
6297 ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
6299 else if (j == 0)
6301 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
6303 else if (j == 1 || j== 2)
6305 if(i == 0)
6306 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
6307 else
6308 ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
6313 // test auto fill user data lists
6314 m_pDoc->SetString( 0, 100, 0, "January" );
6315 m_pDoc->Fill( 0, 100, 0, 100, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
6316 OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
6317 CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("February") );
6318 aTestValue = m_pDoc->GetString( 0, 102, 0 );
6319 CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("March") );
6321 // test that two same user data list entries will not result in incremental fill
6322 m_pDoc->SetString( 0, 101, 0, "January" );
6323 m_pDoc->Fill( 0, 100, 0, 101, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
6324 for ( SCROW i = 102; i <= 103; ++i )
6326 aTestValue = m_pDoc->GetString( 0, i, 0 );
6327 CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("January") );
6329 m_pDoc->DeleteTab(0);
6332 void Test::testCopyPasteFormulas()
6334 m_pDoc->InsertTab(0, "Sheet1");
6335 m_pDoc->InsertTab(1, "Sheet2");
6337 m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
6338 m_pDoc->SetString(0,1,0, "=$A$1+B2" );
6339 m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
6340 m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
6341 m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
6343 // to prevent ScEditableTester in ScDocFunc::MoveBlock
6344 ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,0,0), 1.0);
6345 ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,1,0), 1.0);
6346 ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
6347 bool bMoveDone = rDocFunc.MoveBlock(ScRange(0,0,0,0,4,0), ScAddress( 10, 10, 0), false, false, false, true);
6349 // check that moving was succesful, mainly for editable tester
6350 CPPUNIT_ASSERT(bMoveDone);
6351 ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,10,0), 1.0);
6352 ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,11,0), 1.0);
6353 OUString aFormula;
6354 m_pDoc->GetFormula(10,10,0, aFormula);
6355 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($A$1)"));
6356 m_pDoc->GetFormula(10,11,0, aFormula);
6357 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$A$1+L12"));
6358 m_pDoc->GetFormula(10,12,0, aFormula);
6359 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K11"));
6360 m_pDoc->GetFormula(10,13,0, aFormula);
6361 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.$A$1"));
6362 m_pDoc->GetFormula(10,14,0, aFormula);
6363 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K$1"));
6366 void Test::testCopyPasteFormulasExternalDoc()
6368 OUString aDocName("file:///source.fake");
6369 SfxMedium* pMedium = new SfxMedium(aDocName, STREAM_STD_READWRITE);
6370 m_xDocShRef->DoInitNew(pMedium);
6371 m_pDoc = m_xDocShRef->GetDocument();
6373 ScDocShellRef xExtDocSh = new ScDocShell;
6374 OUString aExtDocName("file:///extdata.fake");
6375 OUString aExtSh1Name("ExtSheet1");
6376 OUString aExtSh2Name("ExtSheet2");
6377 SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
6378 xExtDocSh->DoInitNew(pMed);
6379 CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6380 findLoadedDocShellByName(aExtDocName) != NULL);
6382 ScDocument* pExtDoc = xExtDocSh->GetDocument();
6383 pExtDoc->InsertTab(0, aExtSh1Name);
6384 pExtDoc->InsertTab(1, aExtSh2Name);
6386 m_pDoc->InsertTab(0, "Sheet1");
6387 m_pDoc->InsertTab(1, "Sheet2");
6389 m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
6390 m_pDoc->SetString(0,1,0, "=$A$1+B2" );
6391 m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
6392 m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
6393 m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
6394 m_pDoc->SetString(0,5,0, "=$Sheet1.$A$1");
6396 ScRange aRange(0,0,0,0,5,0);
6397 ScClipParam aClipParam(aRange, false);
6398 ScMarkData aMark;
6399 aMark.SetMarkArea(aRange);
6400 ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
6401 m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
6403 sal_uInt16 nFlags = IDF_ALL;
6404 aRange = ScRange(1,1,1,1,6,1);
6405 ScMarkData aMarkData2;
6406 aMarkData2.SetMarkArea(aRange);
6407 pExtDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
6409 OUString aFormula;
6410 pExtDoc->GetFormula(1,1,1, aFormula);
6411 //adjust absolute refs pointing to the copy area
6412 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($B$2)"));
6413 pExtDoc->GetFormula(1,2,1, aFormula);
6414 //adjust absolute refs and keep relative refs
6415 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$B$2+C3"));
6416 pExtDoc->GetFormula(1,3,1, aFormula);
6417 // make absolute sheet refs external refs
6418 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B2"));
6419 pExtDoc->GetFormula(1,4,1, aFormula);
6420 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.$A$1"));
6421 pExtDoc->GetFormula(1,5,1, aFormula);
6422 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B$1"));
6423 pExtDoc->GetFormula(1,6,1, aFormula);
6424 CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$ExtSheet2.$B$2"));
6427 void Test::testFindAreaPosRowDown()
6429 const char* aData[][2] = {
6430 { "", "1" },
6431 { "1", "" },
6432 { "1", "1" },
6433 { "", "1" },
6434 { "1", "1" },
6435 { "1", "" },
6436 { "1", "1" }, };
6438 ScDocument* pDoc = m_xDocShRef->GetDocument();
6439 OUString aTabName1("test1");
6440 pDoc->InsertTab(0, aTabName1);
6441 clearRange( pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
6442 ScAddress aPos(0,0,0);
6443 ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6444 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6446 pDoc->SetRowHidden(4,4,0,true);
6447 bool bHidden = pDoc->RowHidden(4,0);
6448 CPPUNIT_ASSERT(bHidden);
6450 SCCOL nCol = 0;
6451 SCROW nRow = 0;
6452 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6454 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
6455 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6457 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6459 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
6460 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6462 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6464 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
6465 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6467 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6469 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
6470 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6472 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6474 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(MAXROW), nRow);
6475 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6477 nCol = 1;
6478 nRow = 2;
6480 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6482 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
6483 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
6485 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6487 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
6488 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
6490 pDoc->DeleteTab(0);
6493 void Test::testFindAreaPosColRight()
6495 const char* aData[][7] = {
6496 { "", "1", "1", "", "1", "1", "1" },
6497 { "", "", "1", "1", "1", "", "1" }, };
6499 ScDocument* pDoc = m_xDocShRef->GetDocument();
6500 OUString aTabName1("test1");
6501 pDoc->InsertTab(0, aTabName1);
6502 clearRange( pDoc, ScRange(0, 0, 0, 7, SAL_N_ELEMENTS(aData), 0));
6503 ScAddress aPos(0,0,0);
6504 ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6505 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6507 pDoc->SetColHidden(4,4,0,true);
6508 bool bHidden = pDoc->ColHidden(4,0);
6509 CPPUNIT_ASSERT(bHidden);
6511 SCCOL nCol = 0;
6512 SCROW nRow = 0;
6513 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6515 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6516 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
6518 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6520 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6521 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
6523 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6525 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6526 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
6528 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6530 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6531 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
6533 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6535 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6536 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(MAXCOL), nCol);
6538 nCol = 2;
6539 nRow = 1;
6541 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6543 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
6544 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
6546 pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6548 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
6549 CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
6551 pDoc->DeleteTab(0);
6554 // regression test fo fdo#53814, sorting doens't work as expected
6555 // if cells in the sort are referenced by formulas
6556 void Test::testSortWithFormulaRefs()
6558 ScDocument* pDoc = m_xDocShRef->GetDocument();
6559 OUString aTabName1("List1");
6560 OUString aTabName2("List2");
6561 pDoc->InsertTab(0, aTabName1);
6562 pDoc->InsertTab(1, aTabName2);
6564 const char* aFormulaData[6] = {
6565 "=IF($List1.A2<>\"\",$List1.A2,\"\")",
6566 "=IF($List1.A3<>\"\",$List1.A3,\"\")",
6567 "=IF($List1.A4<>\"\",$List1.A4,\"\")",
6568 "=IF($List1.A5<>\"\",$List1.A5,\"\")",
6569 "=IF($List1.A6<>\"\",$List1.A6,\"\")",
6570 "=IF($List1.A7<>\"\",$List1.A7,\"\")",
6573 const char* aTextData[4] = {
6574 "bob",
6575 "tim",
6576 "brian",
6577 "larry",
6580 const char* aResults[ 6 ] = {
6581 "bob",
6582 "brian",
6583 "larry",
6584 "tim",
6588 // insert data to sort
6589 SCROW nStart = 1, nEnd = 4;
6590 for ( SCROW i = nStart; i <= nEnd; ++i )
6591 pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
6592 // insert forumulas
6593 nStart = 0;
6594 nEnd = SAL_N_ELEMENTS(aFormulaData);
6595 for ( SCROW i = nStart; i < nEnd; ++i )
6596 pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
6598 ScSortParam aSortData;
6599 aSortData.nCol1 = 0;
6600 aSortData.nCol2 = 0;
6601 aSortData.nRow1 = 1;
6602 aSortData.nRow2 = 7;
6603 aSortData.maKeyState[0].bDoSort = true;
6604 aSortData.maKeyState[0].nField = 0;
6606 pDoc->Sort(0, aSortData, false, NULL);
6608 nEnd = SAL_N_ELEMENTS( aResults );
6609 for ( SCROW i = nStart; i < nEnd; ++i )
6611 OUString sResult = pDoc->GetString( 0, i + 1, 0);
6612 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[ i ] ), sResult );
6614 pDoc->DeleteTab(0);
6615 pDoc->DeleteTab(1);
6618 void Test::testSort()
6620 OUString aTabName1("test1");
6621 m_pDoc->InsertTab(0, aTabName1);
6623 ScRange aDataRange;
6624 ScAddress aPos(0,0,0);
6626 const char* aData[][2] = {
6627 { "2", "4" },
6628 { "4", "1" },
6629 { "1", "2" },
6630 { "1", "23" },
6633 clearRange(m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
6634 aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6635 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6638 // Insert note in cell B2.
6639 OUString aHello("Hello");
6640 OUString aJimBob("Jim Bob");
6641 ScAddress rAddr(1, 1, 0);
6642 ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
6643 pNote->SetText(rAddr, aHello);
6644 pNote->SetAuthor(aJimBob);
6646 ScSortParam aSortData;
6647 aSortData.nCol1 = 1;
6648 aSortData.nCol2 = 1;
6649 aSortData.nRow1 = 0;
6650 aSortData.nRow2 = 2;
6651 aSortData.maKeyState[0].bDoSort = true;
6652 aSortData.maKeyState[0].nField = 1;
6653 aSortData.maKeyState[0].bAscending = true;
6655 m_pDoc->Sort(0, aSortData, false, NULL);
6656 double nVal = m_pDoc->GetValue(1,0,0);
6657 ASSERT_DOUBLES_EQUAL(nVal, 1.0);
6659 // check that note is also moved
6660 pNote = m_pDoc->GetNotes(0)->findByAddress( 1, 0 );
6661 CPPUNIT_ASSERT(pNote);
6663 clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
6665 // 0 = empty cell
6666 const char* aData[][1] = {
6667 { "Title" },
6668 { 0 },
6669 { 0 },
6670 { "12" },
6671 { "b" },
6672 { "1" },
6673 { "9" },
6674 { "123" }
6677 aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6678 CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6681 aSortData.nCol1 = aDataRange.aStart.Col();
6682 aSortData.nCol2 = aDataRange.aEnd.Col();
6683 aSortData.nRow1 = aDataRange.aStart.Row();
6684 aSortData.nRow2 = aDataRange.aEnd.Row();
6685 aSortData.bHasHeader = true;
6686 aSortData.maKeyState[0].nField = 0;
6687 m_pDoc->Sort(0, aSortData, false, NULL);
6689 // Title should stay at the top, numbers should be sorted numerically,
6690 // numbers always come before strings, and empty cells always occur at the
6691 // end.
6692 CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos));
6693 aPos.IncRow();
6694 CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos));
6695 aPos.IncRow();
6696 CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos));
6697 aPos.IncRow();
6698 CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos));
6699 aPos.IncRow();
6700 CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos));
6701 aPos.IncRow();
6702 CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos));
6703 aPos.IncRow();
6704 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos));
6706 m_pDoc->DeleteTab(0);
6709 void Test::testShiftCells()
6711 m_pDoc->InsertTab(0, "foo");
6713 OUString aTestVal("Some Text");
6715 // Text into cell E5.
6716 m_pDoc->SetString(4, 3, 0, aTestVal);
6718 // Insert cell at D5. This should shift the string cell to right.
6719 m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
6720 OUString aStr = m_pDoc->GetString(5, 3, 0);
6721 CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
6722 CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 3, 4, 3, 4));
6724 // Delete cell D5, to shift the text cell back into D5.
6725 m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
6726 aStr = m_pDoc->GetString(4, 3, 0);
6727 CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
6728 CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 4, 4, 4, 4));
6730 m_pDoc->DeleteTab(0);
6733 void Test::testDeleteRow()
6735 ScDocument* pDoc = m_xDocShRef->GetDocument();
6736 OUString aSheet1("Sheet1");
6737 pDoc->InsertTab(0, aSheet1);
6739 OUString aHello("Hello");
6740 OUString aJimBob("Jim Bob");
6741 ScAddress rAddr(1, 1, 0);
6742 ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
6743 pNote->SetText(rAddr, aHello);
6744 pNote->SetAuthor(aJimBob);
6746 pDoc->DeleteRow(0, 0, MAXCOL, 0, 1, 1);
6748 CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
6749 pDoc->DeleteTab(0);
6752 void Test::testDeleteCol()
6754 ScDocument* pDoc = m_xDocShRef->GetDocument();
6755 OUString aSheet1("Sheet1");
6756 pDoc->InsertTab(0, aSheet1);
6758 OUString aHello("Hello");
6759 OUString aJimBob("Jim Bob");
6760 ScAddress rAddr(1, 1, 0);
6761 ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
6762 pNote->SetText(rAddr, aHello);
6763 pNote->SetAuthor(aJimBob);
6765 pDoc->DeleteCol(0, 0, MAXROW, 0, 1, 1);
6767 CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
6768 pDoc->DeleteTab(0);
6771 void Test::testAnchoredRotatedShape()
6773 OUString aTabName("TestTab");
6774 m_pDoc->InsertTab(0, aTabName);
6775 SCROW nRow1, nRow2;
6776 bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
6777 CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
6779 m_pDoc->InitDrawLayer();
6780 ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
6781 CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
6782 SdrPage* pPage = pDrawLayer->GetPage(0);
6783 CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
6784 m_pDoc->SetRowHeightRange( 0, MAXROW, 0, sc::HMMToTwips( 1000 ) );
6785 const long TOLERANCE = 30; //30 hmm
6786 for ( SCCOL nCol = 0; nCol < MAXCOL; ++nCol )
6787 m_pDoc->SetColWidth( nCol, 0, sc::HMMToTwips( 1000 ) );
6789 //Add a rect
6790 Rectangle aRect( 4000, 5000, 10000, 7000 );
6792 Rectangle aRotRect( 6000, 3000, 8000, 9000 );
6793 SdrRectObj *pObj = new SdrRectObj(aRect);
6794 pPage->InsertObject(pObj);
6795 Point aRef1(pObj->GetSnapRect().Center());
6796 int nAngle = 9000; //90 deg.
6797 double nSin=sin(nAngle*nPi180);
6798 double nCos=cos(nAngle*nPi180);
6799 pObj->Rotate(aRef1,nAngle,nSin,nCos);
6801 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
6803 Rectangle aSnap = pObj->GetSnapRect();
6804 printf("expected height %ld actual %ld\n", aRotRect.GetHeight(), aSnap.GetHeight() );
6805 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
6806 printf("expected width %ld actual %ld\n", aRotRect.GetWidth(), aSnap.GetWidth() );
6807 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
6808 printf("expected left %ld actual %ld\n", aRotRect.Left(), aSnap.Left() );
6809 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Left(), aSnap.Left(), TOLERANCE ) );
6810 printf("expected right %ld actual %ld\n", aRotRect.Top(), aSnap.Top() );
6811 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Top(), aSnap.Top(), TOLERANCE ) );
6813 ScDrawObjData aAnchor;
6814 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
6816 aAnchor.maStart = pData->maStart;
6817 aAnchor.maEnd = pData->maEnd;
6819 m_pDoc->SetDrawPageSize(0);
6821 // increase row 5 by 2000 hmm
6822 m_pDoc->SetRowHeight( 5, 0, sc::HMMToTwips( 3000 ) );
6823 // increase col 6 by 1000 hmm
6824 m_pDoc->SetColWidth( 6, 0, sc::HMMToTwips( 2000 ) );
6826 aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
6827 aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
6829 m_pDoc->SetDrawPageSize(0);
6831 aSnap = pObj->GetSnapRect();
6833 printf("expected new height %ld actual %ld\n", aRotRect.GetHeight(), aSnap.GetHeight() );
6834 // ensure that width and height have been adjusted accordingly
6835 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
6836 printf("expected new width %ld %ld\n", aRotRect.GetWidth(), aSnap.GetWidth() );
6837 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
6839 // ensure that anchor start and end addresses haven't changed
6840 printf("expected startrow %ld actual %ld\n", (long)aAnchor.maStart.Row(), (long)pData->maStart.Row() );
6841 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
6842 printf("expected startcol %ld actual %ld\n", (long)aAnchor.maStart.Col(), (long)pData->maStart.Col() );
6843 CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
6844 printf("expected endrow %ld actual %ld\n", (long)aAnchor.maEnd.Row(), (long)pData->maEnd.Row() );
6845 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
6846 printf("expected endcol %ld actual %ld\n", (long)aAnchor.maEnd.Col(), (long)pData->maEnd.Col() );
6847 CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
6849 m_pDoc->DeleteTab(0);
6852 void Test::testCellTextWidth()
6854 m_pDoc->InsertTab(0, "Test");
6856 ScAddress aTopCell(0, 0, 0);
6858 // Sheet is empty.
6859 boost::scoped_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
6860 CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
6862 // Sheet only has one cell.
6863 m_pDoc->SetString(0, 0, 0, "Only one cell");
6864 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
6865 CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
6866 SCROW nTestRow = 0;
6867 CPPUNIT_ASSERT_EQUAL(nTestRow, pIter->getPos());
6869 // Setting a text width here should commit it to the column.
6870 sal_uInt16 nTestVal = 432;
6871 pIter->setValue(nTestVal);
6872 CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
6874 // Set values to row 2 through 6.
6875 for (SCROW i = 2; i <= 6; ++i)
6876 m_pDoc->SetString(0, i, 0, "foo");
6878 // Set values to row 10 through 18.
6879 for (SCROW i = 10; i <= 18; ++i)
6880 m_pDoc->SetString(0, i, 0, "foo");
6883 // Full range.
6884 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
6885 SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
6886 size_t n = SAL_N_ELEMENTS(aRows);
6887 for (size_t i = 0; i < n; ++i, pIter->next())
6889 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
6890 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
6892 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
6896 // Specify start and end rows (6 - 16)
6897 ScAddress aStart = aTopCell;
6898 aStart.SetRow(6);
6899 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
6900 SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
6901 size_t n = SAL_N_ELEMENTS(aRows);
6902 for (size_t i = 0; i < n; ++i, pIter->next())
6904 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
6905 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
6907 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
6910 // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
6911 clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
6914 // Full range again.
6915 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
6916 SCROW aRows[] = { 0, 2, 18 };
6917 size_t n = SAL_N_ELEMENTS(aRows);
6918 for (size_t i = 0; i < n; ++i, pIter->next())
6920 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
6921 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
6923 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
6926 // Delete row 2 which shifts all cells below row 2 upward. After this, we
6927 // should only have cells at rows 0 and 17.
6928 m_pDoc->DeleteRow(0, 0, MAXCOL, MAXTAB, 2, 1);
6930 // Full range again.
6931 pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
6932 SCROW aRows[] = { 0, 17 };
6933 size_t n = SAL_N_ELEMENTS(aRows);
6934 for (size_t i = 0; i < n; ++i, pIter->next())
6936 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
6937 CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
6939 CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
6942 m_pDoc->DeleteTab(0);
6945 void Test::testFormulaGrouping()
6947 if ( !getenv("SC_FORMULAGROUP") )
6948 return;
6950 static const struct {
6951 const char *pFormula[3];
6952 bool bGroup[3];
6953 } aGroupTests[] = {
6954 { { "=SUM(B1)", "=SUM(C1)", "" }, // single increments
6955 { true, true, false } },
6956 { { "=SUM(B1)", "=SUM(D1)", "=SUM(F1)" }, // tripple increments
6957 { true, true, true } },
6958 { { "=SUM(B1)", "", "=SUM(C1)" }, // a gap
6959 { false, false, false } },
6960 { { "=SUM(B1)", "=SUM(C1;3)", "=SUM(D1;3)" }, // similar foo
6961 { false, true, true } },
6964 m_pDoc->InsertTab( 0, "sheet" );
6966 for (unsigned i = 0; i < SAL_N_ELEMENTS( aGroupTests ); i++)
6968 for (unsigned j = 0; j < SAL_N_ELEMENTS( aGroupTests[0].pFormula ); j++)
6970 OUString aFormula = OUString::createFromAscii(aGroupTests[i].pFormula[j]);
6971 m_pDoc->SetString(0, (SCROW)j, 0, aFormula);
6973 m_pDoc->RebuildFormulaGroups();
6975 for (unsigned j = 0; j < SAL_N_ELEMENTS( aGroupTests[0].pFormula ); j++)
6977 ScRefCellValue aCell;
6978 aCell.assign(*m_pDoc, ScAddress(0, (SCROW)j, 0));
6979 if (aCell.isEmpty())
6981 CPPUNIT_ASSERT_MESSAGE("invalid empty cell", !aGroupTests[i].bGroup[j]);
6982 continue;
6984 CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", !aCell.isEmpty());
6985 CPPUNIT_ASSERT_MESSAGE("Cell wrong type.",
6986 aCell.meType == CELLTYPE_FORMULA);
6987 ScFormulaCell *pCur = aCell.mpFormula;
6989 if( !!pCur->GetCellGroup().get() ^ aGroupTests[i].bGroup[j] )
6991 printf("expected group test %u at row %u to be %d but is %d\n",
6992 i, j, aGroupTests[i].bGroup[j], !!pCur->GetCellGroup().get());
6993 CPPUNIT_ASSERT_MESSAGE("Failed", false);
6999 void Test::testCondFormatINSDEL()
7001 // fdo#62206
7002 m_pDoc->InsertTab(0, "Test");
7003 ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
7005 ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
7006 ScRangeList aRangeList(ScRange(0,0,0,0,3,0));
7007 pFormat->AddRange(aRangeList);
7008 ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
7009 pFormat->AddEntry(pEntry);
7011 m_pDoc->AddCondFormatData(pFormat->GetRange(), 0, 1);
7012 pList->InsertNew(pFormat);
7014 m_pDoc->InsertCol(0,0,MAXROW,0,0,2);
7015 const ScRangeList& rRange = pFormat->GetRange();
7016 CPPUNIT_ASSERT(rRange == ScRange(2,0,0,2,3,0));
7018 OUString aExpr = pEntry->GetExpression(ScAddress(2,0,0), 0);
7019 CPPUNIT_ASSERT_EQUAL(aExpr, OUString("D2"));
7021 m_pDoc->DeleteTab(0);
7024 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
7028 CPPUNIT_PLUGIN_IMPLEMENT();
7030 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */