1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 #include <sal/config.h>
10 #include <test/bootstrapfixture.hxx>
12 #include <rtl/strbuf.hxx>
13 #include <osl/file.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"
33 #include "reftokenhelper.hxx"
34 #include "userdat.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"
45 #include "dpdimsave.hxx"
46 #include "dpcache.hxx"
47 #include "dpfilteredcache.hxx"
48 #include "calcconfig.hxx"
49 #include "interpre.hxx"
50 #include "columniterator.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>
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
;
96 class Test
: public test::BootstrapFixture
{
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.
111 void testRangeList();
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
137 void testCellBroadcaster();
139 void testFuncParam();
140 void testNamedRange();
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
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
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
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();
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
234 void testFunctionLists();
236 void testGraphicsInGroup();
237 void testGraphicsOnSheetMove();
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
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();
258 void testCopyPasteFormulas();
259 void testCopyPasteFormulasExternalDoc();
261 void testFindAreaPosRowDown();
262 void testFindAreaPosColRight();
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
);
279 CPPUNIT_TEST(testPerf
);
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();
350 ScDocShellRef m_xDocShRef
;
353 void clearRange(ScDocument
* pDoc
, const ScRange
& rRange
)
355 ScMarkData aMarkData
;
356 aMarkData
.SetMarkArea(rRange
);
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
)
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");
402 * Temporarily switch on/off auto calculation mode.
409 AutoCalcSwitch(ScDocument
* pDoc
, bool bAutoCalc
) : mpDoc(pDoc
), mbOldValue(pDoc
->GetAutoCalc())
411 mpDoc
->SetAutoCalc(bAutoCalc
);
416 mpDoc
->SetAutoCalc(mbOldValue
);
421 * Temporarily set formula grammar.
423 class FormulaGrammarSwitch
426 formula::FormulaGrammar::Grammar meOldGrammar
;
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
443 TimeValue maTimeBefore
;
445 MeasureTimeSwitch(double& rDiff
) : mrDiff(rDiff
)
448 osl_getSystemTime(&maTimeBefore
);
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;
476 BootstrapFixture::setUp();
479 m_xDocShRef
= new ScDocShell(
481 SFXMODEL_DISABLE_EMBEDDED_SCRIPTS
|
482 SFXMODEL_DISABLE_DOCUMENT_RECOVERY
);
484 m_xDocShRef
->DoInitUnitTest();
485 m_pDoc
= m_xDocShRef
->GetDocument();
488 void Test::tearDown()
491 BootstrapFixture::tearDown();
494 #define PERF_ASSERT(df,scale,time,message) \
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()); \
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:
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
);
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.
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
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");
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
);
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
));
624 MeasureTimeSwitch
aTime(diff
);
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);
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
;
683 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE
, m_pDoc
->GetCellType(aTmp
));
686 MeasureTimeSwitch
aTime(diff
);
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
));
698 MeasureTimeSwitch
aTime(diff
);
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");
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
;
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
);
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
));
776 MeasureTimeSwitch
aTime(diff
);
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
));
787 m_pDoc
->DeleteTab(0);
790 void Test::testCollator()
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");
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'");
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
)
848 pDoc
->SetValue (0, 0, 0, val
);
849 pDoc
->SetValue (0, 1, 0, val
);
850 pDoc
->SetString (0, 2, 0, OUString("=SUM(A1:A2)"));
852 pDoc
->GetValue (0, 2, 0, result
);
853 CPPUNIT_ASSERT_MESSAGE ("calculation failed", result
== 2.0);
856 void testFuncPRODUCT(ScDocument
* pDoc
)
860 pDoc
->SetValue(0, 0, 0, val
);
862 pDoc
->SetValue(0, 1, 0, val
);
864 pDoc
->SetValue(0, 2, 0, val
);
865 pDoc
->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
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})"));
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
)
880 // Clear the area first.
881 clearRange(pDoc
, ScRange(0, 0, 0, 1, 20, 0));
883 // Put values to reference.
885 pDoc
->SetValue(0, 0, 0, val
);
886 pDoc
->SetString(0, 2, 0, OUString("Text"));
888 pDoc
->SetValue(0, 3, 0, val
);
890 pDoc
->SetValue(0, 4, 0, val
);
892 pDoc
->SetValue(0, 5, 0, val
);
893 pDoc
->SetString(0, 6, 0, OUString("'12.3"));
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)"));
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\")"));
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.
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
];
931 cerr
<< "row " << (i
+1) << ": expected=" << checks1
[i
] << " actual=" << result
<< endl
;
932 CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
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
];
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
[] = {
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
978 const char* pFormula
; double fResult
;
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
)
997 pDoc
->SetString(0, nRow
, 0, OUString::createFromAscii(aChecks
[i
].pFormula
));
1001 for (SCROW i
= 0; i
< nRows
; ++i
)
1004 SCROW nRow
= 20 + i
;
1005 pDoc
->GetValue(0, nRow
, 0, result
);
1006 bool bGood
= result
== aChecks
[i
].fResult
;
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.
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)"));
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
[] = {
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
1059 const char* pFormula
; const char* pResult
;
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.
1086 aMark
.SelectOneTable(0);
1087 pDoc
->InsertMatrixFormula(0, 20 + nRows
-2, 0, 20 + nRows
-1, aMark
, "=IFERROR(3*A11:A12;1998)", NULL
);
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
[] = {
1110 "1ag34 5g g6 78b9%%",
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
1125 const char* pFormula
; const char* pResult
;
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
));
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
)
1159 clearRange(pDoc
, ScRange(0, 0, 0, 5, 39, 0));
1162 const char* aData
[][2] = {
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");
1191 const char* pLookup
; const char* pFormula
; const char* pRes
;
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
));
1220 printRange(pDoc
, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
1223 for (size_t i
= 0; i
< SAL_N_ELEMENTS(aChecks
); ++i
)
1226 // Skip the header row.
1229 OUString aRes
= pDoc
->GetString(4, i
, 0);
1230 bool bGood
= aRes
.equalsAscii(aChecks
[i
].pRes
);
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
{
1245 struct StrStrCheck
{
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
);
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
[] = {
1312 StrStrCheck aChecks
[] = {
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
[] = {
1353 StrStrCheck aChecks
[] = {
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.
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
));
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
);
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
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
);
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
);
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
);
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
);
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.
1543 const char* pFormula1
; const char* pFormula2
; bool bEqual
;
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
);
1583 os
<< " Error: these hashes should differ." << endl
;
1584 CPPUNIT_ASSERT_MESSAGE(os
.str().c_str(), nHashVal1
!= nHashVal2
);
1591 // Go back to row 1.
1595 // Test formula vectorization state.
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);
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
);
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");
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);
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
));
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
));
1714 m_pDoc
->SetValue(0, 0, 0, val
);
1715 m_pDoc
->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
1717 m_pDoc
->GetValue(0, 1, 0, now1
);
1718 CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1
> 0.0);
1721 m_pDoc
->SetValue(0, 0, 0, val
);
1722 m_pDoc
->CalcFormulaTree(false, false);
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);
1728 m_pDoc
->SetValue(0, 0, 0, val
);
1729 m_pDoc
->CalcFormulaTree(false, false);
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);
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
);
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.
1828 for (SCROW nRow
= 1; nRow
<= 9; ++nRow
)
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.
1852 m_pDoc
->SetValue(0, 0, 0, val
);
1854 m_pDoc
->SetValue(1, 0, 0, val
);
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);
1871 bool broadcasterShifted(const ScDocument
& rDoc
, const ScAddress
& rFrom
, const ScAddress
& rTo
)
1873 const SvtBroadcaster
* pBC
= rDoc
.GetBroadcaster(rFrom
);
1876 cerr
<< "Broadcaster shouldn't be here." << endl
;
1880 pBC
= rDoc
.GetBroadcaster(rTo
);
1883 cerr
<< "Broadcaster should be here." << endl
;
1889 ScToken
* getSingleRefToken(ScDocument
& rDoc
, const ScAddress
& rPos
)
1891 ScFormulaCell
* pFC
= rDoc
.GetFormulaCell(rPos
);
1894 cerr
<< "Formula cell expected, but not found." << endl
;
1898 ScTokenArray
* pTokens
= pFC
->GetCode();
1901 cerr
<< "Token array is not present." << endl
;
1905 ScToken
* pToken
= static_cast<ScToken
*>(pTokens
->First());
1906 if (!pToken
|| pToken
->GetType() != formula::svSingleRef
)
1908 cerr
<< "Not a single reference token." << endl
;
1915 bool checkRelativeRefToken(ScDocument
& rDoc
, const ScAddress
& rPos
, SCsCOL nRelCol
, SCsROW nRelRow
)
1917 ScToken
* pToken
= getSingleRefToken(rDoc
, rPos
);
1921 ScSingleRefData
& rRef
= pToken
->GetSingleRef();
1922 if (!rRef
.IsColRel() || rRef
.nRelCol
!= nRelCol
)
1924 cerr
<< "Unexpected relative column address." << endl
;
1928 if (!rRef
.IsRowRel() || rRef
.nRelRow
!= nRelRow
)
1930 cerr
<< "Unexpected relative row address." << endl
;
1937 bool checkDeletedRefToken(ScDocument
& rDoc
, const ScAddress
& rPos
)
1939 ScToken
* pToken
= getSingleRefToken(rDoc
, rPos
);
1943 ScSingleRefData
& rRef
= pToken
->GetSingleRef();
1944 if (!rRef
.IsDeleted())
1946 cerr
<< "Deleted reference is expected, but it's still a valid reference." << endl
;
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.
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.
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);
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
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
2175 ScCalcConfig aConfig
;
2177 // With "Empty string as zero" option.
2178 aConfig
.mbEmptyStringAsZero
= true;
2179 ScInterpreter::SetGlobalConfig(aConfig
);
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
);
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()
2209 const char* pName
; const char* pExpr
; sal_uInt16 nIndex
;
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(
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"));
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;
2273 const char *pStr
; int eSep
; bool bResult
; double nValue
;
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
? ',' : '.',
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
)
2306 rMat
.GetDimensions(nC
, nR
);
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);
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);
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()
2387 // First, test the zero matrix type.
2388 pMat
= new ScMatrix(0, 0, 0.0);
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
);
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");
2457 m_pDoc
->SetValue(0, 1, 0, val
);
2459 m_pDoc
->SetValue(1, 1, 0, val
);
2461 // Create a matrix range in A4:B5 referencing A1:B2.
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);
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.
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
);
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");
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
];
2537 OUString aCheckVal
= OUString::createFromAscii(p
);
2538 bool bEqual
= aCheckVal
.equals(aVal
);
2541 cout
<< "Expected: " << aCheckVal
<< " Actual: " << aVal
<< endl
;
2545 else if (!aVal
.isEmpty())
2547 cout
<< "Empty cell expected" << endl
;
2552 printer
.print(pCaption
);
2556 ScDPObject
* createDPFromSourceDesc(
2557 ScDocument
* pDoc
, const ScSheetSourceDesc
& rDesc
, DPFieldDef aFields
[], size_t nFieldCount
,
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
);
2598 sheet::DataPilotFieldSortInfo aSortInfo
;
2599 aSortInfo
.IsAscending
= true;
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();
2626 ScDPObject
* createDPFromRange(
2627 ScDocument
* pDoc
, const ScRange
& rRange
, DPFieldDef aFields
[], size_t nFieldCount
,
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();
2646 ScRange
refreshGroups(ScDPCollection
* pDPs
, ScDPObject
* pDPObj
)
2648 // We need to first create group data in the cache, then the group data in
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 }
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
)
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
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.
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 }
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 }
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;
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 }
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] = {
3025 { "Group2", "- all -" },
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(),
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] = {
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] = {
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] = {
3111 { "Group2", "- all -" },
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 }
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
);
3159 aSrcRange
.Format(aRangeStr
, SCR_ABS_3D
, m_pDoc
);
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);
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);
3228 m_pDoc
->DeleteTab(1);
3229 m_pDoc
->DeleteTab(0);
3232 void Test::testPivotTableCache()
3234 m_pDoc
->InsertTab(0, OUString("Data"));
3237 const char* aData
[][3] = {
3238 { "F1", "F2", "F3" },
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
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.
3351 // Dimension 0: Z, R, A, F, Y, 12
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));
3363 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem
&& *pItem
== aTest
);
3367 // Dimension 1: A, A, B, B, C, C
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
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"));
3423 const char* aData
[][2] = {
3424 { "Name", "Value" },
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] = {
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"));
3520 const char* aData
[][2] = {
3521 { "Name", "Value" },
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] = {
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 },
3609 { "Group1", "A", "1" },
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 },
3643 { "Group1", "A", "1" },
3646 { "Group2", "D", "4" },
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"));
3671 const char* aData
[][2] = {
3672 { "Order", "Score" },
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;
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] = {
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"));
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
);
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
);
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] = {
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"));
3940 const char* aData
[][2] = {
3941 { "Name", "Value" },
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] = {
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] = {
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] = {
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"));
4053 const char* aData
[][2] = {
4054 { "Name", "Value" },
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
);
4079 // Don't check the header row.
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] = {
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"));
4132 const char* aData
[][2] = {
4133 { "Name", "Value" },
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] = {
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
);
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])");
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 // +--------------+----------------+
4256 // +--------------+----------------+
4257 // | Dennis | <Dennis total> |
4258 // +--------------+----------------+
4259 // | Mike | <Miks total> |
4260 // +--------------+----------------+
4261 // | Sam | <Sam total> |
4262 // +--------------+----------------+
4263 // | Total Result | ... |
4264 // +--------------+----------------+
4266 aPos
= aOutRange
.aStart
;
4269 double fTest
= m_pDoc
->GetValue(aPos
);
4270 CPPUNIT_ASSERT_MESSAGE("Incorrect value for Dennis.", rtl::math::approxEqual(fTest
, fDennisTotal
));
4272 fTest
= m_pDoc
->GetValue(aPos
);
4273 CPPUNIT_ASSERT_MESSAGE("Incorrect value for Mike.", rtl::math::approxEqual(fTest
, fMikeTotal
));
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"));
4293 const char* aData
[][2] = {
4294 { "Name", "Value" },
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] = {
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] = {
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] = {
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] = {
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] = {
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] = {
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");
4461 const char* aData
[][2] = {
4462 { "Name", "Value" },
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] = {
4501 { "Microsoft", "32" },
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);
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));
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
);
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));
4614 SfxMedium
* pMedium
= pShell
->GetMedium();
4617 OUString aName
= pMedium
->GetName();
4618 if (aName
.equals(rName
))
4621 pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pShell
, &aType
, false));
4626 ScRange
getCachedRange(const ScExternalRefCache::TableTypeRef
& pCacheTab
)
4630 vector
<SCROW
> aRows
;
4631 pCacheTab
->getAllRows(aRows
);
4632 vector
<SCROW
>::const_iterator itrRow
= aRows
.begin(), itrRowEnd
= aRows
.end();
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
;
4645 aRange
.aStart
= ScAddress(nCol
, nRow
, 0);
4646 aRange
.aEnd
= aRange
.aStart
;
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
);
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");
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
);
4703 pExtDoc
->SetValue(1, 1, 0, val
);
4705 pExtDoc
->SetValue(1, 2, 0, val
);
4707 pExtDoc
->SetValue(1, 3, 0, val
);
4709 pExtDoc
->SetValue(1, 4, 0, val
);
4711 // Sheet 2 remains empty.
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
);
4721 pExtDoc
->SetValue(1, 1, 2, val
);
4723 pExtDoc
->SetValue(1, 2, 2, val
);
4725 pExtDoc
->SetValue(1, 3, 2, val
);
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)"));
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"));
4879 pExtDoc
->SetValue(0, 0, 0, val
);
4880 // leave cell B1 empty.
4882 pExtDoc
->SetValue(0, 1, 0, val
);
4883 pExtDoc
->SetValue(1, 1, 0, val
);
4885 pExtDoc
->SetValue(0, 2, 0, val
);
4886 pExtDoc
->SetValue(1, 2, 0, val
);
4888 pExtDoc
->SetValue(0, 3, 0, val
);
4889 pExtDoc
->SetValue(1, 3, 0, val
);
4891 m_pDoc
->InsertTab(0, OUString("Test"));
4894 const char* pFormula
; double fResult
;
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
));
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));
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
[] = {
5048 const char* aDateTime
[] = {
5070 const char* aFinancial
[] = {
5096 const char* aInformation
[] = {
5117 const char* aLogical
[] = {
5130 const char* aMathematical
[] = {
5202 const char* aArray
[] = {
5220 const char* aStatistical
[] = {
5305 const char* aSpreadsheet
[] = {
5330 const char* aText
[] = {
5368 const char* Category
; const char** Functions
;
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
},
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);
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
);
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
);
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
);
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
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);
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
);
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
);
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
);
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
);
5752 OUString
aFormula("=A1+4");
5753 ScAddress
aPos(1, 1, 0);
5754 ScRefFinder
aFinder(aFormula
, aPos
, m_pDoc
, formula::FormulaGrammar::CONV_OOO
);
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" },
5799 SCCOL nCols
= SAL_N_ELEMENTS(aData
[0]);
5800 SCROW nRows
= SAL_N_ELEMENTS(aData
);
5803 for (SCROW i
= 0; i
< nRows
; ++i
)
5804 for (SCCOL j
= 0; j
< nCols
; ++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);
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;
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);
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.
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;
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
);
5904 OUString
aFormulaString("=local1+global+SUM($C$1:$D$4)");
5905 m_pDoc
->SetString(1, 0, 0, aFormulaString
);
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);
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
);
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
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
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("")));
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);
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);
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);
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
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
);
6057 m_pDoc
->GetName(0,nameJustSet
);
6058 CPPUNIT_ASSERT_MESSAGE("the correct name is not set after undo", nameJustSet
== anOldName
);
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");
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
);
6094 CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc
->GetTabBgColor(0)== aOldTabBgColor
);
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"));
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
);
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);
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
))
6190 switch (p
->GetType())
6192 case formula::svSingleRef
:
6194 ScSingleRefData aData
= p
->GetSingleRef();
6195 if (rRange
.aStart
!= rRange
.aEnd
)
6198 ScAddress
aThis(aData
.nCol
, aData
.nRow
, aData
.nTab
);
6199 if (aThis
== rRange
.aStart
)
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
)
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
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
)
6297 ASSERT_DOUBLES_EQUAL(static_cast<double>(j
-1+i
), m_pDoc
->GetValue(i
, j
, 0));
6301 ASSERT_DOUBLES_EQUAL(static_cast<double>(i
+1), m_pDoc
->GetValue(i
, 0, 0));
6303 else if (j
== 1 || j
== 2)
6306 ASSERT_DOUBLES_EQUAL(10.0, m_pDoc
->GetValue(0,j
,0));
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);
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);
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
);
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] = {
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
);
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
);
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
);
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
);
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
);
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
);
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] = {
6580 const char* aResults
[ 6 ] = {
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]) );
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
);
6618 void Test::testSort()
6620 OUString
aTabName1("test1");
6621 m_pDoc
->InsertTab(0, aTabName1
);
6624 ScAddress
aPos(0,0,0);
6626 const char* aData
[][2] = {
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.
6666 const char* aData
[][1] = {
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
6692 CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc
->GetString(aPos
));
6694 CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc
->GetString(aPos
));
6696 CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc
->GetString(aPos
));
6698 CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc
->GetString(aPos
));
6700 CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc
->GetString(aPos
));
6702 CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc
->GetString(aPos
));
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());
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());
6771 void Test::testAnchoredRotatedShape()
6773 OUString
aTabName("TestTab");
6774 m_pDoc
->InsertTab(0, aTabName
);
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 ) );
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);
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());
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");
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
;
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") )
6950 static const struct {
6951 const char *pFormula
[3];
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
]);
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()
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: */