Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 : #include <sal/config.h>
10 : #include <test/bootstrapfixture.hxx>
11 :
12 : #include <rtl/strbuf.hxx>
13 : #include <osl/file.hxx>
14 : #include <osl/time.h>
15 :
16 : #include "scdll.hxx"
17 : #include "formulacell.hxx"
18 : #include "document.hxx"
19 : #include "stringutil.hxx"
20 : #include "scmatrix.hxx"
21 : #include "drwlayer.hxx"
22 : #include "scitems.hxx"
23 : #include "reffind.hxx"
24 : #include "markdata.hxx"
25 : #include "clipparam.hxx"
26 : #include "refundo.hxx"
27 : #include "undoblk.hxx"
28 : #include "undotab.hxx"
29 : #include "queryentry.hxx"
30 : #include "postit.hxx"
31 : #include "attrib.hxx"
32 : #include "dbdata.hxx"
33 : #include "reftokenhelper.hxx"
34 : #include "userdat.hxx"
35 :
36 : #include "docsh.hxx"
37 : #include "docfunc.hxx"
38 : #include "dbdocfun.hxx"
39 : #include "funcdesc.hxx"
40 : #include "externalrefmgr.hxx"
41 :
42 : #include "dpshttab.hxx"
43 : #include "dpobject.hxx"
44 : #include "dpsave.hxx"
45 : #include "dpdimsave.hxx"
46 : #include "dpcache.hxx"
47 : #include "dpfilteredcache.hxx"
48 : #include "calcconfig.hxx"
49 : #include "interpre.hxx"
50 : #include "columniterator.hxx"
51 : #include "types.hxx"
52 : #include "conditio.hxx"
53 : #include "globstr.hrc"
54 : #include "tokenarray.hxx"
55 : #include "scopetools.hxx"
56 : #include "dociter.hxx"
57 :
58 : #include "formula/IFunctionDescription.hxx"
59 :
60 : #include <basegfx/polygon/b2dpolygon.hxx>
61 : #include <editeng/boxitem.hxx>
62 :
63 : #include <svx/svdograf.hxx>
64 : #include <svx/svdpage.hxx>
65 : #include <svx/svdocirc.hxx>
66 : #include <svx/svdopath.hxx>
67 : #include "svl/srchitem.hxx"
68 :
69 : #include <sfx2/docfile.hxx>
70 :
71 : #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
72 : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
73 : #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
74 : #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
75 : #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
76 : #include <com/sun/star/sheet/GeneralFunction.hpp>
77 :
78 : #include <iostream>
79 : #include <sstream>
80 : #include <vector>
81 :
82 : #define CALC_DEBUG_OUTPUT 0
83 : #define CALC_TEST_PERF 0
84 :
85 : #include "helper/debughelper.hxx"
86 : #include "helper/qahelper.hxx"
87 :
88 : const int indeterminate = 2;
89 :
90 : using namespace ::com::sun::star;
91 :
92 : using ::std::cout;
93 : using ::std::cerr;
94 : using ::std::endl;
95 : using ::std::vector;
96 :
97 : namespace {
98 :
99 156 : class Test : public test::BootstrapFixture {
100 : public:
101 : Test();
102 :
103 : virtual void setUp();
104 : virtual void tearDown();
105 :
106 : /**
107 : * Basic performance regression test. Pick some actions that *should* take
108 : * only a fraction of a second to complete, and make sure they stay that
109 : * way. We set the threshold to 1 second for each action which should be
110 : * large enough to accommodate slower machines or machines with high load.
111 : */
112 : void testPerf();
113 : void testCollator();
114 : void testRangeList();
115 : void testInput();
116 : void testFormulaHashAndTag();
117 : void testFuncSUM();
118 : void testFuncPRODUCT();
119 : void testFuncN();
120 : void testFuncCOUNTIF();
121 : void testFuncNUMBERVALUE();
122 : void testFuncVLOOKUP();
123 : void testFuncMATCH();
124 : void testFuncCELL();
125 : void testFuncDATEDIF();
126 : void testFuncINDIRECT();
127 : void testFuncIFERROR();
128 : void testCopyToDocument();
129 : /**
130 : * Make sure the SHEETS function gets properly updated during sheet
131 : * insertion and removal.
132 : */
133 : void testSheetsFunc();
134 : void testVolatileFunc();
135 :
136 : void testHorizontalIterator();
137 :
138 : /**
139 : * Basic test for formula dependency tracking.
140 : */
141 : void testFormulaDepTracking();
142 :
143 : /**
144 : * Another test for formula dependency tracking, inspired by fdo#56278.
145 : */
146 : void testFormulaDepTracking2();
147 :
148 : /**
149 : * More direct test for cell broadcaster management, used to track formula
150 : * dependencies.
151 : */
152 : void testCellBroadcaster();
153 :
154 : void testFuncParam();
155 : void testNamedRange();
156 : void testCSV();
157 : void testMatrix();
158 : void testEnterMixedMatrix();
159 :
160 : /**
161 : * Basic test for pivot tables.
162 : */
163 : void testPivotTable();
164 :
165 : /**
166 : * Test against unwanted automatic format detection on field names and
167 : * field members in pivot tables.
168 : */
169 : void testPivotTableLabels();
170 :
171 : /**
172 : * Make sure that we set cells displaying date values numeric cells,
173 : * rather than text cells. Grouping by date or number functionality
174 : * depends on this.
175 : */
176 : void testPivotTableDateLabels();
177 :
178 : /**
179 : * Test for pivot table's filtering functionality by page fields.
180 : */
181 : void testPivotTableFilters();
182 :
183 : /**
184 : * Test for pivot table's named source range.
185 : */
186 : void testPivotTableNamedSource();
187 :
188 : /**
189 : * Test for pivot table cache. Each dimension in the pivot cache stores
190 : * only unique values that are sorted in ascending order.
191 : */
192 : void testPivotTableCache();
193 :
194 : /**
195 : * Test for pivot table containing data fields that reference the same
196 : * source field but different functions.
197 : */
198 : void testPivotTableDuplicateDataFields();
199 :
200 : void testPivotTableNormalGrouping();
201 : void testPivotTableNumberGrouping();
202 : void testPivotTableDateGrouping();
203 : void testPivotTableEmptyRows();
204 : void testPivotTableTextNumber();
205 :
206 : /**
207 : * Test for checking that pivot table treats strings in a case insensitive
208 : * manner.
209 : */
210 : void testPivotTableCaseInsensitiveStrings();
211 :
212 : /**
213 : * Test for pivot table's handling of double-precision numbers that are
214 : * very close together.
215 : */
216 : void testPivotTableNumStability();
217 :
218 : /**
219 : * Test for pivot table that include field with various non-default field
220 : * refrences.
221 : */
222 : void testPivotTableFieldReference();
223 :
224 : /**
225 : * Test pivot table functionality performed via ScDBDocFunc.
226 : */
227 : void testPivotTableDocFunc();
228 :
229 : void testSheetCopy();
230 : void testSheetMove();
231 : void testExternalRef();
232 : void testExternalRefFunctions();
233 : void testDataArea();
234 : void testAutofilter();
235 : void testCopyPaste();
236 : void testMergedCells();
237 : void testUpdateReference();
238 : void testSearchCells();
239 : void testSharedFormulas();
240 : void testFormulaPosition();
241 :
242 : /**
243 : * Make sure the sheet streams are invalidated properly.
244 : */
245 : void testStreamValid();
246 :
247 : /**
248 : * Test built-in cell functions to make sure their categories and order
249 : * are correct.
250 : */
251 : void testFunctionLists();
252 :
253 : void testGraphicsInGroup();
254 : void testGraphicsOnSheetMove();
255 :
256 : void testPostIts();
257 :
258 : /**
259 : * Test toggling relative/absolute flag of cell and cell range references.
260 : * This corresponds with hitting Shift-F4 while the cursor is on a formula
261 : * cell.
262 : */
263 : void testToggleRefFlag();
264 :
265 : /**
266 : * Test to make sure correct precedent / dependent cells are obtained when
267 : * preparing to jump to them.
268 : */
269 : void testJumpToPrecedentsDependents();
270 :
271 : void testSetBackgroundColor();
272 : void testRenameTable();
273 :
274 : void testAutoFill();
275 : void testCopyPasteFormulas();
276 : void testCopyPasteFormulasExternalDoc();
277 :
278 : void testFindAreaPosVertical();
279 : void testFindAreaPosColRight();
280 : void testSort();
281 : void testSortWithFormulaRefs();
282 : void testShiftCells();
283 : void testDeleteRow();
284 : void testDeleteCol();
285 : void testAnchoredRotatedShape();
286 : void testCellTextWidth();
287 :
288 : /**
289 : * Test formula & formula grouping
290 : */
291 : void testFormulaGrouping();
292 : void testCondFormatINSDEL();
293 :
294 2 : CPPUNIT_TEST_SUITE(Test);
295 : #if CALC_TEST_PERF
296 : CPPUNIT_TEST(testPerf);
297 : #endif
298 1 : CPPUNIT_TEST(testCollator);
299 1 : CPPUNIT_TEST(testRangeList);
300 1 : CPPUNIT_TEST(testInput);
301 1 : CPPUNIT_TEST(testFormulaHashAndTag);
302 1 : CPPUNIT_TEST(testFuncSUM);
303 1 : CPPUNIT_TEST(testFuncPRODUCT);
304 1 : CPPUNIT_TEST(testFuncN);
305 1 : CPPUNIT_TEST(testFuncCOUNTIF);
306 1 : CPPUNIT_TEST(testFuncNUMBERVALUE);
307 1 : CPPUNIT_TEST(testFuncVLOOKUP);
308 1 : CPPUNIT_TEST(testFuncMATCH);
309 1 : CPPUNIT_TEST(testFuncCELL);
310 1 : CPPUNIT_TEST(testFuncDATEDIF);
311 1 : CPPUNIT_TEST(testFuncINDIRECT);
312 1 : CPPUNIT_TEST(testFuncIFERROR);
313 1 : CPPUNIT_TEST(testCopyToDocument);
314 1 : CPPUNIT_TEST(testSheetsFunc);
315 1 : CPPUNIT_TEST(testVolatileFunc);
316 1 : CPPUNIT_TEST(testHorizontalIterator);
317 1 : CPPUNIT_TEST(testFormulaDepTracking);
318 1 : CPPUNIT_TEST(testFormulaDepTracking2);
319 1 : CPPUNIT_TEST(testCellBroadcaster);
320 1 : CPPUNIT_TEST(testFuncParam);
321 1 : CPPUNIT_TEST(testNamedRange);
322 1 : CPPUNIT_TEST(testCSV);
323 1 : CPPUNIT_TEST(testMatrix);
324 1 : CPPUNIT_TEST(testEnterMixedMatrix);
325 1 : CPPUNIT_TEST(testPivotTable);
326 1 : CPPUNIT_TEST(testPivotTableLabels);
327 1 : CPPUNIT_TEST(testPivotTableDateLabels);
328 1 : CPPUNIT_TEST(testPivotTableFilters);
329 1 : CPPUNIT_TEST(testPivotTableNamedSource);
330 1 : CPPUNIT_TEST(testPivotTableCache);
331 1 : CPPUNIT_TEST(testPivotTableDuplicateDataFields);
332 1 : CPPUNIT_TEST(testPivotTableNormalGrouping);
333 1 : CPPUNIT_TEST(testPivotTableNumberGrouping);
334 1 : CPPUNIT_TEST(testPivotTableDateGrouping);
335 1 : CPPUNIT_TEST(testPivotTableEmptyRows);
336 1 : CPPUNIT_TEST(testPivotTableTextNumber);
337 1 : CPPUNIT_TEST(testPivotTableCaseInsensitiveStrings);
338 1 : CPPUNIT_TEST(testPivotTableNumStability);
339 1 : CPPUNIT_TEST(testPivotTableFieldReference);
340 1 : CPPUNIT_TEST(testPivotTableDocFunc);
341 1 : CPPUNIT_TEST(testSheetCopy);
342 1 : CPPUNIT_TEST(testSheetMove);
343 1 : CPPUNIT_TEST(testExternalRef);
344 1 : CPPUNIT_TEST(testExternalRefFunctions);
345 1 : CPPUNIT_TEST(testDataArea);
346 1 : CPPUNIT_TEST(testGraphicsInGroup);
347 1 : CPPUNIT_TEST(testGraphicsOnSheetMove);
348 1 : CPPUNIT_TEST(testPostIts);
349 1 : CPPUNIT_TEST(testStreamValid);
350 1 : CPPUNIT_TEST(testFunctionLists);
351 1 : CPPUNIT_TEST(testToggleRefFlag);
352 1 : CPPUNIT_TEST(testAutofilter);
353 1 : CPPUNIT_TEST(testCopyPaste);
354 1 : CPPUNIT_TEST(testMergedCells);
355 1 : CPPUNIT_TEST(testUpdateReference);
356 1 : CPPUNIT_TEST(testSearchCells);
357 1 : CPPUNIT_TEST(testSharedFormulas);
358 1 : CPPUNIT_TEST(testFormulaPosition);
359 1 : CPPUNIT_TEST(testJumpToPrecedentsDependents);
360 1 : CPPUNIT_TEST(testSetBackgroundColor);
361 1 : CPPUNIT_TEST(testRenameTable);
362 1 : CPPUNIT_TEST(testAutoFill);
363 1 : CPPUNIT_TEST(testCopyPasteFormulas);
364 1 : CPPUNIT_TEST(testCopyPasteFormulasExternalDoc);
365 1 : CPPUNIT_TEST(testFindAreaPosVertical);
366 1 : CPPUNIT_TEST(testFindAreaPosColRight);
367 1 : CPPUNIT_TEST(testSort);
368 1 : CPPUNIT_TEST(testSortWithFormulaRefs);
369 1 : CPPUNIT_TEST(testShiftCells);
370 1 : CPPUNIT_TEST(testDeleteRow);
371 1 : CPPUNIT_TEST(testDeleteCol);
372 1 : CPPUNIT_TEST(testAnchoredRotatedShape);
373 1 : CPPUNIT_TEST(testCellTextWidth);
374 1 : CPPUNIT_TEST(testFormulaGrouping);
375 1 : CPPUNIT_TEST(testCondFormatINSDEL);
376 2 : CPPUNIT_TEST_SUITE_END();
377 :
378 : private:
379 : ScDocument *m_pDoc;
380 : ScDocShellRef m_xDocShRef;
381 : };
382 :
383 42 : void clearRange(ScDocument* pDoc, const ScRange& rRange)
384 : {
385 42 : ScMarkData aMarkData;
386 42 : aMarkData.SetMarkArea(rRange);
387 : pDoc->DeleteArea(
388 42 : rRange.aStart.Col(), rRange.aStart.Row(),
389 84 : rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, IDF_CONTENTS);
390 42 : }
391 :
392 30 : void printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption)
393 : {
394 30 : SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
395 30 : SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
396 30 : SheetPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1);
397 299 : for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
398 : {
399 976 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
400 : {
401 707 : OUString aVal = pDoc->GetString(nCol, nRow, rRange.aStart.Tab());
402 707 : printer.set(nRow-nRow1, nCol-nCol1, aVal);
403 707 : }
404 : }
405 30 : printer.print(pCaption);
406 30 : }
407 :
408 : template<size_t _Size>
409 17 : ScRange insertRangeData(ScDocument* pDoc, const ScAddress& rPos, const char* aData[][_Size], size_t nRowCount)
410 : {
411 17 : ScRange aRange(rPos);
412 17 : aRange.aEnd.SetCol(rPos.Col()+_Size-1);
413 17 : aRange.aEnd.SetRow(rPos.Row()+nRowCount-1);
414 :
415 17 : clearRange(pDoc, aRange);
416 :
417 62 : for (size_t i = 0; i < _Size; ++i)
418 : {
419 436 : for (size_t j = 0; j < nRowCount; ++j)
420 : {
421 391 : if (!aData[j][i])
422 7 : continue;
423 :
424 384 : SCCOL nCol = i + rPos.Col();
425 384 : SCROW nRow = j + rPos.Row();
426 384 : pDoc->SetString(nCol, nRow, rPos.Tab(), OUString(aData[j][i], strlen(aData[j][i]), RTL_TEXTENCODING_UTF8));
427 : }
428 : }
429 :
430 17 : printRange(pDoc, aRange, "Range data content");
431 17 : return aRange;
432 : }
433 :
434 : /**
435 : * Temporarily set formula grammar.
436 : */
437 : class FormulaGrammarSwitch
438 : {
439 : ScDocument* mpDoc;
440 : formula::FormulaGrammar::Grammar meOldGrammar;
441 : public:
442 3 : FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
443 3 : mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
444 : {
445 3 : mpDoc->SetGrammar(eGrammar);
446 3 : }
447 :
448 3 : ~FormulaGrammarSwitch()
449 : {
450 3 : mpDoc->SetGrammar(meOldGrammar);
451 3 : }
452 : };
453 :
454 : class MeasureTimeSwitch
455 : {
456 : double& mrDiff;
457 : TimeValue maTimeBefore;
458 : public:
459 0 : MeasureTimeSwitch(double& rDiff) : mrDiff(rDiff)
460 : {
461 0 : mrDiff = 9999.0;
462 0 : osl_getSystemTime(&maTimeBefore);
463 0 : }
464 :
465 0 : ~MeasureTimeSwitch()
466 : {
467 : TimeValue aTimeAfter;
468 0 : osl_getSystemTime(&aTimeAfter);
469 0 : mrDiff = getTimeDiff(aTimeAfter, maTimeBefore);
470 0 : }
471 :
472 0 : double getTimeDiff(const TimeValue& t1, const TimeValue& t2) const
473 : {
474 0 : double tv1 = t1.Seconds;
475 0 : double tv2 = t2.Seconds;
476 0 : tv1 += t1.Nanosec / 1000000000.0;
477 0 : tv2 += t2.Nanosec / 1000000000.0;
478 :
479 0 : return tv1 - tv2;
480 : }
481 : };
482 :
483 78 : Test::Test()
484 78 : : m_pDoc(0)
485 : {
486 78 : }
487 :
488 78 : void Test::setUp()
489 : {
490 78 : BootstrapFixture::setUp();
491 :
492 78 : ScDLL::Init();
493 156 : m_xDocShRef = new ScDocShell(
494 : SFXMODEL_STANDARD |
495 : SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
496 156 : SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
497 :
498 78 : m_xDocShRef->DoInitUnitTest();
499 78 : m_pDoc = m_xDocShRef->GetDocument();
500 78 : }
501 :
502 78 : void Test::tearDown()
503 : {
504 78 : m_xDocShRef.Clear();
505 78 : BootstrapFixture::tearDown();
506 78 : }
507 :
508 : #define PERF_ASSERT(df,scale,time,message) \
509 : do { \
510 : double dfscaled = df / scale; \
511 : if ((dfscaled) >= (time)) \
512 : { \
513 : std::ostringstream os; \
514 : os << message << " took " << dfscaled << " pseudo-cycles (" << df << " real-time seconds), expected: " << time << " pseudo-cycles."; \
515 : CPPUNIT_FAIL(os.str().c_str()); \
516 : } \
517 : } while (0)
518 :
519 0 : void Test::testPerf()
520 : {
521 0 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
522 :
523 : // First do a set of simple operations to try to work out
524 : // how fast (or not) this particular machine is:
525 : double scale;
526 : {
527 0 : MeasureTimeSwitch aTime(scale);
528 0 : for (int i = 0; i < 10000000; ++i)
529 : {
530 : // Bang on the allocator
531 0 : volatile ScRange *pRange = new ScRange (ScAddress (0,0,0));
532 : // Calc does quite a bit of string conversion
533 0 : volatile double it = OUString::number ((double)i/253.0).toDouble();
534 : // Do we have floating point math ?
535 0 : volatile double another = rtl::math::sin (it);
536 0 : (void)another;
537 0 : delete pRange;
538 0 : }
539 : }
540 0 : printf("CPU scale factor %g\n", scale);
541 :
542 : // FIXME: we should check if this already took too long
543 : // and if so not run the perf. tests to have pity on some
544 : // slow ARM machines - I think.
545 :
546 : // to make the numbers more round and helpful,
547 : // but the calculation of scale reasonably precise.
548 0 : scale /= 100000.0;
549 :
550 : double diff;
551 :
552 : // Clearing an already empty sheet should finish in a fraction of a
553 : // second. Flag failure if it takes more than one second. Clearing 100
554 : // columns should be large enough to flag if something goes wrong.
555 : {
556 0 : MeasureTimeSwitch aTime(diff);
557 0 : clearRange(m_pDoc, ScRange(0,0,0,99,MAXROW,0));
558 : }
559 0 : PERF_ASSERT(diff, scale, 1.0, "Clearing an empty sheet");
560 :
561 : {
562 : // Switch to R1C1 to make it easier to input relative references in multiple cells.
563 0 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
564 :
565 : // Insert formulas in B1:B100000. This shouldn't take long, but may take
566 : // close to a second on a slower machine. We don't measure this yet, for
567 : // now.
568 0 : for (SCROW i = 0; i < 100000; ++i)
569 0 : m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
570 :
571 : // Now, Delete B2:B100000. This should complete in a fraction of a second
572 : // (0.06 sec on my machine).
573 : {
574 0 : MeasureTimeSwitch aTime(diff);
575 0 : clearRange(m_pDoc, ScRange(1,1,0,1,99999,0));
576 : }
577 0 : PERF_ASSERT(diff, scale, 2000, "Removal of a large array of formula cells");
578 : }
579 :
580 0 : clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
581 0 : CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
582 0 : CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
583 :
584 : {
585 : // Measure the performance of repeat-pasting a single cell to a large
586 : // cell range, undoing it, and redoing it.
587 :
588 0 : ScAddress aPos(0,0,0);
589 0 : m_pDoc->SetString(aPos, "test");
590 0 : ScMarkData aMark;
591 0 : aMark.SelectOneTable(0);
592 :
593 : // Copy cell A1 to clipboard.
594 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
595 0 : ScClipParam aParam(aPos, false);
596 0 : m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
597 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
598 :
599 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
600 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
601 0 : m_pDoc->CopyToDocument(ScRange(aPos), IDF_CONTENTS, false, pUndoDoc, &aMark);
602 :
603 : // Paste it to A2:A100000, and measure its duration.
604 0 : ScRange aPasteRange(0,1,0,0,99999,0);
605 0 : aMark.SetMarkArea(aPasteRange);
606 :
607 : {
608 0 : MeasureTimeSwitch aTime(diff);
609 0 : m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
610 : }
611 0 : PERF_ASSERT(diff, scale, 1500.0, "Pasting a single cell to A2:A100000");
612 :
613 0 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
614 0 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
615 0 : m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
616 :
617 : // Create an undo object for this.
618 0 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
619 0 : ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
620 :
621 : // Make sure it did what it's supposed to do.
622 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
623 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
624 :
625 : {
626 0 : MeasureTimeSwitch aTime(diff);
627 0 : aUndo.Undo();
628 : }
629 0 : PERF_ASSERT(diff, scale, 500.0, "Undoing a pasting of a cell to A2:A100000");
630 :
631 : // Make sure it's really undone.
632 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, m_pDoc->GetCellType(aPos));
633 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
634 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
635 :
636 : // Now redo.
637 : {
638 0 : MeasureTimeSwitch aTime(diff);
639 0 : aUndo.Redo();
640 : }
641 0 : PERF_ASSERT(diff, scale, 1000.0, "Redoing a pasting of a cell to A2:A100000");
642 :
643 : // Make sure it's really redone.
644 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
645 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
646 : }
647 :
648 0 : clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
649 0 : CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
650 0 : CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
651 :
652 : {
653 : // Measure the performance of repeat-pasting 2 adjacent cells to a
654 : // large cell range, undoing it, and redoing it. The bottom one of
655 : // the two source cells is empty.
656 :
657 0 : ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
658 :
659 0 : ScAddress aPos(0,0,0);
660 0 : m_pDoc->SetValue(aPos, 12);
661 0 : ScMarkData aMark;
662 0 : aMark.SetMarkArea(aSrcRange);
663 :
664 : // Copy to clipboard.
665 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
666 0 : ScClipParam aParam(aSrcRange, false);
667 0 : m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
668 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
669 :
670 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
671 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
672 0 : m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
673 :
674 : // Paste it to A3:A100001, and measure its duration.
675 0 : ScRange aPasteRange(0,2,0,0,100000,0);
676 0 : aMark.SetMarkArea(aPasteRange);
677 :
678 : {
679 0 : MeasureTimeSwitch aTime(diff);
680 0 : m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
681 : }
682 0 : PERF_ASSERT(diff, scale, 1000.0, "Pasting A1:A2 to A3:A100001");
683 :
684 0 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
685 0 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
686 0 : m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
687 :
688 : // Create an undo object for this.
689 0 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
690 0 : ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
691 :
692 : // Make sure it did what it's supposed to do.
693 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
694 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
695 0 : ScAddress aTmp = aPasteRange.aStart;
696 0 : aTmp.IncRow();
697 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
698 :
699 : {
700 0 : MeasureTimeSwitch aTime(diff);
701 0 : aUndo.Undo();
702 : }
703 0 : PERF_ASSERT(diff, scale, 500.0, "Undoing");
704 :
705 : // Make sure it's really undone.
706 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(aPos));
707 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
708 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
709 :
710 : // Now redo.
711 : {
712 0 : MeasureTimeSwitch aTime(diff);
713 0 : aUndo.Redo();
714 : }
715 0 : PERF_ASSERT(diff, scale, 800.0, "Redoing");
716 :
717 : // Make sure it's really redone.
718 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
719 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
720 : }
721 :
722 0 : clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
723 0 : CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
724 0 : CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
725 :
726 : {
727 : // Measure the performance of repeat-pasting 2 adjacent cells to a
728 : // large cell range, undoing it, and redoing it. The bottom one of
729 : // the two source cells is empty. In this scenario, the non-empty
730 : // cell is a formula cell referencing a cell to the right, which
731 : // inserts a broadcaster to cell it references. So it has a higher
732 : // overhead than the previous scenario.
733 :
734 0 : ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
735 :
736 0 : ScAddress aPos(0,0,0);
737 0 : m_pDoc->SetString(aPos, "=B1");
738 0 : ScMarkData aMark;
739 0 : aMark.SetMarkArea(aSrcRange);
740 :
741 : // Copy to clipboard.
742 0 : ScDocument aClipDoc(SCDOCMODE_CLIP);
743 0 : ScClipParam aParam(aSrcRange, false);
744 0 : m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
745 0 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
746 :
747 0 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
748 0 : pUndoDoc->InitUndo(m_pDoc, 0, 0);
749 0 : m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
750 :
751 : // Paste it to A3:A50001, and measure its duration.
752 0 : ScRange aPasteRange(0,2,0,0,50000,0);
753 0 : aMark.SetMarkArea(aPasteRange);
754 :
755 : {
756 0 : MeasureTimeSwitch aTime(diff);
757 0 : m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
758 : }
759 0 : PERF_ASSERT(diff, scale, 2000.0, "Pasting");
760 :
761 0 : ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
762 0 : pRedoDoc->InitUndo(m_pDoc, 0, 0);
763 0 : m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
764 :
765 : // Create an undo object for this.
766 0 : ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
767 0 : ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
768 :
769 : // Make sure it did what it's supposed to do.
770 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
771 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
772 0 : ScAddress aTmp = aPasteRange.aStart;
773 0 : aTmp.IncRow();
774 0 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
775 :
776 : #if 0 // TODO: Undo and redo of this scenario is currently not fast enough to be tested reliably.
777 : {
778 : MeasureTimeSwitch aTime(diff);
779 : aUndo.Undo();
780 : }
781 : PERF_ASSERT(diff, scale, 1.0, "Undoing");
782 :
783 : // Make sure it's really undone.
784 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPos));
785 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
786 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
787 :
788 : // Now redo.
789 : {
790 : MeasureTimeSwitch aTime(diff);
791 : aUndo.Redo();
792 : }
793 : PERF_ASSERT(diff, scale, 1.0, "Redoing");
794 :
795 : // Make sure it's really redone.
796 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
797 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
798 : #endif
799 : }
800 :
801 0 : m_pDoc->DeleteTab(0);
802 0 : }
803 :
804 1 : void Test::testCollator()
805 : {
806 1 : OUString s1("A");
807 2 : OUString s2("B");
808 1 : CollatorWrapper* p = ScGlobal::GetCollator();
809 1 : sal_Int32 nRes = p->compareString(s1, s2);
810 2 : CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
811 1 : }
812 :
813 1 : void Test::testRangeList()
814 : {
815 1 : m_pDoc->InsertTab(0, "foo");
816 :
817 1 : ScRangeList aRL;
818 1 : aRL.Append(ScRange(1,1,0,3,10,0));
819 1 : CPPUNIT_ASSERT_MESSAGE("List should have one range.", aRL.size() == 1);
820 1 : const ScRange* p = aRL[0];
821 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
822 1 : CPPUNIT_ASSERT_MESSAGE("Wrong range.", p->aStart == ScAddress(1,1,0) && p->aEnd == ScAddress(3,10,0));
823 :
824 : // TODO: Add more tests here.
825 :
826 1 : m_pDoc->DeleteTab(0);
827 1 : }
828 :
829 1 : void Test::testInput()
830 : {
831 1 : OUString aTabName("foo");
832 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
833 1 : m_pDoc->InsertTab (0, aTabName));
834 :
835 2 : OUString numstr("'10.5");
836 2 : OUString str("'apple'");
837 2 : OUString test;
838 :
839 1 : m_pDoc->SetString(0, 0, 0, numstr);
840 1 : test = m_pDoc->GetString(0, 0, 0);
841 1 : bool bTest = test == "10.5";
842 1 : CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
843 1 : m_pDoc->SetString(0, 0, 0, str);
844 1 : test = m_pDoc->GetString(0, 0, 0);
845 1 : bTest = test == "'apple'";
846 1 : CPPUNIT_ASSERT_MESSAGE("Text content should have retained the first apostrophe.", bTest);
847 :
848 : // Customized string handling policy.
849 1 : ScSetStringParam aParam;
850 1 : aParam.setTextInput();
851 1 : m_pDoc->SetString(0, 0, 0, "000123", &aParam);
852 1 : test = m_pDoc->GetString(0, 0, 0);
853 1 : CPPUNIT_ASSERT_MESSAGE("Text content should have been treated as string, not number.", test == "000123");
854 :
855 2 : m_pDoc->DeleteTab(0);
856 1 : }
857 :
858 1 : void Test::testFuncSUM()
859 : {
860 1 : OUString aTabName("foo");
861 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
862 1 : m_pDoc->InsertTab (0, aTabName));
863 :
864 1 : double val = 1;
865 : double result;
866 1 : m_pDoc->SetValue (0, 0, 0, val);
867 1 : m_pDoc->SetValue (0, 1, 0, val);
868 1 : m_pDoc->SetString (0, 2, 0, OUString("=SUM(A1:A2)"));
869 1 : m_pDoc->CalcAll();
870 1 : m_pDoc->GetValue (0, 2, 0, result);
871 1 : CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 2.0);
872 :
873 1 : m_pDoc->DeleteTab(0);
874 1 : }
875 :
876 1 : void Test::testFuncPRODUCT()
877 : {
878 1 : OUString aTabName("foo");
879 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
880 1 : m_pDoc->InsertTab (0, aTabName));
881 :
882 1 : double val = 1;
883 : double result;
884 1 : m_pDoc->SetValue(0, 0, 0, val);
885 1 : val = 2;
886 1 : m_pDoc->SetValue(0, 1, 0, val);
887 1 : val = 3;
888 1 : m_pDoc->SetValue(0, 2, 0, val);
889 1 : m_pDoc->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
890 1 : m_pDoc->CalcAll();
891 1 : m_pDoc->GetValue(0, 3, 0, result);
892 1 : CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT failed", result == 6.0);
893 :
894 1 : m_pDoc->SetString(0, 4, 0, OUString("=PRODUCT({1;2;3})"));
895 1 : m_pDoc->CalcAll();
896 1 : m_pDoc->GetValue(0, 4, 0, result);
897 1 : CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT with inline array failed", result == 6.0);
898 :
899 1 : m_pDoc->DeleteTab(0);
900 1 : }
901 :
902 1 : void Test::testFuncN()
903 : {
904 1 : OUString aTabName("foo");
905 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
906 1 : m_pDoc->InsertTab (0, aTabName));
907 :
908 : double result;
909 :
910 : // Clear the area first.
911 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
912 :
913 : // Put values to reference.
914 1 : double val = 0;
915 1 : m_pDoc->SetValue(0, 0, 0, val);
916 1 : m_pDoc->SetString(0, 2, 0, OUString("Text"));
917 1 : val = 1;
918 1 : m_pDoc->SetValue(0, 3, 0, val);
919 1 : val = -1;
920 1 : m_pDoc->SetValue(0, 4, 0, val);
921 1 : val = 12.3;
922 1 : m_pDoc->SetValue(0, 5, 0, val);
923 1 : m_pDoc->SetString(0, 6, 0, OUString("'12.3"));
924 :
925 : // Cell references
926 1 : m_pDoc->SetString(1, 0, 0, OUString("=N(A1)"));
927 1 : m_pDoc->SetString(1, 1, 0, OUString("=N(A2)"));
928 1 : m_pDoc->SetString(1, 2, 0, OUString("=N(A3)"));
929 1 : m_pDoc->SetString(1, 3, 0, OUString("=N(A4)"));
930 1 : m_pDoc->SetString(1, 4, 0, OUString("=N(A5)"));
931 1 : m_pDoc->SetString(1, 5, 0, OUString("=N(A6)"));
932 1 : m_pDoc->SetString(1, 6, 0, OUString("=N(A9)"));
933 :
934 : // In-line values
935 1 : m_pDoc->SetString(1, 7, 0, OUString("=N(0)"));
936 1 : m_pDoc->SetString(1, 8, 0, OUString("=N(1)"));
937 1 : m_pDoc->SetString(1, 9, 0, OUString("=N(-1)"));
938 1 : m_pDoc->SetString(1, 10, 0, OUString("=N(123)"));
939 1 : m_pDoc->SetString(1, 11, 0, OUString("=N(\"\")"));
940 1 : m_pDoc->SetString(1, 12, 0, OUString("=N(\"12\")"));
941 1 : m_pDoc->SetString(1, 13, 0, OUString("=N(\"foo\")"));
942 :
943 : // Range references
944 1 : m_pDoc->SetString(2, 2, 0, OUString("=N(A1:A8)"));
945 1 : m_pDoc->SetString(2, 3, 0, OUString("=N(A1:A8)"));
946 1 : m_pDoc->SetString(2, 4, 0, OUString("=N(A1:A8)"));
947 1 : m_pDoc->SetString(2, 5, 0, OUString("=N(A1:A8)"));
948 :
949 : // Calculate and check the results.
950 1 : m_pDoc->CalcAll();
951 : double checks1[] = {
952 : 0, 0, 0, 1, -1, 12.3, 0, // cell reference
953 : 0, 1, -1, 123, 0, 0, 0 // in-line values
954 1 : };
955 15 : for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
956 : {
957 14 : m_pDoc->GetValue(1, i, 0, result);
958 14 : bool bGood = result == checks1[i];
959 14 : if (!bGood)
960 : {
961 0 : cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
962 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
963 : }
964 : }
965 : double checks2[] = {
966 : 0, 1, -1, 12.3 // range references
967 1 : };
968 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
969 : {
970 4 : m_pDoc->GetValue(1, i+2, 0, result);
971 4 : bool bGood = result == checks2[i];
972 4 : if (!bGood)
973 : {
974 0 : cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
975 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
976 : }
977 : }
978 :
979 1 : m_pDoc->DeleteTab(0);
980 1 : }
981 :
982 1 : void Test::testFuncCOUNTIF()
983 : {
984 : // COUNTIF (test case adopted from OOo i#36381)
985 :
986 1 : OUString aTabName("foo");
987 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
988 1 : m_pDoc->InsertTab (0, aTabName));
989 :
990 : // Empty A1:A39 first.
991 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
992 :
993 : // Raw data (rows 1 through 9)
994 : const char* aData[] = {
995 : "1999",
996 : "2000",
997 : "0",
998 : "0",
999 : "0",
1000 : "2002",
1001 : "2001",
1002 : "X",
1003 : "2002"
1004 1 : };
1005 :
1006 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
1007 10 : for (SCROW i = 0; i < nRows; ++i)
1008 9 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1009 :
1010 1 : printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
1011 :
1012 : // formulas and results
1013 : struct {
1014 : const char* pFormula; double fResult;
1015 : } aChecks[] = {
1016 : { "=COUNTIF(A1:A12;1999)", 1 },
1017 : { "=COUNTIF(A1:A12;2002)", 2 },
1018 : { "=COUNTIF(A1:A12;1998)", 0 },
1019 : { "=COUNTIF(A1:A12;\">=1999\")", 5 },
1020 : { "=COUNTIF(A1:A12;\">1999\")", 4 },
1021 : { "=COUNTIF(A1:A12;\"<2001\")", 5 },
1022 : { "=COUNTIF(A1:A12;\">0\")", 5 },
1023 : { "=COUNTIF(A1:A12;\">=0\")", 8 },
1024 : { "=COUNTIF(A1:A12;0)", 3 },
1025 : { "=COUNTIF(A1:A12;\"X\")", 1 },
1026 : { "=COUNTIF(A1:A12;)", 3 }
1027 1 : };
1028 :
1029 1 : nRows = SAL_N_ELEMENTS(aChecks);
1030 12 : for (SCROW i = 0; i < nRows; ++i)
1031 : {
1032 11 : SCROW nRow = 20 + i;
1033 11 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
1034 : }
1035 1 : m_pDoc->CalcAll();
1036 :
1037 12 : for (SCROW i = 0; i < nRows; ++i)
1038 : {
1039 : double result;
1040 11 : SCROW nRow = 20 + i;
1041 11 : m_pDoc->GetValue(0, nRow, 0, result);
1042 11 : bool bGood = result == aChecks[i].fResult;
1043 11 : if (!bGood)
1044 : {
1045 0 : cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
1046 0 : << " expected=" << aChecks[i].fResult << " actual=" << result << endl;
1047 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
1048 : }
1049 : }
1050 :
1051 : // Don't count empty strings when searching for a number.
1052 :
1053 : // Clear A1:A2.
1054 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
1055 :
1056 1 : m_pDoc->SetString(0, 0, 0, OUString("=\"\""));
1057 1 : m_pDoc->SetString(0, 1, 0, OUString("=COUNTIF(A1;1)"));
1058 1 : m_pDoc->CalcAll();
1059 :
1060 1 : double result = m_pDoc->GetValue(0, 1, 0);
1061 1 : CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
1062 :
1063 1 : m_pDoc->DeleteTab(0);
1064 1 : }
1065 :
1066 1 : void Test::testFuncIFERROR()
1067 : {
1068 : // IFERROR/IFNA (fdo#56124)
1069 :
1070 1 : OUString aTabName("foo");
1071 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1072 1 : m_pDoc->InsertTab (0, aTabName));
1073 :
1074 : // Empty A1:A39 first.
1075 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
1076 :
1077 : // Raw data (rows 1 through 12)
1078 : const char* aData[] = {
1079 : "1",
1080 : "e",
1081 : "=SQRT(4)",
1082 : "=SQRT(-2)",
1083 : "=A4",
1084 : "=1/0",
1085 : "=NA()",
1086 : "bar",
1087 : "4",
1088 : "gee",
1089 : "=1/0",
1090 : "23"
1091 1 : };
1092 :
1093 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
1094 13 : for (SCROW i = 0; i < nRows; ++i)
1095 12 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1096 :
1097 1 : printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
1098 :
1099 : // formulas and results
1100 : struct {
1101 : const char* pFormula; const char* pResult;
1102 : } aChecks[] = {
1103 : { "=IFERROR(A1;9)", "1" },
1104 : { "=IFERROR(A2;9)", "e" },
1105 : { "=IFERROR(A3;9)", "2" },
1106 : { "=IFERROR(A4;-7)", "-7" },
1107 : { "=IFERROR(A5;-7)", "-7" },
1108 : { "=IFERROR(A6;-7)", "-7" },
1109 : { "=IFERROR(A7;-7)", "-7" },
1110 : { "=IFNA(A6;9)", "#DIV/0!" },
1111 : { "=IFNA(A7;-7)", "-7" },
1112 : { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)", "4" },
1113 : { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
1114 : { "{=IFERROR(3*A11:A12;1998)}[0]", "1998" }, // um.. this is not the correct way to insert a
1115 : { "{=IFERROR(3*A11:A12;1998)}[1]", "69" } // matrix formula, just a place holder, see below
1116 1 : };
1117 :
1118 1 : nRows = SAL_N_ELEMENTS(aChecks);
1119 12 : for (SCROW i = 0; i < nRows-2; ++i)
1120 : {
1121 11 : SCROW nRow = 20 + i;
1122 11 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
1123 : }
1124 :
1125 : // Create a matrix range in last two rows of the range above, actual data
1126 : // of the placeholders.
1127 2 : ScMarkData aMark;
1128 1 : aMark.SelectOneTable(0);
1129 1 : m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)", NULL);
1130 :
1131 1 : m_pDoc->CalcAll();
1132 :
1133 14 : for (SCROW i = 0; i < nRows; ++i)
1134 : {
1135 13 : SCROW nRow = 20 + i;
1136 13 : OUString aResult = m_pDoc->GetString(0, nRow, 0);
1137 26 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
1138 13 : aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
1139 13 : }
1140 :
1141 2 : m_pDoc->DeleteTab(0);
1142 1 : }
1143 :
1144 1 : void Test::testFuncNUMBERVALUE()
1145 : {
1146 : // NUMBERVALUE fdo#57180
1147 :
1148 1 : OUString aTabName("foo");
1149 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1150 1 : m_pDoc->InsertTab (0, aTabName));
1151 :
1152 : // Empty A1:A39 first.
1153 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
1154 :
1155 : // Raw data (rows 1 through 6)
1156 : const char* aData[] = {
1157 : "1ag9a9b9",
1158 : "1ag34 5g g6 78b9%%",
1159 : "1 234d56E-2",
1160 : "d4",
1161 : "54.4",
1162 : "1a2b3e1%"
1163 1 : };
1164 :
1165 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
1166 7 : for (SCROW i = 0; i < nRows; ++i)
1167 6 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1168 :
1169 1 : printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
1170 :
1171 : // formulas and results
1172 : struct {
1173 : const char* pFormula; const char* pResult;
1174 : } aChecks[] = {
1175 : { "=NUMBERVALUE(A1;\"b\";\"ag\")", "199.9" },
1176 : { "=NUMBERVALUE(A2;\"b\";\"ag\")", "134.56789" },
1177 : { "=NUMBERVALUE(A2;\"b\";\"g\")", "#VALUE!" },
1178 : { "=NUMBERVALUE(A3;\"d\")", "12.3456" },
1179 : { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
1180 : { "=NUMBERVALUE(A4;)", "Err:502" },
1181 : { "=NUMBERVALUE(A5;)", "Err:502" },
1182 : { "=NUMBERVALUE(A6;\"b\";\"a\")", "1.23" }
1183 1 : };
1184 :
1185 1 : nRows = SAL_N_ELEMENTS(aChecks);
1186 9 : for (SCROW i = 0; i < nRows; ++i)
1187 : {
1188 8 : SCROW nRow = 20 + i;
1189 8 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
1190 : }
1191 1 : m_pDoc->CalcAll();
1192 :
1193 9 : for (SCROW i = 0; i < nRows; ++i)
1194 : {
1195 8 : SCROW nRow = 20 + i;
1196 8 : OUString aResult = m_pDoc->GetString(0, nRow, 0);
1197 16 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
1198 8 : aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
1199 8 : }
1200 :
1201 1 : m_pDoc->DeleteTab(0);
1202 1 : }
1203 :
1204 1 : void Test::testFuncVLOOKUP()
1205 : {
1206 : // VLOOKUP
1207 :
1208 1 : OUString aTabName("foo");
1209 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1210 1 : m_pDoc->InsertTab (0, aTabName));
1211 :
1212 : // Clear A1:F40.
1213 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
1214 :
1215 : // Raw data
1216 : const char* aData[][2] = {
1217 : { "Key", "Val" },
1218 : { "10", "3" },
1219 : { "20", "4" },
1220 : { "30", "5" },
1221 : { "40", "6" },
1222 : { "50", "7" },
1223 : { "60", "8" },
1224 : { "70", "9" },
1225 : { "B", "10" },
1226 : { "B", "11" },
1227 : { "C", "12" },
1228 : { "D", "13" },
1229 : { "E", "14" },
1230 : { "F", "15" },
1231 : { 0, 0 } // terminator
1232 1 : };
1233 :
1234 : // Insert raw data into A1:B14.
1235 15 : for (SCROW i = 0; aData[i][0]; ++i)
1236 : {
1237 14 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
1238 14 : m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
1239 : }
1240 :
1241 1 : printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
1242 :
1243 : // Formula data
1244 : struct {
1245 : const char* pLookup; const char* pFormula; const char* pRes;
1246 : } aChecks[] = {
1247 : { "Lookup", "Formula", 0 },
1248 : { "12", "=VLOOKUP(D2;A2:B14;2;1)", "3" },
1249 : { "29", "=VLOOKUP(D3;A2:B14;2;1)", "4" },
1250 : { "31", "=VLOOKUP(D4;A2:B14;2;1)", "5" },
1251 : { "45", "=VLOOKUP(D5;A2:B14;2;1)", "6" },
1252 : { "56", "=VLOOKUP(D6;A2:B14;2;1)", "7" },
1253 : { "65", "=VLOOKUP(D7;A2:B14;2;1)", "8" },
1254 : { "78", "=VLOOKUP(D8;A2:B14;2;1)", "9" },
1255 : { "Andy", "=VLOOKUP(D9;A2:B14;2;1)", "#N/A" },
1256 : { "Bruce", "=VLOOKUP(D10;A2:B14;2;1)", "11" },
1257 : { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)", "12" },
1258 : { "David", "=VLOOKUP(D12;A2:B14;2;1)", "13" },
1259 : { "Edward", "=VLOOKUP(D13;A2:B14;2;1)", "14" },
1260 : { "Frank", "=VLOOKUP(D14;A2:B14;2;1)", "15" },
1261 : { "Henry", "=VLOOKUP(D15;A2:B14;2;1)", "15" },
1262 : { "100", "=VLOOKUP(D16;A2:B14;2;1)", "9" },
1263 : { "1000", "=VLOOKUP(D17;A2:B14;2;1)", "9" },
1264 : { "Zena", "=VLOOKUP(D18;A2:B14;2;1)", "15" }
1265 1 : };
1266 :
1267 : // Insert formula data into D1:E18.
1268 19 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1269 : {
1270 18 : m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
1271 18 : m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
1272 : }
1273 1 : m_pDoc->CalcAll();
1274 1 : printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
1275 :
1276 : // Verify results.
1277 19 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1278 : {
1279 18 : if (i == 0)
1280 : // Skip the header row.
1281 1 : continue;
1282 :
1283 17 : OUString aRes = m_pDoc->GetString(4, i, 0);
1284 17 : bool bGood = aRes.equalsAscii(aChecks[i].pRes);
1285 17 : if (!bGood)
1286 : {
1287 0 : cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
1288 0 : << "' expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
1289 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
1290 : }
1291 17 : }
1292 :
1293 1 : m_pDoc->DeleteTab(0);
1294 1 : }
1295 :
1296 : struct NumStrCheck {
1297 : double fVal;
1298 : const char* pRes;
1299 : };
1300 :
1301 : struct StrStrCheck {
1302 : const char* pVal;
1303 : const char* pRes;
1304 : };
1305 :
1306 : template<size_t _DataSize, size_t _FormulaSize, int _Type>
1307 2 : void runTestMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
1308 : {
1309 2 : size_t nDataSize = _DataSize;
1310 26 : for (size_t i = 0; i < nDataSize; ++i)
1311 24 : pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
1312 :
1313 33 : for (size_t i = 0; i < _FormulaSize; ++i)
1314 : {
1315 31 : pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
1316 :
1317 31 : OUStringBuffer aBuf;
1318 31 : aBuf.appendAscii("=MATCH(B");
1319 31 : aBuf.append(static_cast<sal_Int32>(i+1));
1320 31 : aBuf.appendAscii(";A1:A");
1321 31 : aBuf.append(static_cast<sal_Int32>(nDataSize));
1322 31 : aBuf.appendAscii(";");
1323 31 : aBuf.append(static_cast<sal_Int32>(_Type));
1324 31 : aBuf.appendAscii(")");
1325 62 : OUString aFormula = aBuf.makeStringAndClear();
1326 31 : pDoc->SetString(2, i, 0, aFormula);
1327 : }
1328 :
1329 2 : pDoc->CalcAll();
1330 2 : printRange(pDoc, ScRange(0, 0, 0, 2, _FormulaSize-1, 0), "MATCH");
1331 :
1332 : // verify the results.
1333 33 : for (size_t i = 0; i < _FormulaSize; ++i)
1334 : {
1335 31 : OUString aStr = pDoc->GetString(2, i, 0);
1336 31 : if (!aStr.equalsAscii(aChecks[i].pRes))
1337 : {
1338 0 : cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
1339 0 : << " criterion='" << aChecks[i].pVal << "'" << endl;
1340 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
1341 : }
1342 : }
1343 2 : }
1344 :
1345 1 : void Test::testFuncMATCH()
1346 : {
1347 1 : OUString aTabName("foo");
1348 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1349 1 : m_pDoc->InsertTab (0, aTabName));
1350 :
1351 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
1352 : {
1353 : // Ascending in-exact match
1354 :
1355 : // data range (A1:A9)
1356 : const char* aData[] = {
1357 : "1",
1358 : "2",
1359 : "3",
1360 : "4",
1361 : "5",
1362 : "6",
1363 : "7",
1364 : "8",
1365 : "9",
1366 : "B",
1367 : "B",
1368 : "C",
1369 1 : };
1370 :
1371 : // formula (B1:C12)
1372 : StrStrCheck aChecks[] = {
1373 : { "0.8", "#N/A" },
1374 : { "1.2", "1" },
1375 : { "2.3", "2" },
1376 : { "3.9", "3" },
1377 : { "4.1", "4" },
1378 : { "5.99", "5" },
1379 : { "6.1", "6" },
1380 : { "7.2", "7" },
1381 : { "8.569", "8" },
1382 : { "9.59", "9" },
1383 : { "10", "9" },
1384 : { "100", "9" },
1385 : { "Andy", "#N/A" },
1386 : { "Bruce", "11" },
1387 : { "Charlie", "12" }
1388 1 : };
1389 :
1390 1 : runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
1391 : }
1392 :
1393 : {
1394 : // Descending in-exact match
1395 :
1396 : // data range (A1:A9)
1397 : const char* aData[] = {
1398 : "D",
1399 : "C",
1400 : "B",
1401 : "9",
1402 : "8",
1403 : "7",
1404 : "6",
1405 : "5",
1406 : "4",
1407 : "3",
1408 : "2",
1409 : "1"
1410 1 : };
1411 :
1412 : // formula (B1:C12)
1413 : StrStrCheck aChecks[] = {
1414 : { "10", "#N/A" },
1415 : { "8.9", "4" },
1416 : { "7.8", "5" },
1417 : { "6.7", "6" },
1418 : { "5.5", "7" },
1419 : { "4.6", "8" },
1420 : { "3.3", "9" },
1421 : { "2.2", "10" },
1422 : { "1.1", "11" },
1423 : { "0.8", "12" },
1424 : { "0", "12" },
1425 : { "-2", "12" },
1426 : { "Andy", "3" },
1427 : { "Bruce", "2" },
1428 : { "Charlie", "1" },
1429 : { "David", "#N/A" }
1430 1 : };
1431 :
1432 1 : runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
1433 : }
1434 :
1435 1 : m_pDoc->DeleteTab(0);
1436 1 : }
1437 :
1438 1 : void Test::testFuncCELL()
1439 : {
1440 1 : OUString aTabName("foo");
1441 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1442 1 : m_pDoc->InsertTab (0, aTabName));
1443 :
1444 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
1445 :
1446 : {
1447 1 : const char* pContent = "Some random text";
1448 1 : m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
1449 1 : double val = 1.2;
1450 1 : m_pDoc->SetValue(2, 0, 0, val); // Set numeric value to C1;
1451 :
1452 : // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
1453 : StrStrCheck aChecks[] = {
1454 : { "=CELL(\"COL\";C10)", "3" },
1455 : { "=CELL(\"ROW\";C10)", "10" },
1456 : { "=CELL(\"SHEET\";C10)", "1" },
1457 : { "=CELL(\"ADDRESS\";C10)", "$C$10" },
1458 : { "=CELL(\"CONTENTS\";C10)", pContent },
1459 : { "=CELL(\"COLOR\";C10)", "0" },
1460 : { "=CELL(\"TYPE\";C9)", "b" },
1461 : { "=CELL(\"TYPE\";C10)", "l" },
1462 : { "=CELL(\"TYPE\";C1)", "v" },
1463 : { "=CELL(\"PARENTHESES\";C10)", "0" }
1464 1 : };
1465 :
1466 11 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1467 10 : m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
1468 1 : m_pDoc->CalcAll();
1469 :
1470 11 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1471 : {
1472 10 : OUString aVal = m_pDoc->GetString(0, i, 0);
1473 10 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
1474 10 : }
1475 : }
1476 :
1477 1 : m_pDoc->DeleteTab(0);
1478 1 : }
1479 :
1480 : /** See also test case document fdo#44456 sheet cpearson */
1481 1 : void Test::testFuncDATEDIF()
1482 : {
1483 1 : OUString aTabName("foo");
1484 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1485 1 : m_pDoc->InsertTab (0, aTabName));
1486 :
1487 : const char* aData[][5] = {
1488 : { "2007-01-01", "2007-01-10", "d", "9", "=DATEDIF(A1;B1;C1)" } ,
1489 : { "2007-01-01", "2007-01-31", "m", "0", "=DATEDIF(A2;B2;C2)" } ,
1490 : { "2007-01-01", "2007-02-01", "m", "1", "=DATEDIF(A3;B3;C3)" } ,
1491 : { "2007-01-01", "2007-02-28", "m", "1", "=DATEDIF(A4;B4;C4)" } ,
1492 : { "2007-01-01", "2007-12-31", "d", "364", "=DATEDIF(A5;B5;C5)" } ,
1493 : { "2007-01-01", "2007-01-31", "y", "0", "=DATEDIF(A6;B6;C6)" } ,
1494 : { "2007-01-01", "2008-07-01", "d", "547", "=DATEDIF(A7;B7;C7)" } ,
1495 : { "2007-01-01", "2008-07-01", "m", "18", "=DATEDIF(A8;B8;C8)" } ,
1496 : { "2007-01-01", "2008-07-01", "ym", "6", "=DATEDIF(A9;B9;C9)" } ,
1497 : { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
1498 : { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
1499 : { "2007-01-01", "2007-01-31", "md", "30", "=DATEDIF(A12;B12;C12)" } ,
1500 : { "2007-02-01", "2009-03-01", "md", "0", "=DATEDIF(A13;B13;C13)" } ,
1501 : { "2008-02-01", "2009-03-01", "md", "0", "=DATEDIF(A14;B14;C14)" } ,
1502 : { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" } // fail date1 > date2
1503 1 : };
1504 :
1505 1 : clearRange( m_pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
1506 1 : ScAddress aPos(0,0,0);
1507 1 : ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
1508 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
1509 :
1510 1 : m_pDoc->CalcAll();
1511 :
1512 16 : for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
1513 : {
1514 15 : OUString aVal = m_pDoc->GetString( 4, i, 0);
1515 : //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
1516 15 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
1517 15 : }
1518 :
1519 1 : m_pDoc->DeleteTab(0);
1520 1 : }
1521 :
1522 1 : void Test::testFuncINDIRECT()
1523 : {
1524 1 : OUString aTabName("foo");
1525 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1526 1 : m_pDoc->InsertTab (0, aTabName));
1527 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
1528 :
1529 1 : bool bGood = m_pDoc->GetName(0, aTabName);
1530 1 : CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
1531 :
1532 2 : OUString aTest = "Test", aRefErr = "#REF!";
1533 1 : m_pDoc->SetString(0, 10, 0, aTest);
1534 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", m_pDoc->GetString(0,10,0) == aTest);
1535 :
1536 2 : OUString aPrefix = "=INDIRECT(\"";
1537 :
1538 2 : OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
1539 1 : m_pDoc->SetString(0, 0, 0, aFormula);
1540 1 : aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
1541 1 : m_pDoc->SetString(0, 1, 0, aFormula);
1542 1 : aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
1543 1 : m_pDoc->SetString(0, 2, 0, aFormula);
1544 1 : aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
1545 1 : m_pDoc->SetString(0, 3, 0, aFormula);
1546 :
1547 1 : m_pDoc->CalcAll();
1548 : {
1549 : // Default is to use the current formula syntax, which is Calc A1.
1550 : const OUString* aChecks[] = {
1551 : &aTest, &aRefErr, &aRefErr, &aTest
1552 1 : };
1553 :
1554 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1555 : {
1556 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
1557 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
1558 4 : }
1559 : }
1560 :
1561 1 : ScCalcConfig aConfig;
1562 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_OOO;
1563 1 : ScInterpreter::SetGlobalConfig(aConfig);
1564 1 : m_pDoc->CalcAll();
1565 : {
1566 : // Explicit Calc A1 syntax
1567 : const OUString* aChecks[] = {
1568 : &aTest, &aRefErr, &aRefErr, &aTest
1569 1 : };
1570 :
1571 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1572 : {
1573 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
1574 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1575 4 : }
1576 : }
1577 :
1578 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_A1;
1579 1 : ScInterpreter::SetGlobalConfig(aConfig);
1580 1 : m_pDoc->CalcAll();
1581 : {
1582 : // Excel A1 syntax
1583 : const OUString* aChecks[] = {
1584 : &aRefErr, &aTest, &aRefErr, &aTest
1585 1 : };
1586 :
1587 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1588 : {
1589 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
1590 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1591 4 : }
1592 : }
1593 :
1594 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_R1C1;
1595 1 : ScInterpreter::SetGlobalConfig(aConfig);
1596 1 : m_pDoc->CalcAll();
1597 : {
1598 : // Excel R1C1 syntax
1599 : const OUString* aChecks[] = {
1600 : &aRefErr, &aRefErr, &aTest, &aTest
1601 1 : };
1602 :
1603 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1604 : {
1605 4 : OUString aVal = m_pDoc->GetString(0, i, 0);
1606 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1607 4 : }
1608 : }
1609 :
1610 2 : m_pDoc->DeleteTab(0);
1611 1 : }
1612 :
1613 1 : void Test::testFormulaHashAndTag()
1614 : {
1615 1 : m_pDoc->InsertTab(0, "Test");
1616 :
1617 1 : ScAddress aPos1(0,0,0), aPos2(1,0,0);
1618 :
1619 : // Test formula hashing.
1620 :
1621 : struct {
1622 : const char* pFormula1; const char* pFormula2; bool bEqual;
1623 : } aHashTests[] = {
1624 : { "=1", "=2", false }, // different constants
1625 : { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
1626 : { "=C2*3", "=D2*3", true }, // relative references
1627 : { "=C2*3", "=D2*4", false }, // different constants
1628 : { "=C2*4", "=D2*4", true }, // relative references
1629 : { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
1630 : { "=$C3/2", "=$C3/2", true }, // absolute column references
1631 : { "=C$3/2", "=D$3/2", true }, // absolute row references
1632 : { "=$E$30/2", "=$E$30/2", true }, // absolute references
1633 : { "=X20", "=$X$20", false }, // absolute vs relative
1634 : { "=X20", "=X$20", false }, // absolute vs relative
1635 : { "=X20", "=$X20", false }, // absolute vs relative
1636 : { "=X$20", "=$X20", false }, // column absolute vs row absolute
1637 : // similar enough for merging ...
1638 : { "=A1", "=B1", true },
1639 : { "=$A$1", "=$B$1", true },
1640 : { "=A1", "=C2", true },
1641 : { "=SUM(A1)", "=SUM(B1)", true },
1642 : { "=A1+3", "=B1+3", true },
1643 : { "=A1+7", "=B1+42", false },
1644 1 : };
1645 :
1646 20 : for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
1647 : {
1648 19 : m_pDoc->SetString(aPos1, OUString::createFromAscii(aHashTests[i].pFormula1));
1649 19 : m_pDoc->SetString(aPos2, OUString::createFromAscii(aHashTests[i].pFormula2));
1650 19 : size_t nHashVal1 = m_pDoc->GetFormulaHash(aPos1);
1651 19 : size_t nHashVal2 = m_pDoc->GetFormulaHash(aPos2);
1652 :
1653 19 : std::ostringstream os;
1654 19 : os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
1655 19 : if (aHashTests[i].bEqual)
1656 : {
1657 10 : os << " Error: these hashes should be equal." << endl;
1658 10 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 == nHashVal2);
1659 : }
1660 : else
1661 : {
1662 9 : os << " Error: these hashes should differ." << endl;
1663 9 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 != nHashVal2);
1664 : }
1665 :
1666 19 : aPos1.IncRow();
1667 19 : aPos2.IncRow();
1668 19 : }
1669 :
1670 : // Go back to row 1.
1671 1 : aPos1.SetRow(0);
1672 1 : aPos2.SetRow(0);
1673 :
1674 : // Test formula vectorization state.
1675 :
1676 : struct {
1677 : const char* pFormula; ScFormulaVectorState eState;
1678 : } aVectorTests[] = {
1679 : { "=SUM(1;2;3;4;5)", FormulaVectorEnabled },
1680 : { "=NOW()", FormulaVectorDisabled },
1681 : { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference },
1682 : { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference },
1683 : { "=MIN(10;11;22)", FormulaVectorEnabled },
1684 : { "=H4", FormulaVectorCheckReference },
1685 1 : };
1686 :
1687 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
1688 : {
1689 6 : m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
1690 6 : ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
1691 :
1692 6 : if (eState != aVectorTests[i].eState)
1693 : {
1694 0 : std::ostringstream os;
1695 0 : os << "Unexpected vectorization state: expr:" << aVectorTests[i].pFormula;
1696 0 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
1697 : }
1698 6 : aPos1.IncRow();
1699 : }
1700 :
1701 1 : m_pDoc->DeleteTab(0);
1702 1 : }
1703 :
1704 1 : void Test::testCopyToDocument()
1705 : {
1706 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "src"));
1707 :
1708 1 : m_pDoc->SetString(0, 0, 0, "Header");
1709 1 : m_pDoc->SetString(0, 1, 0, "1");
1710 1 : m_pDoc->SetString(0, 2, 0, "2");
1711 1 : m_pDoc->SetString(0, 3, 0, "3");
1712 1 : m_pDoc->SetString(0, 4, 0, "=4/2");
1713 1 : m_pDoc->CalcAll();
1714 :
1715 : // Copy statically to another document.
1716 :
1717 1 : ScDocument aDestDoc(SCDOCMODE_DOCUMENT);
1718 1 : aDestDoc.InsertTab(0, "src");
1719 1 : m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, &aDestDoc); // Copy A2:A4
1720 1 : m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, &aDestDoc); // Copy A1
1721 1 : m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, &aDestDoc); // Copy A5:A8
1722 :
1723 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), aDestDoc.GetString(0,0,0));
1724 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), aDestDoc.GetString(0,1,0));
1725 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), aDestDoc.GetString(0,2,0));
1726 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), aDestDoc.GetString(0,3,0));
1727 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), aDestDoc.GetString(0,4,0));
1728 :
1729 1 : m_pDoc->DeleteTab(0);
1730 1 : }
1731 :
1732 1 : void Test::testSheetsFunc()
1733 : {
1734 1 : OUString aTabName1("test1");
1735 2 : OUString aTabName2("test2");
1736 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1737 1 : m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
1738 :
1739 1 : m_pDoc->SetString(0, 0, 0, OUString("=SHEETS()"));
1740 1 : m_pDoc->CalcFormulaTree(false, false);
1741 : double original;
1742 1 : m_pDoc->GetValue(0, 0, 0, original);
1743 :
1744 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
1745 1 : static_cast<SCTAB>(original) == m_pDoc->GetTableCount());
1746 :
1747 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1748 1 : m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
1749 :
1750 : double modified;
1751 1 : m_pDoc->GetValue(0, 0, 0, modified);
1752 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
1753 1 : modified - original == 1.0);
1754 :
1755 1 : SCTAB nTabCount = m_pDoc->GetTableCount();
1756 1 : m_pDoc->DeleteTab(--nTabCount);
1757 :
1758 1 : m_pDoc->GetValue(0, 0, 0, modified);
1759 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
1760 1 : modified - original == 0.0);
1761 :
1762 2 : m_pDoc->DeleteTab(--nTabCount);
1763 1 : }
1764 :
1765 1 : void Test::testVolatileFunc()
1766 : {
1767 1 : OUString aTabName("foo");
1768 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1769 1 : m_pDoc->InsertTab (0, aTabName));
1770 :
1771 1 : double val = 1;
1772 1 : m_pDoc->SetValue(0, 0, 0, val);
1773 1 : m_pDoc->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
1774 : double now1;
1775 1 : m_pDoc->GetValue(0, 1, 0, now1);
1776 1 : CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
1777 :
1778 1 : val = 0;
1779 1 : m_pDoc->SetValue(0, 0, 0, val);
1780 1 : m_pDoc->CalcFormulaTree(false, false);
1781 : double zero;
1782 1 : m_pDoc->GetValue(0, 1, 0, zero);
1783 1 : CPPUNIT_ASSERT_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", zero == 0.0);
1784 :
1785 1 : val = 1;
1786 1 : m_pDoc->SetValue(0, 0, 0, val);
1787 1 : m_pDoc->CalcFormulaTree(false, false);
1788 : double now2;
1789 1 : m_pDoc->GetValue(0, 1, 0, now2);
1790 1 : CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
1791 :
1792 1 : m_pDoc->DeleteTab(0);
1793 1 : }
1794 :
1795 : namespace {
1796 :
1797 : struct HoriIterCheck
1798 : {
1799 : SCCOL nCol;
1800 : SCROW nRow;
1801 : const char* pVal;
1802 : };
1803 :
1804 : template<size_t _Size>
1805 2 : bool checkHorizontalIterator(ScDocument* pDoc, const char* pData[][_Size], size_t nDataCount, const HoriIterCheck* pChecks, size_t nCheckCount)
1806 : {
1807 2 : ScAddress aPos(0,0,0);
1808 2 : insertRangeData(pDoc, aPos, pData, nDataCount);
1809 2 : ScHorizontalCellIterator aIter(pDoc, 0, 0, 0, 1, nDataCount-1);
1810 :
1811 : SCCOL nCol;
1812 : SCROW nRow;
1813 2 : size_t i = 0;
1814 15 : for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
1815 : {
1816 13 : if (i >= nCheckCount)
1817 0 : CPPUNIT_FAIL("Iterator claims there is more data than there should be.");
1818 :
1819 13 : if (pChecks[i].nCol != nCol)
1820 0 : return false;
1821 :
1822 13 : if (pChecks[i].nRow != nRow)
1823 0 : return false;
1824 :
1825 13 : if (OUString::createFromAscii(pChecks[i].pVal) != pCell->getString())
1826 0 : return false;
1827 : }
1828 :
1829 2 : return true;
1830 : }
1831 :
1832 : }
1833 :
1834 1 : void Test::testHorizontalIterator()
1835 : {
1836 1 : m_pDoc->InsertTab(0, "test");
1837 :
1838 : {
1839 : // Raw data
1840 : const char* aData[][2] = {
1841 : { "A", "B" },
1842 : { "C", "1" },
1843 : { "D", "2" },
1844 : { "E", "3" }
1845 1 : };
1846 :
1847 : HoriIterCheck aChecks[] = {
1848 : { 0, 0, "A" },
1849 : { 1, 0, "B" },
1850 : { 0, 1, "C" },
1851 : { 1, 1, "1" },
1852 : { 0, 2, "D" },
1853 : { 1, 2, "2" },
1854 : { 0, 3, "E" },
1855 : { 1, 3, "3" },
1856 1 : };
1857 :
1858 : bool bRes = checkHorizontalIterator(
1859 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1860 :
1861 1 : if (!bRes)
1862 0 : CPPUNIT_FAIL("Failed on test 1.");
1863 : }
1864 :
1865 : {
1866 : // Raw data
1867 : const char* aData[][2] = {
1868 : { "A", "B" },
1869 : { "C", 0 },
1870 : { "D", "E" },
1871 1 : };
1872 :
1873 : HoriIterCheck aChecks[] = {
1874 : { 0, 0, "A" },
1875 : { 1, 0, "B" },
1876 : { 0, 1, "C" },
1877 : { 0, 2, "D" },
1878 : { 1, 2, "E" },
1879 1 : };
1880 :
1881 : bool bRes = checkHorizontalIterator(
1882 1 : m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
1883 :
1884 1 : if (!bRes)
1885 0 : CPPUNIT_FAIL("Failed on test 2.");
1886 : }
1887 :
1888 1 : m_pDoc->DeleteTab(0);
1889 1 : }
1890 :
1891 1 : void Test::testFormulaDepTracking()
1892 : {
1893 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1894 :
1895 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
1896 :
1897 : // B2 listens on D2.
1898 1 : m_pDoc->SetString(1, 1, 0, "=D2");
1899 1 : double val = -999.0; // dummy initial value
1900 1 : m_pDoc->GetValue(1, 1, 0, val);
1901 1 : CPPUNIT_ASSERT_MESSAGE("Referencing an empty cell should yield zero.", val == 0.0);
1902 :
1903 : // Changing the value of D2 should trigger recalculation of B2.
1904 1 : m_pDoc->SetValue(3, 1, 0, 1.1);
1905 1 : m_pDoc->GetValue(1, 1, 0, val);
1906 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 1.1);
1907 :
1908 : // And again.
1909 1 : m_pDoc->SetValue(3, 1, 0, 2.2);
1910 1 : m_pDoc->GetValue(1, 1, 0, val);
1911 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 2.2);
1912 :
1913 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1914 :
1915 : // Now, let's test the range dependency tracking.
1916 :
1917 : // B2 listens on D2:E6.
1918 1 : m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
1919 1 : m_pDoc->GetValue(1, 1, 0, val);
1920 1 : CPPUNIT_ASSERT_MESSAGE("Summing an empty range should yield zero.", val == 0.0);
1921 :
1922 : // Set value to E3. This should trigger recalc on B2.
1923 1 : m_pDoc->SetValue(4, 2, 0, 2.4);
1924 1 : m_pDoc->GetValue(1, 1, 0, val);
1925 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", val == 2.4);
1926 :
1927 : // Set value to D5 to trigger recalc again. Note that this causes an
1928 : // addition of 1.2 + 2.4 which is subject to binary floating point
1929 : // rounding error. We need to use approxEqual to assess its value.
1930 :
1931 1 : m_pDoc->SetValue(3, 4, 0, 1.2);
1932 1 : m_pDoc->GetValue(1, 1, 0, val);
1933 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
1934 :
1935 : // Change the value of D2 (boundary case).
1936 1 : m_pDoc->SetValue(3, 1, 0, 1.0);
1937 1 : m_pDoc->GetValue(1, 1, 0, val);
1938 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
1939 :
1940 : // Change the value of E6 (another boundary case).
1941 1 : m_pDoc->SetValue(4, 5, 0, 2.0);
1942 1 : m_pDoc->GetValue(1, 1, 0, val);
1943 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
1944 :
1945 : // Change the value of D6 (another boundary case).
1946 1 : m_pDoc->SetValue(3, 5, 0, 3.0);
1947 1 : m_pDoc->GetValue(1, 1, 0, val);
1948 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
1949 :
1950 : // Change the value of E2 (another boundary case).
1951 1 : m_pDoc->SetValue(4, 1, 0, 0.4);
1952 1 : m_pDoc->GetValue(1, 1, 0, val);
1953 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
1954 :
1955 : // Change the existing non-empty value cell (E2).
1956 1 : m_pDoc->SetValue(4, 1, 0, 2.4);
1957 1 : m_pDoc->GetValue(1, 1, 0, val);
1958 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
1959 :
1960 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1961 :
1962 : // Now, column-based dependency tracking. We now switch to the R1C1
1963 : // syntax which is easier to use for repeated relative references.
1964 :
1965 2 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1966 :
1967 1 : val = 0.0;
1968 10 : for (SCROW nRow = 1; nRow <= 9; ++nRow)
1969 : {
1970 : // Static value in column 1.
1971 9 : m_pDoc->SetValue(0, nRow, 0, ++val);
1972 :
1973 : // Formula in column 2 that references cell to the left.
1974 9 : m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
1975 :
1976 : // Formula in column 3 that references cell to the left.
1977 9 : m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
1978 : }
1979 :
1980 : // Check formula values.
1981 1 : val = 0.0;
1982 10 : for (SCROW nRow = 1; nRow <= 9; ++nRow)
1983 : {
1984 9 : ++val;
1985 9 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, nRow, 0) == val);
1986 9 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, nRow, 0) == val*2.0);
1987 : }
1988 :
1989 : // Intentionally insert a formula in column 1. This will break column 1's
1990 : // uniformity of consisting only of static value cells.
1991 1 : m_pDoc->SetString(0, 4, 0, "=R2C3");
1992 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(0, 4, 0) == 2.0);
1993 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, 4, 0) == 2.0);
1994 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, 4, 0) == 4.0);
1995 :
1996 2 : m_pDoc->DeleteTab(0);
1997 1 : }
1998 :
1999 1 : void Test::testFormulaDepTracking2()
2000 : {
2001 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
2002 :
2003 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
2004 :
2005 1 : double val = 2.0;
2006 1 : m_pDoc->SetValue(0, 0, 0, val);
2007 1 : val = 4.0;
2008 1 : m_pDoc->SetValue(1, 0, 0, val);
2009 1 : val = 5.0;
2010 1 : m_pDoc->SetValue(0, 1, 0, val);
2011 1 : m_pDoc->SetString(2, 0, 0, "=A1/B1");
2012 1 : m_pDoc->SetString(1, 1, 0, "=B1*C1");
2013 :
2014 1 : CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
2015 :
2016 1 : clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
2017 :
2018 1 : CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
2019 :
2020 1 : m_pDoc->DeleteTab(0);
2021 1 : }
2022 :
2023 : namespace {
2024 :
2025 7 : bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
2026 : {
2027 7 : const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
2028 7 : if (pBC)
2029 : {
2030 0 : cerr << "Broadcaster shouldn't be here." << endl;
2031 0 : return false;
2032 : }
2033 :
2034 7 : pBC = rDoc.GetBroadcaster(rTo);
2035 7 : if (!pBC)
2036 : {
2037 0 : cerr << "Broadcaster should be here." << endl;
2038 0 : return false;
2039 : }
2040 7 : return true;
2041 : }
2042 :
2043 11 : ScToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
2044 : {
2045 11 : ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
2046 11 : if (!pFC)
2047 : {
2048 0 : cerr << "Formula cell expected, but not found." << endl;
2049 0 : return NULL;
2050 : }
2051 :
2052 11 : ScTokenArray* pTokens = pFC->GetCode();
2053 11 : if (!pTokens)
2054 : {
2055 0 : cerr << "Token array is not present." << endl;
2056 0 : return NULL;
2057 : }
2058 :
2059 11 : ScToken* pToken = static_cast<ScToken*>(pTokens->First());
2060 11 : if (!pToken || pToken->GetType() != formula::svSingleRef)
2061 : {
2062 0 : cerr << "Not a single reference token." << endl;
2063 0 : return NULL;
2064 : }
2065 :
2066 11 : return pToken;
2067 : }
2068 :
2069 9 : bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCsCOL nRelCol, SCsROW nRelRow)
2070 : {
2071 9 : ScToken* pToken = getSingleRefToken(rDoc, rPos);
2072 9 : if (!pToken)
2073 0 : return false;
2074 :
2075 9 : ScSingleRefData& rRef = pToken->GetSingleRef();
2076 9 : if (!rRef.IsColRel() || rRef.nRelCol != nRelCol)
2077 : {
2078 0 : cerr << "Unexpected relative column address." << endl;
2079 0 : return false;
2080 : }
2081 :
2082 9 : if (!rRef.IsRowRel() || rRef.nRelRow != nRelRow)
2083 : {
2084 0 : cerr << "Unexpected relative row address." << endl;
2085 0 : return false;
2086 : }
2087 :
2088 9 : return true;
2089 : }
2090 :
2091 2 : bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
2092 : {
2093 2 : ScToken* pToken = getSingleRefToken(rDoc, rPos);
2094 2 : if (!pToken)
2095 0 : return false;
2096 :
2097 2 : ScSingleRefData& rRef = pToken->GetSingleRef();
2098 2 : if (!rRef.IsDeleted())
2099 : {
2100 0 : cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
2101 0 : return false;
2102 : }
2103 :
2104 2 : return true;
2105 : }
2106 :
2107 : }
2108 :
2109 1 : void Test::testCellBroadcaster()
2110 : {
2111 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
2112 :
2113 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
2114 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
2115 1 : double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
2116 1 : CPPUNIT_ASSERT_EQUAL(0.0, val);
2117 :
2118 1 : const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
2119 1 : CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
2120 :
2121 : // Change the value of A1 and make sure that B1 follows.
2122 1 : m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
2123 1 : val = m_pDoc->GetValue(ScAddress(1,0,0));
2124 1 : CPPUNIT_ASSERT_EQUAL(1.23, val);
2125 :
2126 : // Move column A down 5 cells. Make sure B1 now references A6, not A1.
2127 1 : m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
2128 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
2129 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
2130 :
2131 : // Make sure the broadcaster has also moved.
2132 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2133 1 : broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
2134 :
2135 : // Set new value to A6 and make sure B1 gets updated.
2136 1 : m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
2137 1 : val = m_pDoc->GetValue(ScAddress(1,0,0));
2138 1 : CPPUNIT_ASSERT_EQUAL(45.6, val);
2139 :
2140 : // Move column A up 3 cells, and make sure B1 now references A3, not A6.
2141 1 : m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
2142 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
2143 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
2144 :
2145 : // The broadcaster should also have been relocated from A6 to A3.
2146 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2147 1 : broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
2148 :
2149 : // Insert cells over A1:A10 and shift cells to right.
2150 1 : m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
2151 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
2152 1 : checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
2153 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2154 1 : broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
2155 :
2156 : // Delete formula in C2, which should remove the broadcaster in B3.
2157 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
2158 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
2159 1 : clearRange(m_pDoc, ScAddress(2,0,0));
2160 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
2161 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
2162 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
2163 :
2164 : // Clear everything and start over.
2165 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2166 :
2167 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
2168 1 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
2169 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
2170 :
2171 : // While column A is still empty, move column A down 2 cells. This should
2172 : // move the broadcaster from A1 to A3.
2173 1 : m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
2174 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2175 1 : broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
2176 :
2177 : // Move it back while column A is still empty.
2178 1 : m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
2179 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2180 1 : broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
2181 :
2182 : // Clear everything again
2183 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2184 :
2185 : // B1:B3 depends on A1:A3
2186 1 : m_pDoc->SetString(ScAddress(1,0,0), "=A1");
2187 1 : m_pDoc->SetString(ScAddress(1,1,0), "=A2");
2188 1 : m_pDoc->SetString(ScAddress(1,2,0), "=A3");
2189 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
2190 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
2191 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
2192 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
2193 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
2194 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
2195 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
2196 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
2197 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
2198 :
2199 : // Insert Rows at row 2, down 5 rows.
2200 1 : m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
2201 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
2202 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
2203 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
2204 :
2205 : // Broadcasters in A2 and A3 should shift down by 5 rows.
2206 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2207 1 : broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
2208 2 : CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
2209 1 : broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
2210 :
2211 : // B2 and B3 should reference shifted cells.
2212 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
2213 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
2214 2 : CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
2215 1 : checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
2216 :
2217 : // Delete cells with broadcasters.
2218 1 : m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
2219 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
2220 1 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
2221 :
2222 : // References in B2 and B3 should be invalid.
2223 2 : CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
2224 1 : checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
2225 2 : CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
2226 1 : checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
2227 :
2228 : // Clear everything again
2229 1 : clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
2230 :
2231 : // Switch to R1C1 to make it easier to input relative references in multiple cells.
2232 2 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
2233 :
2234 : // Have B1:B20 reference A1:A20.
2235 1 : val = 0.0;
2236 21 : for (SCROW i = 0; i < 20; ++i)
2237 : {
2238 20 : m_pDoc->SetValue(ScAddress(0,i,0), val++);
2239 20 : m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
2240 : }
2241 :
2242 : // Ensure that the formula cells show correct values, and the referenced
2243 : // cells have broadcasters.
2244 1 : val = 0.0;
2245 21 : for (SCROW i = 0; i < 20; ++i)
2246 : {
2247 20 : CPPUNIT_ASSERT_EQUAL(val++, m_pDoc->GetValue(ScAddress(1,i,0)));
2248 20 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
2249 20 : CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
2250 : }
2251 :
2252 : // Delete formula cells in B2:B19.
2253 1 : clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
2254 : // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
2255 1 : CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
2256 1 : CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
2257 19 : for (SCROW i = 1; i <= 18; ++i)
2258 : {
2259 18 : pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
2260 18 : CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
2261 : }
2262 :
2263 2 : m_pDoc->DeleteTab(0);
2264 1 : }
2265 :
2266 1 : void Test::testFuncParam()
2267 : {
2268 1 : OUString aTabName("foo");
2269 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
2270 1 : m_pDoc->InsertTab (0, aTabName));
2271 :
2272 : // First, the normal case, with no missing parameters.
2273 1 : m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;2;3)"));
2274 1 : m_pDoc->CalcFormulaTree(false, false);
2275 : double val;
2276 1 : m_pDoc->GetValue(0, 0, 0, val);
2277 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 2);
2278 :
2279 : // Now function with missing parameters. Missing values should be treated
2280 : // as zeros.
2281 1 : m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;;;)"));
2282 1 : m_pDoc->CalcFormulaTree(false, false);
2283 1 : m_pDoc->GetValue(0, 0, 0, val);
2284 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 0.25);
2285 :
2286 : // Conversion of string to numeric argument.
2287 1 : m_pDoc->SetString(0, 0, 0, OUString("=\"\"+3")); // empty string
2288 1 : m_pDoc->SetString(0, 1, 0, OUString("=\" \"+3")); // only blank
2289 1 : m_pDoc->SetString(0, 2, 0, OUString("=\" 4 \"+3")); // number in blanks
2290 1 : m_pDoc->SetString(0, 3, 0, OUString("=\" x \"+3")); // non-numeric => #VALUE! error
2291 :
2292 2 : OUString aVal;
2293 1 : ScCalcConfig aConfig;
2294 :
2295 : // With "Empty string as zero" option.
2296 1 : aConfig.mbEmptyStringAsZero = true;
2297 1 : ScInterpreter::SetGlobalConfig(aConfig);
2298 1 : m_pDoc->CalcAll();
2299 1 : m_pDoc->GetValue(0, 0, 0, val);
2300 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
2301 1 : m_pDoc->GetValue(0, 1, 0, val);
2302 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
2303 1 : m_pDoc->GetValue(0, 2, 0, val);
2304 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
2305 1 : aVal = m_pDoc->GetString( 0, 3, 0);
2306 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2307 :
2308 : // Without "Empty string as zero" option.
2309 1 : aConfig.mbEmptyStringAsZero = false;
2310 1 : ScInterpreter::SetGlobalConfig(aConfig);
2311 1 : m_pDoc->CalcAll();
2312 1 : aVal = m_pDoc->GetString( 0, 0, 0);
2313 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2314 1 : aVal = m_pDoc->GetString( 0, 1, 0);
2315 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2316 1 : m_pDoc->GetValue(0, 2, 0, val);
2317 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
2318 1 : aVal = m_pDoc->GetString( 0, 3, 0);
2319 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
2320 :
2321 2 : m_pDoc->DeleteTab(0);
2322 1 : }
2323 :
2324 1 : void Test::testNamedRange()
2325 : {
2326 : struct {
2327 : const char* pName; const char* pExpr; sal_uInt16 nIndex;
2328 : } aNames[] = {
2329 : { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
2330 : { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
2331 : { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
2332 : { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
2333 1 : };
2334 :
2335 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "Sheet1"));
2336 :
2337 1 : m_pDoc->SetValue (0, 0, 0, 101);
2338 :
2339 1 : ScAddress aA1(0, 0, 0);
2340 1 : ScRangeName* pNewRanges = new ScRangeName();
2341 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
2342 : {
2343 : ScRangeData* pNew = new ScRangeData(
2344 : m_pDoc,
2345 : OUString::createFromAscii(aNames[i].pName),
2346 : OUString::createFromAscii(aNames[i].pExpr),
2347 4 : aA1, 0, formula::FormulaGrammar::GRAM_ENGLISH);
2348 4 : pNew->SetIndex(aNames[i].nIndex);
2349 4 : bool bSuccess = pNewRanges->insert(pNew);
2350 4 : CPPUNIT_ASSERT_MESSAGE ("insertion failed", bSuccess);
2351 : }
2352 :
2353 : // Make sure the index lookup does the right thing.
2354 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
2355 : {
2356 4 : const ScRangeData* p = pNewRanges->findByIndex(aNames[i].nIndex);
2357 4 : CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
2358 4 : OUString aName = p->GetName();
2359 4 : CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].pName));
2360 4 : }
2361 :
2362 : // Test usage in formula expression.
2363 1 : m_pDoc->SetRangeName(pNewRanges);
2364 1 : m_pDoc->SetString (1, 0, 0, OUString("=A1/Divisor"));
2365 1 : m_pDoc->CalcAll();
2366 :
2367 : double result;
2368 1 : m_pDoc->GetValue (1, 0, 0, result);
2369 1 : CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 1.0);
2370 :
2371 : // Test copy-ability of range names.
2372 1 : ScRangeName* pCopiedRanges = new ScRangeName(*pNewRanges);
2373 1 : m_pDoc->SetRangeName(pCopiedRanges);
2374 : // Make sure the index lookup still works.
2375 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
2376 : {
2377 4 : const ScRangeData* p = pCopiedRanges->findByIndex(aNames[i].nIndex);
2378 4 : CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
2379 4 : OUString aName = p->GetName();
2380 4 : CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].pName));
2381 4 : }
2382 :
2383 1 : m_pDoc->SetRangeName(NULL); // Delete the names.
2384 1 : m_pDoc->DeleteTab(0);
2385 1 : }
2386 :
2387 1 : void Test::testCSV()
2388 : {
2389 1 : const int English = 0, European = 1;
2390 : struct {
2391 : const char *pStr; int eSep; bool bResult; double nValue;
2392 : } aTests[] = {
2393 : { "foo", English, false, 0.0 },
2394 : { "1.0", English, true, 1.0 },
2395 : { "1,0", English, false, 0.0 },
2396 : { "1.0", European, false, 0.0 },
2397 : { "1.000", European, true, 1000.0 },
2398 : { "1,000", European, true, 1.0 },
2399 : { "1.000", English, true, 1.0 },
2400 : { "1,000", English, true, 1000.0 },
2401 : { " 1.0", English, true, 1.0 },
2402 : { " 1.0 ", English, true, 1.0 },
2403 : { "1.0 ", European, false, 0.0 },
2404 : { "1.000", European, true, 1000.0 },
2405 : { "1137.999", English, true, 1137.999 },
2406 : { "1.000.00", European, false, 0.0 }
2407 1 : };
2408 15 : for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
2409 14 : OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
2410 14 : double nValue = 0.0;
2411 : bool bResult = ScStringUtil::parseSimpleNumber
2412 14 : (aStr, aTests[i].eSep == English ? '.' : ',',
2413 14 : aTests[i].eSep == English ? ',' : '.',
2414 28 : nValue);
2415 14 : CPPUNIT_ASSERT_MESSAGE ("CSV numeric detection failure", bResult == aTests[i].bResult);
2416 14 : CPPUNIT_ASSERT_MESSAGE ("CSV numeric value failure", nValue == aTests[i].nValue);
2417 14 : }
2418 1 : }
2419 :
2420 : template<typename Evaluator>
2421 4 : void checkMatrixElements(const ScMatrix& rMat)
2422 : {
2423 : SCSIZE nC, nR;
2424 4 : rMat.GetDimensions(nC, nR);
2425 : Evaluator aEval;
2426 32 : for (SCSIZE i = 0; i < nC; ++i)
2427 : {
2428 508 : for (SCSIZE j = 0; j < nR; ++j)
2429 : {
2430 480 : aEval(i, j, rMat.Get(i, j));
2431 : }
2432 : }
2433 4 : }
2434 :
2435 : struct AllZeroMatrix
2436 : {
2437 40 : void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2438 : {
2439 40 : CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
2440 40 : CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
2441 40 : }
2442 : };
2443 :
2444 : struct PartiallyFilledZeroMatrix
2445 : {
2446 40 : void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2447 : {
2448 40 : CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
2449 40 : if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
2450 : {
2451 14 : CPPUNIT_ASSERT_MESSAGE("element value must be 3.0", rVal.fVal == 3.0);
2452 : }
2453 : else
2454 : {
2455 26 : CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
2456 : }
2457 40 : }
2458 : };
2459 :
2460 : struct AllEmptyMatrix
2461 : {
2462 200 : void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
2463 : {
2464 200 : CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
2465 200 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
2466 200 : }
2467 : };
2468 :
2469 : struct PartiallyFilledEmptyMatrix
2470 : {
2471 200 : void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
2472 : {
2473 200 : if (nCol == 1 && nRow == 1)
2474 : {
2475 1 : CPPUNIT_ASSERT_MESSAGE("element is not of boolean type", rVal.nType == SC_MATVAL_BOOLEAN);
2476 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == 1.0);
2477 : }
2478 199 : else if (nCol == 4 && nRow == 5)
2479 : {
2480 1 : CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_VALUE);
2481 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == -12.5);
2482 : }
2483 198 : else if (nCol == 8 && nRow == 2)
2484 : {
2485 1 : CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING);
2486 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr == "Test");
2487 : }
2488 197 : else if (nCol == 8 && nRow == 11)
2489 : {
2490 1 : CPPUNIT_ASSERT_MESSAGE("element is not of empty path type", rVal.nType == SC_MATVAL_EMPTYPATH);
2491 1 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
2492 : }
2493 : else
2494 : {
2495 196 : CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
2496 196 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
2497 : }
2498 200 : }
2499 : };
2500 :
2501 1 : void Test::testMatrix()
2502 : {
2503 1 : ScMatrixRef pMat;
2504 :
2505 : // First, test the zero matrix type.
2506 1 : pMat = new ScMatrix(0, 0, 0.0);
2507 : SCSIZE nC, nR;
2508 1 : pMat->GetDimensions(nC, nR);
2509 1 : CPPUNIT_ASSERT_MESSAGE("matrix is not empty", nC == 0 && nR == 0);
2510 1 : pMat->Resize(4, 10, 0.0);
2511 1 : pMat->GetDimensions(nC, nR);
2512 1 : CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 4 && nR == 10);
2513 2 : CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
2514 1 : !pMat->And() && !pMat->Or());
2515 :
2516 : // Resizing into a larger matrix should fill the void space with zeros.
2517 1 : checkMatrixElements<AllZeroMatrix>(*pMat);
2518 :
2519 1 : pMat->FillDouble(3.0, 1, 2, 2, 8);
2520 1 : checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
2521 1 : CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
2522 2 : CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
2523 1 : !pMat->And() && pMat->Or());
2524 1 : pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
2525 2 : CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
2526 1 : pMat->And() && pMat->Or());
2527 :
2528 : // Test the AND and OR evaluations.
2529 1 : pMat = new ScMatrix(2, 2, 0.0);
2530 :
2531 : // Only some of the elements are non-zero.
2532 1 : pMat->PutBoolean(true, 0, 0);
2533 1 : pMat->PutDouble(1.0, 1, 1);
2534 1 : CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2535 1 : CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
2536 :
2537 : // All of the elements are non-zero.
2538 1 : pMat->PutBoolean(true, 0, 1);
2539 1 : pMat->PutDouble(2.3, 1, 0);
2540 1 : CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
2541 1 : CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
2542 :
2543 : // Now test the emtpy matrix type.
2544 1 : pMat = new ScMatrix(10, 20);
2545 1 : pMat->GetDimensions(nC, nR);
2546 1 : CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 10 && nR == 20);
2547 1 : checkMatrixElements<AllEmptyMatrix>(*pMat);
2548 :
2549 1 : pMat->PutBoolean(true, 1, 1);
2550 1 : pMat->PutDouble(-12.5, 4, 5);
2551 2 : OUString aStr("Test");
2552 1 : pMat->PutString(aStr, 8, 2);
2553 1 : pMat->PutEmptyPath(8, 11);
2554 1 : checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
2555 :
2556 : // Test resizing.
2557 1 : pMat = new ScMatrix(0, 0);
2558 1 : pMat->Resize(2, 2, 1.5);
2559 1 : pMat->PutEmpty(1, 1);
2560 :
2561 1 : CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
2562 1 : CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
2563 1 : CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
2564 1 : CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
2565 :
2566 : // Max and min values.
2567 1 : pMat = new ScMatrix(2, 2, 0.0);
2568 1 : pMat->PutDouble(-10, 0, 0);
2569 1 : pMat->PutDouble(-12, 0, 1);
2570 1 : pMat->PutDouble(-8, 1, 0);
2571 1 : pMat->PutDouble(-25, 1, 1);
2572 1 : CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
2573 1 : CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
2574 1 : pMat->PutString("Test", 0, 0);
2575 1 : CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
2576 1 : CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
2577 1 : pMat->PutBoolean(true, 0, 0);
2578 1 : CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
2579 1 : pMat = new ScMatrix(2, 2, 10.0);
2580 1 : pMat->PutBoolean(false, 0, 0);
2581 1 : pMat->PutDouble(12.5, 1, 1);
2582 1 : CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
2583 2 : CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
2584 1 : }
2585 :
2586 1 : void Test::testEnterMixedMatrix()
2587 : {
2588 1 : m_pDoc->InsertTab(0, "foo");
2589 :
2590 : // Insert the source values in A1:B2.
2591 1 : m_pDoc->SetString(0, 0, 0, "A");
2592 1 : m_pDoc->SetString(1, 0, 0, "B");
2593 1 : double val = 1.0;
2594 1 : m_pDoc->SetValue(0, 1, 0, val);
2595 1 : val = 2.0;
2596 1 : m_pDoc->SetValue(1, 1, 0, val);
2597 :
2598 : // Create a matrix range in A4:B5 referencing A1:B2.
2599 1 : ScMarkData aMark;
2600 1 : aMark.SelectOneTable(0);
2601 1 : m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, "=A1:B2", NULL);
2602 :
2603 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
2604 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
2605 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
2606 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
2607 :
2608 1 : m_pDoc->DeleteTab(0);
2609 1 : }
2610 :
2611 : namespace {
2612 :
2613 : struct DPFieldDef
2614 : {
2615 : const char* pName;
2616 : sheet::DataPilotFieldOrientation eOrient;
2617 :
2618 : /**
2619 : * Function for data field. It's used only for data field. When 0, the
2620 : * default function (SUM) is used.
2621 : */
2622 : int eFunc;
2623 : };
2624 :
2625 : template<size_t _Size>
2626 5 : ScRange insertDPSourceData(ScDocument* pDoc, DPFieldDef aFields[], size_t nFieldCount, const char* aData[][_Size], size_t nDataCount)
2627 : {
2628 : // Insert field names in row 0.
2629 22 : for (size_t i = 0; i < nFieldCount; ++i)
2630 17 : pDoc->SetString(static_cast<SCCOL>(i), 0, 0, OUString(aFields[i].pName, strlen(aFields[i].pName), RTL_TEXTENCODING_UTF8));
2631 :
2632 : // Insert data into row 1 and downward.
2633 31 : for (size_t i = 0; i < nDataCount; ++i)
2634 : {
2635 26 : SCROW nRow = static_cast<SCROW>(i) + 1;
2636 120 : for (size_t j = 0; j < nFieldCount; ++j)
2637 : {
2638 94 : SCCOL nCol = static_cast<SCCOL>(j);
2639 94 : pDoc->SetString(
2640 188 : nCol, nRow, 0, OUString(aData[i][j], strlen(aData[i][j]), RTL_TEXTENCODING_UTF8));
2641 : }
2642 : }
2643 :
2644 5 : SCROW nRow1 = 0, nRow2 = 0;
2645 5 : SCCOL nCol1 = 0, nCol2 = 0;
2646 5 : pDoc->GetDataArea(0, nCol1, nRow1, nCol2, nRow2, true, false);
2647 5 : CPPUNIT_ASSERT_MESSAGE("Data is expected to start from (col=0,row=0).", nCol1 == 0 && nRow1 == 0);
2648 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected data range.",
2649 : nCol2 == static_cast<SCCOL>(nFieldCount - 1) && nRow2 == static_cast<SCROW>(nDataCount));
2650 :
2651 5 : ScRange aSrcRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
2652 5 : printRange(pDoc, aSrcRange, "Data sheet content");
2653 5 : return aSrcRange;
2654 : }
2655 :
2656 : template<size_t _Size>
2657 31 : bool checkDPTableOutput(ScDocument* pDoc, const ScRange& aOutRange, const char* aOutputCheck[][_Size], const char* pCaption)
2658 : {
2659 31 : bool bResult = true;
2660 31 : const ScAddress& s = aOutRange.aStart;
2661 31 : const ScAddress& e = aOutRange.aEnd;
2662 31 : SheetPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1);
2663 31 : SCROW nOutRowSize = e.Row() - s.Row() + 1;
2664 31 : SCCOL nOutColSize = e.Col() - s.Col() + 1;
2665 242 : for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
2666 : {
2667 829 : for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
2668 : {
2669 618 : OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
2670 618 : printer.set(nRow, nCol, aVal);
2671 618 : const char* p = aOutputCheck[nRow][nCol];
2672 618 : if (p)
2673 : {
2674 474 : OUString aCheckVal = OUString::createFromAscii(p);
2675 474 : bool bEqual = aCheckVal.equals(aVal);
2676 474 : if (!bEqual)
2677 : {
2678 0 : cout << "Expected: " << aCheckVal << " Actual: " << aVal << endl;
2679 0 : bResult = false;
2680 474 : }
2681 : }
2682 144 : else if (!aVal.isEmpty())
2683 : {
2684 0 : cout << "Empty cell expected" << endl;
2685 0 : bResult = false;
2686 : }
2687 : }
2688 : }
2689 31 : printer.print(pCaption);
2690 31 : return bResult;
2691 : }
2692 :
2693 16 : ScDPObject* createDPFromSourceDesc(
2694 : ScDocument* pDoc, const ScSheetSourceDesc& rDesc, DPFieldDef aFields[], size_t nFieldCount,
2695 : bool bFilterButton)
2696 : {
2697 16 : ScDPObject* pDPObj = new ScDPObject(pDoc);
2698 16 : pDPObj->SetSheetDesc(rDesc);
2699 16 : pDPObj->SetOutRange(ScAddress(0, 0, 1));
2700 :
2701 16 : ScDPSaveData aSaveData;
2702 : // Set data pilot table output options.
2703 16 : aSaveData.SetIgnoreEmptyRows(false);
2704 16 : aSaveData.SetRepeatIfEmpty(false);
2705 16 : aSaveData.SetColumnGrand(true);
2706 16 : aSaveData.SetRowGrand(true);
2707 16 : aSaveData.SetFilterButton(bFilterButton);
2708 16 : aSaveData.SetDrillDown(true);
2709 :
2710 : // Check the sanity of the source range.
2711 16 : const ScRange& rSrcRange = rDesc.GetSourceRange();
2712 16 : SCROW nRow1 = rSrcRange.aStart.Row();
2713 16 : SCROW nRow2 = rSrcRange.aEnd.Row();
2714 16 : CPPUNIT_ASSERT_MESSAGE("source range contains no data!", nRow2 - nRow1 > 1);
2715 :
2716 : // Set the dimension information.
2717 57 : for (size_t i = 0; i < nFieldCount; ++i)
2718 : {
2719 41 : OUString aDimName = OUString::createFromAscii(aFields[i].pName);
2720 41 : ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(aDimName);
2721 41 : pDim->SetOrientation(static_cast<sal_uInt16>(aFields[i].eOrient));
2722 41 : pDim->SetUsedHierarchy(0);
2723 :
2724 41 : if (aFields[i].eOrient == sheet::DataPilotFieldOrientation_DATA)
2725 : {
2726 18 : sheet::GeneralFunction eFunc = sheet::GeneralFunction_SUM;
2727 18 : if (aFields[i].eFunc)
2728 11 : eFunc = static_cast<sheet::GeneralFunction>(aFields[i].eFunc);
2729 :
2730 18 : pDim->SetFunction(eFunc);
2731 18 : pDim->SetReferenceValue(NULL);
2732 : }
2733 : else
2734 : {
2735 23 : sheet::DataPilotFieldSortInfo aSortInfo;
2736 23 : aSortInfo.IsAscending = true;
2737 23 : aSortInfo.Mode = 2;
2738 23 : pDim->SetSortInfo(&aSortInfo);
2739 :
2740 23 : sheet::DataPilotFieldLayoutInfo aLayInfo;
2741 23 : aLayInfo.LayoutMode = 0;
2742 23 : aLayInfo.AddEmptyLines = false;
2743 23 : pDim->SetLayoutInfo(&aLayInfo);
2744 46 : sheet::DataPilotFieldAutoShowInfo aShowInfo;
2745 23 : aShowInfo.IsEnabled = false;
2746 23 : aShowInfo.ShowItemsMode = 0;
2747 23 : aShowInfo.ItemCount = 0;
2748 46 : pDim->SetAutoShowInfo(&aShowInfo);
2749 : }
2750 41 : }
2751 :
2752 : // Don't forget the data layout dimension.
2753 16 : ScDPSaveDimension* pDim = aSaveData.GetDataLayoutDimension();
2754 16 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
2755 16 : pDim->SetShowEmpty(true);
2756 :
2757 16 : pDPObj->SetSaveData(aSaveData);
2758 16 : pDPObj->InvalidateData();
2759 :
2760 16 : return pDPObj;
2761 : }
2762 :
2763 15 : ScDPObject* createDPFromRange(
2764 : ScDocument* pDoc, const ScRange& rRange, DPFieldDef aFields[], size_t nFieldCount,
2765 : bool bFilterButton)
2766 : {
2767 15 : ScSheetSourceDesc aSheetDesc(pDoc);
2768 15 : aSheetDesc.SetSourceRange(rRange);
2769 15 : return createDPFromSourceDesc(pDoc, aSheetDesc, aFields, nFieldCount, bFilterButton);
2770 : }
2771 :
2772 28 : ScRange refresh(ScDPObject* pDPObj)
2773 : {
2774 28 : bool bOverFlow = false;
2775 28 : ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
2776 28 : CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
2777 :
2778 28 : pDPObj->Output(aOutRange.aStart);
2779 28 : aOutRange = pDPObj->GetOutRange();
2780 28 : return aOutRange;
2781 : }
2782 :
2783 4 : ScRange refreshGroups(ScDPCollection* pDPs, ScDPObject* pDPObj)
2784 : {
2785 : // We need to first create group data in the cache, then the group data in
2786 : // the object.
2787 4 : std::set<ScDPObject*> aRefs;
2788 4 : bool bSuccess = pDPs->ReloadGroupsInCache(pDPObj, aRefs);
2789 4 : CPPUNIT_ASSERT_MESSAGE("Failed to reload group data in cache.", bSuccess);
2790 4 : CPPUNIT_ASSERT_MESSAGE("There should be only one table linked to this cache.", aRefs.size() == 1);
2791 4 : pDPObj->ReloadGroupTableData();
2792 :
2793 4 : return refresh(pDPObj);
2794 : }
2795 :
2796 : }
2797 :
2798 1 : void Test::testPivotTable()
2799 : {
2800 1 : m_pDoc->InsertTab(0, OUString("Data"));
2801 1 : m_pDoc->InsertTab(1, OUString("Table"));
2802 :
2803 : // Dimension definition
2804 : DPFieldDef aFields[] = {
2805 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
2806 : { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
2807 : { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
2808 1 : };
2809 :
2810 : // Raw data
2811 : const char* aData[][3] = {
2812 : { "Andy", "A", "30" },
2813 : { "Bruce", "A", "20" },
2814 : { "Charlie", "B", "45" },
2815 : { "David", "B", "12" },
2816 : { "Edward", "C", "8" },
2817 : { "Frank", "C", "15" },
2818 1 : };
2819 :
2820 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
2821 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
2822 :
2823 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
2824 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
2825 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
2826 :
2827 : ScDPObject* pDPObj = createDPFromRange(
2828 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2829 :
2830 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2831 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2832 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2833 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2834 1 : pDPs->GetCount() == 1);
2835 1 : pDPObj->SetName(pDPs->CreateNewName());
2836 :
2837 1 : bool bOverFlow = false;
2838 1 : ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
2839 1 : CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
2840 :
2841 1 : pDPObj->Output(aOutRange.aStart);
2842 1 : aOutRange = pDPObj->GetOutRange();
2843 : {
2844 : // Expected output table content. 0 = empty cell
2845 : const char* aOutputCheck[][5] = {
2846 : { "Sum - Score", "Group", 0, 0, 0 },
2847 : { "Name", "A", "B", "C", "Total Result" },
2848 : { "Andy", "30", 0, 0, "30" },
2849 : { "Bruce", "20", 0, 0, "20" },
2850 : { "Charlie", 0, "45", 0, "45" },
2851 : { "David", 0, "12", 0, "12" },
2852 : { "Edward", 0, 0, "8", "8" },
2853 : { "Frank", 0, 0, "15", "15" },
2854 : { "Total Result", "50", "57", "23", "130" }
2855 1 : };
2856 :
2857 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2858 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2859 : }
2860 1 : CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
2861 :
2862 : // Update the cell values.
2863 1 : double aData2[] = { 100, 200, 300, 400, 500, 600 };
2864 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aData2); ++i)
2865 : {
2866 6 : SCROW nRow = i + 1;
2867 6 : m_pDoc->SetValue(2, nRow, 0, aData2[i]);
2868 : }
2869 :
2870 1 : printRange(m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), "Data sheet content (modified)");
2871 :
2872 : // Now, create a copy of the datapilot object for the updated table, but
2873 : // don't reload the cache which should force the copy to use the old data
2874 : // from the cache.
2875 1 : ScDPObject* pDPObj2 = new ScDPObject(*pDPObj);
2876 1 : pDPs->InsertNewTable(pDPObj2);
2877 :
2878 1 : aOutRange = pDPObj2->GetOutRange();
2879 1 : pDPObj2->ClearTableData();
2880 1 : pDPObj2->Output(aOutRange.aStart);
2881 : {
2882 : // Expected output table content. 0 = empty cell
2883 : const char* aOutputCheck[][5] = {
2884 : { "Sum - Score", "Group", 0, 0, 0 },
2885 : { "Name", "A", "B", "C", "Total Result" },
2886 : { "Andy", "30", 0, 0, "30" },
2887 : { "Bruce", "20", 0, 0, "20" },
2888 : { "Charlie", 0, "45", 0, "45" },
2889 : { "David", 0, "12", 0, "12" },
2890 : { "Edward", 0, 0, "8", "8" },
2891 : { "Frank", 0, 0, "15", "15" },
2892 : { "Total Result", "50", "57", "23", "130" }
2893 1 : };
2894 :
2895 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (from old cache)");
2896 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2897 : }
2898 :
2899 1 : CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
2900 :
2901 : // Free the first datapilot object after the 2nd one gets reloaded, to
2902 : // prevent the data cache from being deleted before the reload.
2903 1 : pDPs->FreeTable(pDPObj);
2904 :
2905 1 : CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
2906 :
2907 : // This time clear the cache to refresh the data from the source range.
2908 1 : CPPUNIT_ASSERT_MESSAGE("This datapilot should be based on sheet data.", pDPObj2->IsSheetData());
2909 1 : std::set<ScDPObject*> aRefs;
2910 1 : sal_uLong nErrId = pDPs->ReloadCache(pDPObj2, aRefs);
2911 1 : CPPUNIT_ASSERT_MESSAGE("Cache reload failed.", nErrId == 0);
2912 2 : CPPUNIT_ASSERT_MESSAGE("Reloading a cache shouldn't remove any cache.",
2913 1 : pDPs->GetSheetCaches().size() == 1);
2914 :
2915 1 : pDPObj2->ClearTableData();
2916 1 : pDPObj2->Output(aOutRange.aStart);
2917 :
2918 : {
2919 : // Expected output table content. 0 = empty cell
2920 : const char* aOutputCheck[][5] = {
2921 : { "Sum - Score", "Group", 0, 0, 0 },
2922 : { "Name", "A", "B", "C", "Total Result" },
2923 : { "Andy", "100", 0, 0, "100" },
2924 : { "Bruce", "200", 0, 0, "200" },
2925 : { "Charlie", 0, "300", 0, "300" },
2926 : { "David", 0, "400", 0, "400" },
2927 : { "Edward", 0, 0, "500", "500" },
2928 : { "Frank", 0, 0, "600", "600" },
2929 : { "Total Result", "300", "700", "1100", "2100" }
2930 1 : };
2931 :
2932 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (refreshed)");
2933 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2934 : }
2935 :
2936 1 : CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
2937 :
2938 : // Swap the two sheets.
2939 1 : m_pDoc->MoveTab(1, 0);
2940 2 : CPPUNIT_ASSERT_MESSAGE("Swapping the sheets shouldn't remove the cache.",
2941 1 : pDPs->GetSheetCaches().size() == 1);
2942 1 : CPPUNIT_ASSERT_MESSAGE("Cache should have moved.", !pDPs->GetSheetCaches().hasCache(aSrcRange));
2943 1 : aSrcRange.aStart.SetTab(1);
2944 1 : aSrcRange.aEnd.SetTab(1);
2945 1 : CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
2946 :
2947 1 : pDPs->FreeTable(pDPObj2);
2948 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
2949 1 : pDPs->GetCount() == 0);
2950 :
2951 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more data cache.",
2952 1 : pDPs->GetSheetCaches().size() == 0);
2953 :
2954 : // Insert a brand new pivot table object once again, but this time, don't
2955 : // create the output to avoid creating a data cache.
2956 1 : m_pDoc->DeleteTab(1);
2957 1 : m_pDoc->InsertTab(1, OUString("Table"));
2958 :
2959 : pDPObj = createDPFromRange(
2960 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2961 1 : bSuccess = pDPs->InsertNewTable(pDPObj);
2962 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2963 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2964 1 : pDPs->GetCount() == 1);
2965 1 : pDPObj->SetName(pDPs->CreateNewName());
2966 2 : CPPUNIT_ASSERT_MESSAGE("Data cache shouldn't exist yet before creating the table output.",
2967 1 : pDPs->GetSheetCaches().size() == 0);
2968 :
2969 : // Now, "refresh" the table. This should still return a reference to self
2970 : // even with the absence of data cache.
2971 1 : aRefs.clear();
2972 1 : pDPs->ReloadCache(pDPObj, aRefs);
2973 2 : CPPUNIT_ASSERT_MESSAGE("It should return the same object as a reference.",
2974 1 : aRefs.size() == 1 && *aRefs.begin() == pDPObj);
2975 :
2976 1 : pDPs->FreeTable(pDPObj);
2977 :
2978 1 : m_pDoc->DeleteTab(1);
2979 1 : m_pDoc->DeleteTab(0);
2980 1 : }
2981 :
2982 1 : void Test::testPivotTableLabels()
2983 : {
2984 1 : m_pDoc->InsertTab(0, OUString("Data"));
2985 1 : m_pDoc->InsertTab(1, OUString("Table"));
2986 :
2987 : // Dimension definition
2988 : DPFieldDef aFields[] = {
2989 : { "Software", sheet::DataPilotFieldOrientation_ROW, 0 },
2990 : { "Version", sheet::DataPilotFieldOrientation_COLUMN, 0 },
2991 : { "1.2.3", sheet::DataPilotFieldOrientation_DATA, 0 }
2992 1 : };
2993 :
2994 : // Raw data
2995 : const char* aData[][3] = {
2996 : { "LibreOffice", "3.3.0", "30" },
2997 : { "LibreOffice", "3.3.1", "20" },
2998 : { "LibreOffice", "3.4.0", "45" },
2999 1 : };
3000 :
3001 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
3002 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
3003 :
3004 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
3005 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
3006 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
3007 :
3008 : ScDPObject* pDPObj = createDPFromRange(
3009 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
3010 :
3011 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3012 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3013 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
3014 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3015 1 : pDPs->GetCount() == 1);
3016 1 : pDPObj->SetName(pDPs->CreateNewName());
3017 :
3018 1 : ScRange aOutRange = refresh(pDPObj);
3019 : {
3020 : // Expected output table content. 0 = empty cell
3021 : const char* aOutputCheck[][5] = {
3022 : { "Sum - 1.2.3", "Version", 0, 0, 0 },
3023 : { "Software", "3.3.0", "3.3.1", "3.4.0", "Total Result" },
3024 : { "LibreOffice", "30", "20", "45", "95" },
3025 : { "Total Result", "30", "20", "45", "95" }
3026 1 : };
3027 :
3028 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3029 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3030 : }
3031 :
3032 1 : pDPs->FreeTable(pDPObj);
3033 :
3034 1 : m_pDoc->DeleteTab(1);
3035 1 : m_pDoc->DeleteTab(0);
3036 1 : }
3037 :
3038 1 : void Test::testPivotTableDateLabels()
3039 : {
3040 1 : m_pDoc->InsertTab(0, OUString("Data"));
3041 1 : m_pDoc->InsertTab(1, OUString("Table"));
3042 :
3043 : // Dimension definition
3044 : DPFieldDef aFields[] = {
3045 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3046 : { "Date", sheet::DataPilotFieldOrientation_COLUMN, 0 },
3047 : { "Value", sheet::DataPilotFieldOrientation_DATA, 0 }
3048 1 : };
3049 :
3050 : // Raw data
3051 : const char* aData[][3] = {
3052 : { "Zena", "2011-1-1", "30" },
3053 : { "Yodel", "2011-1-2", "20" },
3054 : { "Xavior", "2011-1-3", "45" }
3055 1 : };
3056 :
3057 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
3058 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
3059 :
3060 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
3061 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
3062 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
3063 :
3064 : ScDPObject* pDPObj = createDPFromRange(
3065 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
3066 :
3067 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3068 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3069 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
3070 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3071 1 : pDPs->GetCount() == 1);
3072 1 : pDPObj->SetName(pDPs->CreateNewName());
3073 :
3074 1 : ScRange aOutRange = refresh(pDPObj);
3075 : {
3076 : // Expected output table content. 0 = empty cell
3077 : const char* aOutputCheck[][5] = {
3078 : { "Sum - Value", "Date", 0, 0, 0 },
3079 : { "Name", "2011-01-01", "2011-01-02", "2011-01-03", "Total Result" },
3080 : { "Xavior", 0, 0, "45", "45" },
3081 : { "Yodel", 0, "20", 0, "20" },
3082 : { "Zena", "30", 0, 0, "30" },
3083 : { "Total Result", "30", "20", "45", "95" }
3084 1 : };
3085 :
3086 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3087 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3088 : }
3089 :
3090 : {
3091 : const char* aChecks[] = {
3092 : "2011-01-01", "2011-01-02", "2011-01-03"
3093 1 : };
3094 :
3095 : // Make sure those cells that contain dates are numeric.
3096 1 : SCROW nRow = aOutRange.aStart.Row() + 1;
3097 1 : nCol1 = aOutRange.aStart.Col() + 1;
3098 1 : nCol2 = nCol1 + 2;
3099 4 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
3100 : {
3101 3 : OUString aVal = m_pDoc->GetString(nCol, nRow, 1);
3102 3 : CPPUNIT_ASSERT_MESSAGE("Cell value is not as expected.", aVal.equalsAscii(aChecks[nCol-nCol1]));
3103 6 : CPPUNIT_ASSERT_MESSAGE("This cell contains a date value and is supposed to be numeric.",
3104 3 : m_pDoc->HasValueData(nCol, nRow, 1));
3105 3 : }
3106 : }
3107 :
3108 1 : pDPs->FreeTable(pDPObj);
3109 :
3110 1 : m_pDoc->DeleteTab(1);
3111 1 : m_pDoc->DeleteTab(0);
3112 1 : }
3113 :
3114 1 : void Test::testPivotTableFilters()
3115 : {
3116 1 : m_pDoc->InsertTab(0, OUString("Data"));
3117 1 : m_pDoc->InsertTab(1, OUString("Table"));
3118 :
3119 : // Dimension definition
3120 : DPFieldDef aFields[] = {
3121 : { "Name", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
3122 : { "Group1", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
3123 : { "Group2", sheet::DataPilotFieldOrientation_PAGE, 0 },
3124 : { "Val1", sheet::DataPilotFieldOrientation_DATA, 0 },
3125 : { "Val2", sheet::DataPilotFieldOrientation_DATA, 0 }
3126 1 : };
3127 :
3128 : // Raw data
3129 : const char* aData[][5] = {
3130 : { "A", "1", "A", "1", "10" },
3131 : { "B", "1", "A", "1", "10" },
3132 : { "C", "1", "B", "1", "10" },
3133 : { "D", "1", "B", "1", "10" },
3134 : { "E", "2", "A", "1", "10" },
3135 : { "F", "2", "A", "1", "10" },
3136 : { "G", "2", "B", "1", "10" },
3137 : { "H", "2", "B", "1", "10" }
3138 1 : };
3139 :
3140 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
3141 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
3142 :
3143 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
3144 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
3145 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
3146 :
3147 : ScDPObject* pDPObj = createDPFromRange(
3148 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, true);
3149 :
3150 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3151 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3152 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
3153 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3154 1 : pDPs->GetCount() == 1);
3155 1 : pDPObj->SetName(pDPs->CreateNewName());
3156 :
3157 1 : ScRange aOutRange = refresh(pDPObj);
3158 : {
3159 : // Expected output table content. 0 = empty cell
3160 : const char* aOutputCheck[][2] = {
3161 : { "Filter", 0 },
3162 : { "Group2", "- all -" },
3163 : { 0, 0 },
3164 : { "Data", 0 },
3165 : { "Sum - Val1", "8" },
3166 : { "Sum - Val2", "80" }
3167 1 : };
3168 :
3169 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (unfiltered)");
3170 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3171 : }
3172 :
3173 1 : sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
3174 :
3175 1 : ScAddress aFormulaAddr = aOutRange.aEnd;
3176 1 : aFormulaAddr.IncRow(2);
3177 2 : m_pDoc->SetString(aFormulaAddr.Col(), aFormulaAddr.Row(), aFormulaAddr.Tab(),
3178 3 : OUString("=B6"));
3179 1 : double fTest = m_pDoc->GetValue(aFormulaAddr);
3180 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 80.0);
3181 :
3182 : // Set current page of 'Group2' to 'A'.
3183 1 : pDPObj->BuildAllDimensionMembers();
3184 2 : ScDPSaveData aSaveData(*pDPObj->GetSaveData());
3185 : ScDPSaveDimension* pPageDim = aSaveData.GetDimensionByName(
3186 1 : OUString("Group2"));
3187 1 : CPPUNIT_ASSERT_MESSAGE("Dimension not found", pPageDim);
3188 2 : OUString aPage("A");
3189 1 : pPageDim->SetCurrentPage(&aPage);
3190 1 : pDPObj->SetSaveData(aSaveData);
3191 1 : aOutRange = refresh(pDPObj);
3192 : {
3193 : // Expected output table content. 0 = empty cell
3194 : const char* aOutputCheck[][2] = {
3195 : { "Filter", 0 },
3196 : { "Group2", "A" },
3197 : { 0, 0 },
3198 : { "Data", 0 },
3199 : { "Sum - Val1", "4" },
3200 : { "Sum - Val2", "40" }
3201 1 : };
3202 :
3203 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
3204 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3205 : }
3206 :
3207 1 : fTest = m_pDoc->GetValue(aFormulaAddr);
3208 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 40.0);
3209 :
3210 : // Set query filter.
3211 2 : ScSheetSourceDesc aDesc(*pDPObj->GetSheetDesc());
3212 2 : ScQueryParam aQueryParam(aDesc.GetQueryParam());
3213 1 : CPPUNIT_ASSERT_MESSAGE("There should be at least one query entry.", aQueryParam.GetEntryCount() > 0);
3214 1 : ScQueryEntry& rEntry = aQueryParam.GetEntry(0);
3215 1 : rEntry.bDoQuery = true;
3216 1 : rEntry.nField = 1; // Group1
3217 1 : rEntry.GetQueryItem().mfVal = 1;
3218 1 : aDesc.SetQueryParam(aQueryParam);
3219 1 : pDPObj->SetSheetDesc(aDesc);
3220 1 : aOutRange = refresh(pDPObj);
3221 : {
3222 : // Expected output table content. 0 = empty cell
3223 : const char* aOutputCheck[][2] = {
3224 : { "Filter", 0 },
3225 : { "Group2", "A" },
3226 : { 0, 0 },
3227 : { "Data", 0 },
3228 : { "Sum - Val1", "2" },
3229 : { "Sum - Val2", "20" }
3230 1 : };
3231 :
3232 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by query)");
3233 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3234 : }
3235 :
3236 1 : fTest = m_pDoc->GetValue(aFormulaAddr);
3237 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 20.0);
3238 :
3239 : // Set the current page of 'Group2' back to '- all -'. The query filter
3240 : // should still be in effect.
3241 1 : pPageDim->SetCurrentPage(NULL); // Remove the page.
3242 1 : pDPObj->SetSaveData(aSaveData);
3243 1 : aOutRange = refresh(pDPObj);
3244 : {
3245 : // Expected output table content. 0 = empty cell
3246 : const char* aOutputCheck[][2] = {
3247 : { "Filter", 0 },
3248 : { "Group2", "- all -" },
3249 : { 0, 0 },
3250 : { "Data", 0 },
3251 : { "Sum - Val1", "4" },
3252 : { "Sum - Val2", "40" }
3253 1 : };
3254 :
3255 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
3256 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3257 : }
3258 :
3259 :
3260 1 : pDPs->FreeTable(pDPObj);
3261 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
3262 1 : pDPs->GetCount() == 0);
3263 :
3264 1 : m_pDoc->DeleteTab(1);
3265 2 : m_pDoc->DeleteTab(0);
3266 1 : }
3267 :
3268 1 : void Test::testPivotTableNamedSource()
3269 : {
3270 1 : m_pDoc->InsertTab(0, OUString("Data"));
3271 1 : m_pDoc->InsertTab(1, OUString("Table"));
3272 :
3273 : // Dimension definition
3274 : DPFieldDef aFields[] = {
3275 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3276 : { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
3277 : { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
3278 1 : };
3279 :
3280 : // Raw data
3281 : const char* aData[][3] = {
3282 : { "Andy", "A", "30" },
3283 : { "Bruce", "A", "20" },
3284 : { "Charlie", "B", "45" },
3285 : { "David", "B", "12" },
3286 : { "Edward", "C", "8" },
3287 : { "Frank", "C", "15" },
3288 1 : };
3289 :
3290 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
3291 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
3292 :
3293 : // Insert the raw data.
3294 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
3295 1 : OUString aRangeStr;
3296 1 : aSrcRange.Format(aRangeStr, SCR_ABS_3D, m_pDoc);
3297 :
3298 : // Name this range.
3299 2 : OUString aRangeName("MyData");
3300 1 : ScRangeName* pNames = m_pDoc->GetRangeName();
3301 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get global range name container.", pNames);
3302 : ScRangeData* pName = new ScRangeData(
3303 1 : m_pDoc, aRangeName, aRangeStr);
3304 1 : bool bSuccess = pNames->insert(pName);
3305 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bSuccess);
3306 :
3307 2 : ScSheetSourceDesc aSheetDesc(m_pDoc);
3308 1 : aSheetDesc.SetRangeName(aRangeName);
3309 1 : ScDPObject* pDPObj = createDPFromSourceDesc(m_pDoc, aSheetDesc, aFields, nFieldCount, false);
3310 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create a new pivot table object.", pDPObj);
3311 :
3312 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3313 1 : bSuccess = pDPs->InsertNewTable(pDPObj);
3314 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3315 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3316 1 : pDPs->GetCount() == 1);
3317 1 : pDPObj->SetName(pDPs->CreateNewName());
3318 :
3319 1 : ScRange aOutRange = refresh(pDPObj);
3320 : {
3321 : // Expected output table content. 0 = empty cell
3322 : const char* aOutputCheck[][5] = {
3323 : { "Sum - Score", "Group", 0, 0, 0 },
3324 : { "Name", "A", "B", "C", "Total Result" },
3325 : { "Andy", "30", 0, 0, "30" },
3326 : { "Bruce", "20", 0, 0, "20" },
3327 : { "Charlie", 0, "45", 0, "45" },
3328 : { "David", 0, "12", 0, "12" },
3329 : { "Edward", 0, 0, "8", "8" },
3330 : { "Frank", 0, 0, "15", "15" },
3331 : { "Total Result", "50", "57", "23", "130" }
3332 1 : };
3333 :
3334 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3335 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3336 : }
3337 :
3338 2 : CPPUNIT_ASSERT_MESSAGE("There should be one named range data cache.",
3339 1 : pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
3340 :
3341 : // Move the table with pivot table to the left of the source data sheet.
3342 1 : m_pDoc->MoveTab(1, 0);
3343 2 : OUString aTabName;
3344 1 : m_pDoc->GetName(0, aTabName);
3345 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong sheet name.", aTabName == "Table" );
3346 2 : CPPUNIT_ASSERT_MESSAGE("Pivot table output is on the wrong sheet!",
3347 1 : pDPObj->GetOutRange().aStart.Tab() == 0);
3348 :
3349 2 : CPPUNIT_ASSERT_MESSAGE("Moving the pivot table to another sheet shouldn't have changed the cache state.",
3350 1 : pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
3351 :
3352 1 : const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
3353 1 : CPPUNIT_ASSERT_MESSAGE("Sheet source description doesn't exist.", pDesc);
3354 2 : CPPUNIT_ASSERT_MESSAGE("Named source range has been altered unexpectedly!",
3355 1 : pDesc->GetRangeName().equals(aRangeName));
3356 :
3357 1 : CPPUNIT_ASSERT_MESSAGE("Cache should exist.", pDPs->GetNameCaches().hasCache(aRangeName));
3358 :
3359 1 : pDPs->FreeTable(pDPObj);
3360 1 : CPPUNIT_ASSERT_MESSAGE("There should be no more tables.", pDPs->GetCount() == 0);
3361 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more cache stored.",
3362 1 : pDPs->GetNameCaches().size() == 0);
3363 :
3364 1 : pNames->clear();
3365 1 : m_pDoc->DeleteTab(1);
3366 2 : m_pDoc->DeleteTab(0);
3367 1 : }
3368 :
3369 1 : void Test::testPivotTableCache()
3370 : {
3371 1 : m_pDoc->InsertTab(0, OUString("Data"));
3372 :
3373 : // Raw data
3374 : const char* aData[][3] = {
3375 : { "F1", "F2", "F3" },
3376 : { "Z", "A", "30" },
3377 : { "R", "A", "20" },
3378 : { "A", "B", "45" },
3379 : { "F", "B", "12" },
3380 : { "Y", "C", "8" },
3381 : { "12", "C", "15" },
3382 1 : };
3383 :
3384 1 : ScAddress aPos(1,1,0);
3385 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3386 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3387 :
3388 1 : ScDPCache aCache(m_pDoc);
3389 1 : aCache.InitFromDoc(m_pDoc, aDataRange);
3390 1 : long nDimCount = aCache.GetColumnCount();
3391 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension count.", nDimCount == 3);
3392 2 : OUString aDimName = aCache.GetDimensionName(0);
3393 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F1"));
3394 1 : aDimName = aCache.GetDimensionName(1);
3395 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F2"));
3396 1 : aDimName = aCache.GetDimensionName(2);
3397 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F3"));
3398 :
3399 : // In each dimension, member ID values also represent their sort order (in
3400 : // source dimensions only, not in group dimensions). Value items are
3401 : // sorted before string ones. Also, no duplicate dimension members should
3402 : // exist.
3403 :
3404 : // Dimension 0 - a mix of strings and values.
3405 1 : long nMemCount = aCache.GetDimMemberCount(0);
3406 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
3407 1 : const ScDPItemData* pItem = aCache.GetItemDataById(0, 0);
3408 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3409 : pItem->GetType() == ScDPItemData::Value &&
3410 1 : pItem->GetValue() == 12);
3411 1 : pItem = aCache.GetItemDataById(0, 1);
3412 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3413 : pItem->GetType() == ScDPItemData::String &&
3414 1 : pItem->GetString().equalsAscii("A"));
3415 1 : pItem = aCache.GetItemDataById(0, 2);
3416 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3417 : pItem->GetType() == ScDPItemData::String &&
3418 1 : pItem->GetString().equalsAscii("F"));
3419 1 : pItem = aCache.GetItemDataById(0, 3);
3420 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3421 : pItem->GetType() == ScDPItemData::String &&
3422 1 : pItem->GetString().equalsAscii("R"));
3423 1 : pItem = aCache.GetItemDataById(0, 4);
3424 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3425 : pItem->GetType() == ScDPItemData::String &&
3426 1 : pItem->GetString().equalsAscii("Y"));
3427 1 : pItem = aCache.GetItemDataById(0, 5);
3428 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3429 : pItem->GetType() == ScDPItemData::String &&
3430 1 : pItem->GetString().equalsAscii("Z"));
3431 1 : pItem = aCache.GetItemDataById(0, 6);
3432 1 : CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
3433 :
3434 : // Dimension 1 - duplicate values in source.
3435 1 : nMemCount = aCache.GetDimMemberCount(1);
3436 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 3);
3437 1 : pItem = aCache.GetItemDataById(1, 0);
3438 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3439 : pItem->GetType() == ScDPItemData::String &&
3440 1 : pItem->GetString().equalsAscii("A"));
3441 1 : pItem = aCache.GetItemDataById(1, 1);
3442 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3443 : pItem->GetType() == ScDPItemData::String &&
3444 1 : pItem->GetString().equalsAscii("B"));
3445 1 : pItem = aCache.GetItemDataById(1, 2);
3446 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3447 : pItem->GetType() == ScDPItemData::String &&
3448 1 : pItem->GetString().equalsAscii("C"));
3449 1 : pItem = aCache.GetItemDataById(1, 3);
3450 1 : CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
3451 :
3452 : // Dimension 2 - values only.
3453 1 : nMemCount = aCache.GetDimMemberCount(2);
3454 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
3455 1 : pItem = aCache.GetItemDataById(2, 0);
3456 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3457 : pItem->GetType() == ScDPItemData::Value &&
3458 1 : pItem->GetValue() == 8);
3459 1 : pItem = aCache.GetItemDataById(2, 1);
3460 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3461 : pItem->GetType() == ScDPItemData::Value &&
3462 1 : pItem->GetValue() == 12);
3463 1 : pItem = aCache.GetItemDataById(2, 2);
3464 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3465 : pItem->GetType() == ScDPItemData::Value &&
3466 1 : pItem->GetValue() == 15);
3467 1 : pItem = aCache.GetItemDataById(2, 3);
3468 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3469 : pItem->GetType() == ScDPItemData::Value &&
3470 1 : pItem->GetValue() == 20);
3471 1 : pItem = aCache.GetItemDataById(2, 4);
3472 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3473 : pItem->GetType() == ScDPItemData::Value &&
3474 1 : pItem->GetValue() == 30);
3475 1 : pItem = aCache.GetItemDataById(2, 5);
3476 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
3477 : pItem->GetType() == ScDPItemData::Value &&
3478 1 : pItem->GetValue() == 45);
3479 1 : pItem = aCache.GetItemDataById(2, 6);
3480 1 : CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
3481 :
3482 : {
3483 : // Check the integrity of the source data.
3484 1 : ScDPItemData aTest;
3485 : long nDim;
3486 :
3487 : {
3488 : // Dimension 0: Z, R, A, F, Y, 12
3489 1 : nDim = 0;
3490 1 : const char* aChecks[] = { "Z", "R", "A", "F", "Y" };
3491 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3492 : {
3493 5 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
3494 5 : aTest.SetString(OUString::createFromAscii(aChecks[i]));
3495 5 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3496 : }
3497 :
3498 1 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, 5, false));
3499 1 : aTest.SetValue(12);
3500 1 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3501 : }
3502 :
3503 : {
3504 : // Dimension 1: A, A, B, B, C, C
3505 1 : nDim = 1;
3506 1 : const char* aChecks[] = { "A", "A", "B", "B", "C", "C" };
3507 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3508 : {
3509 6 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
3510 6 : aTest.SetString(OUString::createFromAscii(aChecks[i]));
3511 6 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3512 : }
3513 : }
3514 :
3515 : {
3516 : // Dimension 2: 30, 20, 45, 12, 8, 15
3517 1 : nDim = 2;
3518 1 : double aChecks[] = { 30, 20, 45, 12, 8, 15 };
3519 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3520 : {
3521 6 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
3522 6 : aTest.SetValue(aChecks[i]);
3523 6 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
3524 : }
3525 1 : }
3526 : }
3527 :
3528 : // Now, on to testing the filtered cache.
3529 :
3530 : {
3531 : // Non-filtered cache - everything should be visible.
3532 1 : ScDPFilteredCache aFilteredCache(aCache);
3533 1 : aFilteredCache.fillTable();
3534 :
3535 1 : sal_Int32 nRows = aFilteredCache.getRowSize();
3536 1 : CPPUNIT_ASSERT_MESSAGE("Wrong dimension.", nRows == 6 && aFilteredCache.getColSize() == 3);
3537 :
3538 7 : for (sal_Int32 i = 0; i < nRows; ++i)
3539 : {
3540 6 : if (!aFilteredCache.isRowActive(i))
3541 : {
3542 0 : std::ostringstream os;
3543 0 : os << "Row " << i << " should be visible but it isn't.";
3544 0 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
3545 : }
3546 1 : }
3547 : }
3548 :
3549 : // TODO : Add test for filtered caches.
3550 :
3551 2 : m_pDoc->DeleteTab(0);
3552 1 : }
3553 :
3554 1 : void Test::testPivotTableDuplicateDataFields()
3555 : {
3556 1 : m_pDoc->InsertTab(0, OUString("Data"));
3557 1 : m_pDoc->InsertTab(1, OUString("Table"));
3558 :
3559 : // Raw data
3560 : const char* aData[][2] = {
3561 : { "Name", "Value" },
3562 : { "A", "45" },
3563 : { "A", "5" },
3564 : { "A", "41" },
3565 : { "A", "49" },
3566 : { "A", "4" },
3567 : { "B", "33" },
3568 : { "B", "84" },
3569 : { "B", "74" },
3570 : { "B", "8" },
3571 : { "B", "68" }
3572 1 : };
3573 :
3574 : // Dimension definition
3575 : DPFieldDef aFields[] = {
3576 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3577 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3578 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_COUNT }
3579 1 : };
3580 :
3581 1 : ScAddress aPos(2,2,0);
3582 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3583 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3584 :
3585 : ScDPObject* pDPObj = createDPFromRange(
3586 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3587 :
3588 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3589 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3590 :
3591 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3592 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3593 1 : pDPs->GetCount(), static_cast<size_t>(1));
3594 1 : pDPObj->SetName(pDPs->CreateNewName());
3595 :
3596 1 : ScRange aOutRange = refresh(pDPObj);
3597 : {
3598 : // Expected output table content. 0 = empty cell
3599 : const char* aOutputCheck[][3] = {
3600 : { "Name", "Data", 0 },
3601 : { "A", "Sum - Value", "144" },
3602 : { 0, "Count - Value", "5" },
3603 : { "B", "Sum - Value", "267" },
3604 : { 0, "Count - Value", "5" },
3605 : { "Total Sum - Value", 0, "411" },
3606 : { "Total Count - Value", 0, "10" },
3607 1 : };
3608 :
3609 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3610 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3611 : }
3612 :
3613 : // Move the data layout dimension from row to column.
3614 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3615 1 : CPPUNIT_ASSERT_MESSAGE("No save data!?", pSaveData);
3616 1 : ScDPSaveDimension* pDataLayout = pSaveData->GetDataLayoutDimension();
3617 1 : CPPUNIT_ASSERT_MESSAGE("No data layout dimension.", pDataLayout);
3618 1 : pDataLayout->SetOrientation(sheet::DataPilotFieldOrientation_COLUMN);
3619 1 : pDPObj->SetSaveData(*pSaveData);
3620 :
3621 : // Refresh the table output.
3622 1 : aOutRange = refresh(pDPObj);
3623 : {
3624 : // Expected output table content. 0 = empty cell
3625 : const char* aOutputCheck[][3] = {
3626 : { 0, "Data", 0 },
3627 : { "Name", "Sum - Value", "Count - Value" },
3628 : { "A", "144", "5" },
3629 : { "B", "267", "5" },
3630 : { "Total Result", "411", "10" }
3631 1 : };
3632 :
3633 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
3634 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3635 : }
3636 :
3637 1 : ScPivotParam aParam;
3638 1 : pDPObj->FillLabelData(aParam);
3639 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 4 labels (2 original, 1 data layout, and 1 duplicate dimensions).",
3640 1 : aParam.maLabelArray.size(), static_cast<size_t>(4));
3641 :
3642 1 : pDPs->FreeTable(pDPObj);
3643 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3644 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3645 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3646 :
3647 1 : m_pDoc->DeleteTab(1);
3648 1 : m_pDoc->DeleteTab(0);
3649 1 : }
3650 :
3651 1 : void Test::testPivotTableNormalGrouping()
3652 : {
3653 1 : m_pDoc->InsertTab(0, OUString("Data"));
3654 1 : m_pDoc->InsertTab(1, OUString("Table"));
3655 :
3656 : // Raw data
3657 : const char* aData[][2] = {
3658 : { "Name", "Value" },
3659 : { "A", "1" },
3660 : { "B", "2" },
3661 : { "C", "3" },
3662 : { "D", "4" },
3663 : { "E", "5" },
3664 : { "F", "6" },
3665 : { "G", "7" }
3666 1 : };
3667 :
3668 : // Dimension definition
3669 : DPFieldDef aFields[] = {
3670 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3671 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3672 1 : };
3673 :
3674 1 : ScAddress aPos(1,1,0);
3675 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3676 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3677 :
3678 : ScDPObject* pDPObj = createDPFromRange(
3679 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3680 :
3681 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3682 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3683 :
3684 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3685 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3686 1 : pDPs->GetCount(), static_cast<size_t>(1));
3687 1 : pDPObj->SetName(pDPs->CreateNewName());
3688 :
3689 1 : ScRange aOutRange = refresh(pDPObj);
3690 : {
3691 : // Expected output table content. 0 = empty cell
3692 : const char* aOutputCheck[][2] = {
3693 : { "Name", 0 },
3694 : { "A", "1" },
3695 : { "B", "2" },
3696 : { "C", "3" },
3697 : { "D", "4" },
3698 : { "E", "5" },
3699 : { "F", "6" },
3700 : { "G", "7" },
3701 : { "Total Result", "28" }
3702 1 : };
3703 :
3704 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Initial output without grouping");
3705 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3706 : }
3707 :
3708 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3709 1 : CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
3710 1 : ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
3711 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create dimension data.", pDimData);
3712 :
3713 1 : OUString aGroupPrefix("Group");
3714 2 : OUString aBaseDimName("Name");
3715 : OUString aGroupDimName =
3716 2 : pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, NULL);
3717 :
3718 : {
3719 : // Group A, B and C together.
3720 1 : ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
3721 2 : OUString aGroupName = aGroupDim.CreateGroupName(aGroupPrefix);
3722 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group1"));
3723 :
3724 2 : ScDPSaveGroupItem aGroup(aGroupName);
3725 1 : aGroup.AddElement(OUString("A"));
3726 1 : aGroup.AddElement(OUString("B"));
3727 1 : aGroup.AddElement(OUString("C"));
3728 1 : aGroupDim.AddGroupItem(aGroup);
3729 1 : pDimData->AddGroupDimension(aGroupDim);
3730 :
3731 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
3732 1 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
3733 2 : pSaveData->SetPosition(pDim, 0); // Set it before the base dimension.
3734 : }
3735 :
3736 1 : pDPObj->SetSaveData(*pSaveData);
3737 1 : aOutRange = refreshGroups(pDPs, pDPObj);
3738 : {
3739 : // Expected output table content. 0 = empty cell
3740 : const char* aOutputCheck[][3] = {
3741 : { "Name2", "Name", 0 },
3742 : { "D", "D", "4" },
3743 : { "E", "E", "5" },
3744 : { "F", "F", "6" },
3745 : { "G", "G", "7" },
3746 : { "Group1", "A", "1" },
3747 : { 0, "B", "2" },
3748 : { 0, "C", "3" },
3749 : { "Total Result", 0, "28" }
3750 1 : };
3751 :
3752 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "A, B, C grouped by Group1.");
3753 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3754 : }
3755 :
3756 1 : pSaveData = pDPObj->GetSaveData();
3757 1 : pDimData = pSaveData->GetDimensionData();
3758 :
3759 : {
3760 : // Group D, E, F together.
3761 1 : ScDPSaveGroupDimension* pGroupDim = pDimData->GetGroupDimAccForBase(aBaseDimName);
3762 1 : CPPUNIT_ASSERT_MESSAGE("There should be an existing group dimension.", pGroupDim);
3763 1 : OUString aGroupName = pGroupDim->CreateGroupName(aGroupPrefix);
3764 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group2"));
3765 :
3766 2 : ScDPSaveGroupItem aGroup(aGroupName);
3767 1 : aGroup.AddElement(OUString("D"));
3768 1 : aGroup.AddElement(OUString("E"));
3769 1 : aGroup.AddElement(OUString("F"));
3770 2 : pGroupDim->AddGroupItem(aGroup);
3771 : }
3772 :
3773 1 : pDPObj->SetSaveData(*pSaveData);
3774 1 : aOutRange = refreshGroups(pDPs, pDPObj);
3775 : {
3776 : // Expected output table content. 0 = empty cell
3777 : const char* aOutputCheck[][3] = {
3778 : { "Name2", "Name", 0 },
3779 : { "G", "G", "7" },
3780 : { "Group1", "A", "1" },
3781 : { 0, "B", "2" },
3782 : { 0, "C", "3" },
3783 : { "Group2", "D", "4" },
3784 : { 0, "E", "5" },
3785 : { 0, "F", "6" },
3786 : { "Total Result", 0, "28" }
3787 1 : };
3788 :
3789 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "D, E, F grouped by Group2.");
3790 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3791 : }
3792 :
3793 1 : pDPs->FreeTable(pDPObj);
3794 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3795 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3796 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3797 :
3798 1 : m_pDoc->DeleteTab(1);
3799 2 : m_pDoc->DeleteTab(0);
3800 1 : }
3801 :
3802 1 : void Test::testPivotTableNumberGrouping()
3803 : {
3804 1 : m_pDoc->InsertTab(0, OUString("Data"));
3805 1 : m_pDoc->InsertTab(1, OUString("Table"));
3806 :
3807 : // Raw data
3808 : const char* aData[][2] = {
3809 : { "Order", "Score" },
3810 : { "43", "171" },
3811 : { "18", "20" },
3812 : { "69", "159" },
3813 : { "95", "19" },
3814 : { "96", "163" },
3815 : { "46", "70" },
3816 : { "22", "36" },
3817 : { "81", "49" },
3818 : { "54", "61" },
3819 : { "39", "62" },
3820 : { "86", "17" },
3821 : { "34", "0" },
3822 : { "30", "25" },
3823 : { "24", "103" },
3824 : { "16", "59" },
3825 : { "24", "119" },
3826 : { "15", "86" },
3827 : { "69", "170" }
3828 1 : };
3829 :
3830 : // Dimension definition
3831 : DPFieldDef aFields[] = {
3832 : { "Order", sheet::DataPilotFieldOrientation_ROW, 0 },
3833 : { "Score", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3834 1 : };
3835 :
3836 1 : ScAddress aPos(1,1,0);
3837 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3838 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3839 :
3840 : ScDPObject* pDPObj = createDPFromRange(
3841 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3842 :
3843 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3844 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3845 :
3846 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3847 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3848 1 : pDPs->GetCount(), static_cast<size_t>(1));
3849 1 : pDPObj->SetName(pDPs->CreateNewName());
3850 :
3851 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3852 1 : CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
3853 1 : ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
3854 1 : CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
3855 :
3856 : {
3857 1 : ScDPNumGroupInfo aInfo;
3858 1 : aInfo.mbEnable = true;
3859 1 : aInfo.mbAutoStart = false;
3860 1 : aInfo.mbAutoEnd = false;
3861 1 : aInfo.mbDateValues = false;
3862 1 : aInfo.mbIntegerOnly = true;
3863 1 : aInfo.mfStart = 30;
3864 1 : aInfo.mfEnd = 60;
3865 1 : aInfo.mfStep = 10;
3866 1 : ScDPSaveNumGroupDimension aGroup(OUString("Order"), aInfo);
3867 1 : pDimData->AddNumGroupDimension(aGroup);
3868 : }
3869 :
3870 1 : pDPObj->SetSaveData(*pSaveData);
3871 1 : ScRange aOutRange = refreshGroups(pDPs, pDPObj);
3872 : {
3873 : // Expected output table content. 0 = empty cell
3874 : const char* aOutputCheck[][2] = {
3875 : { "Order", 0 },
3876 : { "<30", "423" },
3877 : { "30-39", "87" },
3878 : { "40-49", "241" },
3879 : { "50-60", "61" },
3880 : { ">60", "577" },
3881 : { "Total Result", "1389" }
3882 1 : };
3883 :
3884 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Order grouped by numbers");
3885 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3886 : }
3887 :
3888 1 : pDPs->FreeTable(pDPObj);
3889 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3890 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3891 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3892 :
3893 1 : m_pDoc->DeleteTab(1);
3894 1 : m_pDoc->DeleteTab(0);
3895 1 : }
3896 :
3897 1 : void Test::testPivotTableDateGrouping()
3898 : {
3899 1 : m_pDoc->InsertTab(0, OUString("Data"));
3900 1 : m_pDoc->InsertTab(1, OUString("Table"));
3901 :
3902 : // Raw data
3903 : const char* aData[][2] = {
3904 : { "Date", "Value" },
3905 : { "2011-01-01", "1" },
3906 : { "2011-03-02", "2" },
3907 : { "2012-01-04", "3" },
3908 : { "2012-02-23", "4" },
3909 : { "2012-02-24", "5" },
3910 : { "2012-03-15", "6" },
3911 : { "2011-09-03", "7" },
3912 : { "2012-12-25", "8" }
3913 1 : };
3914 :
3915 : // Dimension definition
3916 : DPFieldDef aFields[] = {
3917 : { "Date", sheet::DataPilotFieldOrientation_ROW, 0 },
3918 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3919 1 : };
3920 :
3921 1 : ScAddress aPos(1,1,0);
3922 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3923 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3924 :
3925 : ScDPObject* pDPObj = createDPFromRange(
3926 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3927 :
3928 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3929 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3930 :
3931 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3932 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3933 1 : pDPs->GetCount() == 1);
3934 1 : pDPObj->SetName(pDPs->CreateNewName());
3935 :
3936 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3937 1 : CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
3938 1 : ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
3939 1 : CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
3940 :
3941 1 : OUString aBaseDimName("Date");
3942 :
3943 1 : ScDPNumGroupInfo aInfo;
3944 1 : aInfo.mbEnable = true;
3945 1 : aInfo.mbAutoStart = true;
3946 1 : aInfo.mbAutoEnd = true;
3947 : {
3948 : // Turn the Date dimension into months. The first of the date
3949 : // dimensions is always a number-group dimension which replaces the
3950 : // original dimension.
3951 1 : ScDPSaveNumGroupDimension aGroup(aBaseDimName, aInfo, sheet::DataPilotFieldGroupBy::MONTHS);
3952 1 : pDimData->AddNumGroupDimension(aGroup);
3953 : }
3954 :
3955 : {
3956 : // Add quarter dimension. This will be an additional dimension.
3957 : OUString aGroupDimName =
3958 : pDimData->CreateDateGroupDimName(
3959 1 : sheet::DataPilotFieldGroupBy::QUARTERS, *pDPObj, true, NULL);
3960 2 : ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
3961 1 : aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::QUARTERS);
3962 1 : pDimData->AddGroupDimension(aGroupDim);
3963 :
3964 : // Set orientation.
3965 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
3966 1 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
3967 2 : pSaveData->SetPosition(pDim, 0); // set it to the left end.
3968 : }
3969 :
3970 : {
3971 : // Add year dimension. This is a new dimension also.
3972 : OUString aGroupDimName =
3973 : pDimData->CreateDateGroupDimName(
3974 1 : sheet::DataPilotFieldGroupBy::YEARS, *pDPObj, true, NULL);
3975 2 : ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
3976 1 : aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::YEARS);
3977 1 : pDimData->AddGroupDimension(aGroupDim);
3978 :
3979 : // Set orientation.
3980 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
3981 1 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
3982 2 : pSaveData->SetPosition(pDim, 0); // set it to the left end.
3983 : }
3984 :
3985 1 : pDPObj->SetSaveData(*pSaveData);
3986 1 : ScRange aOutRange = refreshGroups(pDPs, pDPObj);
3987 : {
3988 : // Expected output table content. 0 = empty cell
3989 : const char* aOutputCheck[][4] = {
3990 : { "Years", "Quarters", "Date", 0 },
3991 : { "2011", "Q1", "Jan", "1" },
3992 : { 0, 0, "Mar", "2" },
3993 : { 0, "Q3", "Sep", "7" },
3994 : { "2012", "Q1", "Jan", "3" },
3995 : { 0, 0, "Feb", "9" },
3996 : { 0, 0, "Mar", "6" },
3997 : { 0, "Q4", "Dec", "8" },
3998 : { "Total Result", 0, 0, "36" },
3999 1 : };
4000 :
4001 1 : bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Years, quarters and months date groups.");
4002 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4003 : }
4004 :
4005 : {
4006 : // Let's hide year 2012.
4007 1 : pSaveData = pDPObj->GetSaveData();
4008 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(OUString("Years"));
4009 1 : CPPUNIT_ASSERT_MESSAGE("Years dimension should exist.", pDim);
4010 1 : ScDPSaveMember* pMem = pDim->GetMemberByName(OUString("2012"));
4011 1 : CPPUNIT_ASSERT_MESSAGE("Member should exist.", pMem);
4012 1 : pMem->SetIsVisible(false);
4013 : }
4014 1 : pDPObj->SetSaveData(*pSaveData);
4015 1 : pDPObj->ReloadGroupTableData();
4016 1 : pDPObj->InvalidateData();
4017 :
4018 1 : aOutRange = refresh(pDPObj);
4019 : {
4020 : // Expected output table content. 0 = empty cell
4021 : const char* aOutputCheck[][4] = {
4022 : { "Years", "Quarters", "Date", 0 },
4023 : { "2011", "Q1", "Jan", "1" },
4024 : { 0, 0, "Mar", "2" },
4025 : { 0, "Q3", "Sep", "7" },
4026 : { "Total Result", 0, 0, "10" },
4027 1 : };
4028 :
4029 1 : bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Year 2012 data now hidden");
4030 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4031 : }
4032 :
4033 : // Remove all date grouping. The source dimension "Date" has two
4034 : // external dimensions ("Years" and "Quarters") and one internal ("Date"
4035 : // the same name but different hierarchy). Remove all of them.
4036 1 : pSaveData = pDPObj->GetSaveData();
4037 1 : pSaveData->RemoveAllGroupDimensions(aBaseDimName);
4038 1 : pDPObj->SetSaveData(*pSaveData);
4039 1 : pDPObj->ReloadGroupTableData();
4040 1 : pDPObj->InvalidateData();
4041 :
4042 1 : aOutRange = refresh(pDPObj);
4043 : {
4044 : // Expected output table content. 0 = empty cell
4045 : const char* aOutputCheck[][2] = {
4046 : { "Date", 0 },
4047 : { "2011-01-01", "1" },
4048 : { "2011-03-02", "2" },
4049 : { "2011-09-03", "7" },
4050 : { "2012-01-04", "3" },
4051 : { "2012-02-23", "4" },
4052 : { "2012-02-24", "5" },
4053 : { "2012-03-15", "6" },
4054 : { "2012-12-25", "8" },
4055 : { "Total Result", "36" }
4056 1 : };
4057 :
4058 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Remove all date grouping.");
4059 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4060 : }
4061 :
4062 1 : pDPs->FreeTable(pDPObj);
4063 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4064 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4065 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4066 :
4067 1 : m_pDoc->DeleteTab(1);
4068 1 : m_pDoc->DeleteTab(0);
4069 1 : }
4070 :
4071 1 : void Test::testPivotTableEmptyRows()
4072 : {
4073 1 : m_pDoc->InsertTab(0, OUString("Data"));
4074 1 : m_pDoc->InsertTab(1, OUString("Table"));
4075 :
4076 : // Raw data
4077 : const char* aData[][2] = {
4078 : { "Name", "Value" },
4079 : { "A", "1" },
4080 : { "B", "2" },
4081 : { "C", "3" },
4082 : { "D", "4" },
4083 1 : };
4084 :
4085 : // Dimension definition
4086 : DPFieldDef aFields[] = {
4087 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4088 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4089 1 : };
4090 :
4091 1 : ScAddress aPos(1,1,0);
4092 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4093 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4094 :
4095 : // Extend the range downward to include some trailing empty rows.
4096 1 : aDataRange.aEnd.IncRow(2);
4097 :
4098 : ScDPObject* pDPObj = createDPFromRange(
4099 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4100 :
4101 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4102 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
4103 :
4104 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4105 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4106 1 : pDPs->GetCount() == 1);
4107 1 : pDPObj->SetName(pDPs->CreateNewName());
4108 :
4109 1 : ScRange aOutRange = refresh(pDPObj);
4110 :
4111 : {
4112 : // Expected output table content. 0 = empty cell
4113 : const char* aOutputCheck[][2] = {
4114 : { "Name", 0 },
4115 : { "A", "1" },
4116 : { "B", "2" },
4117 : { "C", "3" },
4118 : { "D", "4" },
4119 : { "(empty)", 0 },
4120 : { "Total Result", "10" },
4121 1 : };
4122 :
4123 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Include empty rows");
4124 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4125 : }
4126 :
4127 : // This time, ignore empty rows.
4128 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
4129 1 : CPPUNIT_ASSERT_MESSAGE("Save data doesn't exist.", pSaveData);
4130 1 : pSaveData->SetIgnoreEmptyRows(true);
4131 1 : pDPObj->ClearTableData();
4132 1 : aOutRange = refresh(pDPObj);
4133 :
4134 : {
4135 : // Expected output table content. 0 = empty cell
4136 : const char* aOutputCheck[][2] = {
4137 : { "Name", 0 },
4138 : { "A", "1" },
4139 : { "B", "2" },
4140 : { "C", "3" },
4141 : { "D", "4" },
4142 : { "Total Result", "10" },
4143 1 : };
4144 :
4145 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
4146 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4147 : }
4148 :
4149 : // Modify the source to remove member 'A', then refresh the table.
4150 1 : m_pDoc->SetString(1, 2, 0, "B");
4151 :
4152 1 : std::set<ScDPObject*> aRefs;
4153 1 : sal_uLong nErr = pDPs->ReloadCache(pDPObj, aRefs);
4154 1 : CPPUNIT_ASSERT_MESSAGE("Failed to reload cache.", !nErr);
4155 2 : CPPUNIT_ASSERT_MESSAGE("There should only be one pivot table linked to this cache.",
4156 1 : aRefs.size() == 1 && *aRefs.begin() == pDPObj);
4157 :
4158 1 : pDPObj->ClearTableData();
4159 1 : aOutRange = refresh(pDPObj);
4160 :
4161 : {
4162 : // Expected output table content. 0 = empty cell
4163 : const char* aOutputCheck[][2] = {
4164 : { "Name", 0 },
4165 : { "B", "3" },
4166 : { "C", "3" },
4167 : { "D", "4" },
4168 : { "Total Result", "10" },
4169 1 : };
4170 :
4171 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
4172 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4173 : }
4174 :
4175 1 : pDPs->FreeTable(pDPObj);
4176 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4177 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4178 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4179 :
4180 1 : m_pDoc->DeleteTab(1);
4181 1 : m_pDoc->DeleteTab(0);
4182 1 : }
4183 :
4184 1 : void Test::testPivotTableTextNumber()
4185 : {
4186 1 : m_pDoc->InsertTab(0, OUString("Data"));
4187 1 : m_pDoc->InsertTab(1, OUString("Table"));
4188 :
4189 : // Raw data
4190 : const char* aData[][2] = {
4191 : { "Name", "Value" },
4192 : { "0001", "1" },
4193 : { "0002", "2" },
4194 : { "0003", "3" },
4195 : { "0004", "4" },
4196 1 : };
4197 :
4198 : // Dimension definition
4199 : DPFieldDef aFields[] = {
4200 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4201 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4202 1 : };
4203 :
4204 : // Insert raw data such that the first column values are entered as text.
4205 1 : size_t nRowCount = SAL_N_ELEMENTS(aData);
4206 6 : for (size_t nRow = 0; nRow < nRowCount; ++nRow)
4207 : {
4208 5 : ScSetStringParam aParam;
4209 5 : aParam.mbDetectNumberFormat = false;
4210 5 : aParam.meSetTextNumFormat = ScSetStringParam::Always;
4211 5 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aData[nRow][0]), &aParam);
4212 5 : aParam.meSetTextNumFormat = ScSetStringParam::Never;
4213 5 : m_pDoc->SetString(1, nRow, 0, OUString::createFromAscii(aData[nRow][1]), &aParam);
4214 :
4215 5 : if (nRow == 0)
4216 : // Don't check the header row.
4217 1 : continue;
4218 :
4219 : // Check the data rows.
4220 4 : CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be text.", m_pDoc->HasStringData(0, nRow, 0));
4221 4 : CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be numeric.", m_pDoc->HasValueData(1, nRow, 0));
4222 : }
4223 :
4224 1 : ScRange aDataRange(0, 0, 0, 1, 4, 0);
4225 :
4226 : ScDPObject* pDPObj = createDPFromRange(
4227 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4228 :
4229 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4230 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
4231 :
4232 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4233 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4234 1 : pDPs->GetCount() == 1);
4235 1 : pDPObj->SetName(pDPs->CreateNewName());
4236 :
4237 1 : ScRange aOutRange = refresh(pDPObj);
4238 :
4239 : {
4240 : // Expected output table content. 0 = empty cell
4241 : const char* aOutputCheck[][2] = {
4242 : { "Name", 0 },
4243 : { "0001", "1" },
4244 : { "0002", "2" },
4245 : { "0003", "3" },
4246 : { "0004", "4" },
4247 : { "Total Result", "10" },
4248 1 : };
4249 :
4250 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Text number field members");
4251 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4252 : }
4253 :
4254 1 : pDPs->FreeTable(pDPObj);
4255 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4256 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4257 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4258 :
4259 1 : m_pDoc->DeleteTab(1);
4260 1 : m_pDoc->DeleteTab(0);
4261 1 : }
4262 :
4263 1 : void Test::testPivotTableCaseInsensitiveStrings()
4264 : {
4265 1 : m_pDoc->InsertTab(0, OUString("Data"));
4266 1 : m_pDoc->InsertTab(1, OUString("Table"));
4267 :
4268 : // Raw data
4269 : const char* aData[][2] = {
4270 : { "Name", "Value" },
4271 : { "A", "1" },
4272 : { "a", "2" },
4273 1 : };
4274 :
4275 : // Dimension definition
4276 : DPFieldDef aFields[] = {
4277 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4278 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4279 1 : };
4280 :
4281 1 : ScAddress aPos(1,1,0);
4282 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4283 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4284 :
4285 : ScDPObject* pDPObj = createDPFromRange(
4286 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4287 :
4288 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4289 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
4290 :
4291 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4292 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4293 1 : pDPs->GetCount() == 1);
4294 1 : pDPObj->SetName(pDPs->CreateNewName());
4295 :
4296 1 : ScRange aOutRange = refresh(pDPObj);
4297 :
4298 : {
4299 : // Expected output table content. 0 = empty cell
4300 : const char* aOutputCheck[][2] = {
4301 : { "Name", 0 },
4302 : { "A", "3" },
4303 : { "Total Result", "3" },
4304 1 : };
4305 :
4306 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Case insensitive strings");
4307 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4308 : }
4309 :
4310 1 : pDPs->FreeTable(pDPObj);
4311 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4312 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4313 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4314 :
4315 1 : m_pDoc->DeleteTab(1);
4316 1 : m_pDoc->DeleteTab(0);
4317 1 : }
4318 :
4319 1 : void Test::testPivotTableNumStability()
4320 : {
4321 1 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
4322 :
4323 : // Raw Data
4324 : const char* aData[][4] = {
4325 : { "Name", "Time Start", "Time End", "Total" },
4326 : { "Sam", "07:48 AM", "09:00 AM", "=RC[-1]-RC[-2]" },
4327 : { "Sam", "09:00 AM", "10:30 AM", "=RC[-1]-RC[-2]" },
4328 : { "Sam", "10:30 AM", "12:30 PM", "=RC[-1]-RC[-2]" },
4329 : { "Sam", "12:30 PM", "01:00 PM", "=RC[-1]-RC[-2]" },
4330 : { "Sam", "01:00 PM", "01:30 PM", "=RC[-1]-RC[-2]" },
4331 : { "Sam", "01:30 PM", "02:00 PM", "=RC[-1]-RC[-2]" },
4332 : { "Sam", "02:00 PM", "07:15 PM", "=RC[-1]-RC[-2]" },
4333 : { "Sam", "07:47 AM", "09:00 AM", "=RC[-1]-RC[-2]" },
4334 : { "Sam", "09:00 AM", "10:00 AM", "=RC[-1]-RC[-2]" },
4335 : { "Sam", "10:00 AM", "11:00 AM", "=RC[-1]-RC[-2]" },
4336 : { "Sam", "11:00 AM", "11:30 AM", "=RC[-1]-RC[-2]" },
4337 : { "Sam", "11:30 AM", "12:45 PM", "=RC[-1]-RC[-2]" },
4338 : { "Sam", "12:45 PM", "01:15 PM", "=RC[-1]-RC[-2]" },
4339 : { "Sam", "01:15 PM", "02:30 PM", "=RC[-1]-RC[-2]" },
4340 : { "Sam", "02:30 PM", "02:45 PM", "=RC[-1]-RC[-2]" },
4341 : { "Sam", "02:45 PM", "04:30 PM", "=RC[-1]-RC[-2]" },
4342 : { "Sam", "04:30 PM", "06:00 PM", "=RC[-1]-RC[-2]" },
4343 : { "Sam", "06:00 PM", "07:15 PM", "=RC[-1]-RC[-2]" },
4344 : { "Mike", "06:15 AM", "08:30 AM", "=RC[-1]-RC[-2]" },
4345 : { "Mike", "08:30 AM", "10:03 AM", "=RC[-1]-RC[-2]" },
4346 : { "Mike", "10:03 AM", "12:00 PM", "=RC[-1]-RC[-2]" },
4347 : { "Dennis", "11:00 AM", "01:00 PM", "=RC[-1]-RC[-2]" },
4348 : { "Dennis", "01:00 PM", "02:00 PM", "=RC[-1]-RC[-2]" }
4349 1 : };
4350 :
4351 : // Dimension definition
4352 : DPFieldDef aFields[] = {
4353 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4354 : { "Total", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4355 1 : };
4356 :
4357 1 : m_pDoc->InsertTab(0, OUString("Data"));
4358 1 : m_pDoc->InsertTab(1, OUString("Table"));
4359 :
4360 1 : size_t nRowCount = SAL_N_ELEMENTS(aData);
4361 1 : ScAddress aPos(1,1,0);
4362 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, nRowCount);
4363 :
4364 : // Insert formulas to manually calculate sums for each name.
4365 1 : 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])");
4366 1 : 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])");
4367 1 : 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])");
4368 :
4369 1 : m_pDoc->CalcAll();
4370 :
4371 : // Get correct sum values.
4372 1 : double fDennisTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+1, aDataRange.aStart.Tab());
4373 1 : double fMikeTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+2, aDataRange.aStart.Tab());
4374 1 : double fSamTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+3, aDataRange.aStart.Tab());
4375 :
4376 : ScDPObject* pDPObj = createDPFromRange(
4377 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4378 :
4379 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4380 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
4381 :
4382 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4383 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
4384 1 : pDPs->GetCount(), static_cast<size_t>(1));
4385 1 : pDPObj->SetName(pDPs->CreateNewName());
4386 :
4387 1 : ScRange aOutRange = refresh(pDPObj);
4388 :
4389 : // Manually check the total value for each name.
4390 : //
4391 : // +--------------+----------------+
4392 : // | Name | |
4393 : // +--------------+----------------+
4394 : // | Dennis | <Dennis total> |
4395 : // +--------------+----------------+
4396 : // | Mike | <Miks total> |
4397 : // +--------------+----------------+
4398 : // | Sam | <Sam total> |
4399 : // +--------------+----------------+
4400 : // | Total Result | ... |
4401 : // +--------------+----------------+
4402 :
4403 1 : aPos = aOutRange.aStart;
4404 1 : aPos.IncCol();
4405 1 : aPos.IncRow();
4406 1 : double fTest = m_pDoc->GetValue(aPos);
4407 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect value for Dennis.", rtl::math::approxEqual(fTest, fDennisTotal));
4408 1 : aPos.IncRow();
4409 1 : fTest = m_pDoc->GetValue(aPos);
4410 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect value for Mike.", rtl::math::approxEqual(fTest, fMikeTotal));
4411 1 : aPos.IncRow();
4412 1 : fTest = m_pDoc->GetValue(aPos);
4413 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect value for Sam.", rtl::math::approxEqual(fTest, fSamTotal));
4414 :
4415 1 : pDPs->FreeTable(pDPObj);
4416 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4417 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4418 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4419 :
4420 1 : m_pDoc->DeleteTab(1);
4421 1 : m_pDoc->DeleteTab(0);
4422 1 : }
4423 :
4424 1 : void Test::testPivotTableFieldReference()
4425 : {
4426 1 : m_pDoc->InsertTab(0, OUString("Data"));
4427 1 : m_pDoc->InsertTab(1, OUString("Table"));
4428 :
4429 : // Raw data
4430 : const char* aData[][2] = {
4431 : { "Name", "Value" },
4432 : { "A", "1" },
4433 : { "B", "2" },
4434 : { "C", "4" },
4435 : { "D", "8" },
4436 1 : };
4437 :
4438 : // Dimension definition
4439 : DPFieldDef aFields[] = {
4440 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4441 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4442 1 : };
4443 :
4444 1 : ScAddress aPos(1,1,0);
4445 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4446 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4447 :
4448 : ScDPObject* pDPObj = createDPFromRange(
4449 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4450 :
4451 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4452 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
4453 :
4454 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
4455 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
4456 1 : pDPs->GetCount() == 1);
4457 1 : pDPObj->SetName(pDPs->CreateNewName());
4458 :
4459 1 : ScRange aOutRange = refresh(pDPObj);
4460 :
4461 : {
4462 : // Expected output table content. 0 = empty cell
4463 : const char* aOutputCheck[][2] = {
4464 : { "Name", 0 },
4465 : { "A", "1" },
4466 : { "B", "2" },
4467 : { "C", "4" },
4468 : { "D", "8" },
4469 : { "Total Result", "15" },
4470 1 : };
4471 :
4472 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (none)");
4473 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4474 : }
4475 :
4476 1 : ScDPSaveData aSaveData = *pDPObj->GetSaveData();
4477 2 : sheet::DataPilotFieldReference aFieldRef;
4478 1 : aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE;
4479 1 : aFieldRef.ReferenceField = "Name";
4480 1 : aFieldRef.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NAMED;
4481 1 : aFieldRef.ReferenceItemName = "A";
4482 1 : ScDPSaveDimension* pDim = aSaveData.GetDimensionByName("Value");
4483 1 : CPPUNIT_ASSERT_MESSAGE("Failed to retrieve dimension 'Value'.", pDim);
4484 1 : pDim->SetReferenceValue(&aFieldRef);
4485 1 : pDPObj->SetSaveData(aSaveData);
4486 :
4487 1 : aOutRange = refresh(pDPObj);
4488 : {
4489 : // Expected output table content. 0 = empty cell
4490 : const char* aOutputCheck[][2] = {
4491 : { "Name", 0 },
4492 : { "A", 0 },
4493 : { "B", "1" },
4494 : { "C", "3" },
4495 : { "D", "7" },
4496 : { "Total Result", 0 },
4497 1 : };
4498 :
4499 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (difference from)");
4500 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4501 : }
4502 :
4503 1 : aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE;
4504 1 : pDim->SetReferenceValue(&aFieldRef);
4505 1 : pDPObj->SetSaveData(aSaveData);
4506 :
4507 1 : aOutRange = refresh(pDPObj);
4508 : {
4509 : // Expected output table content. 0 = empty cell
4510 : const char* aOutputCheck[][2] = {
4511 : { "Name", 0 },
4512 : { "A", "100.00%" },
4513 : { "B", "200.00%" },
4514 : { "C", "400.00%" },
4515 : { "D", "800.00%" },
4516 : { "Total Result", 0 },
4517 1 : };
4518 :
4519 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% of)");
4520 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4521 : }
4522 :
4523 1 : aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
4524 1 : pDim->SetReferenceValue(&aFieldRef);
4525 1 : pDPObj->SetSaveData(aSaveData);
4526 :
4527 1 : aOutRange = refresh(pDPObj);
4528 : {
4529 : // Expected output table content. 0 = empty cell
4530 : const char* aOutputCheck[][2] = {
4531 : { "Name", 0 },
4532 : { "A", 0 },
4533 : { "B", "100.00%" },
4534 : { "C", "300.00%" },
4535 : { "D", "700.00%" },
4536 : { "Total Result", 0 },
4537 1 : };
4538 :
4539 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% difference from)");
4540 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4541 : }
4542 :
4543 1 : aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::RUNNING_TOTAL;
4544 1 : pDim->SetReferenceValue(&aFieldRef);
4545 1 : pDPObj->SetSaveData(aSaveData);
4546 :
4547 1 : aOutRange = refresh(pDPObj);
4548 : {
4549 : // Expected output table content. 0 = empty cell
4550 : const char* aOutputCheck[][2] = {
4551 : { "Name", 0 },
4552 : { "A", "1" },
4553 : { "B", "3" },
4554 : { "C", "7" },
4555 : { "D", "15" },
4556 : { "Total Result", 0 },
4557 1 : };
4558 :
4559 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (Running total)");
4560 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4561 : }
4562 :
4563 1 : aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
4564 1 : pDim->SetReferenceValue(&aFieldRef);
4565 1 : pDPObj->SetSaveData(aSaveData);
4566 :
4567 1 : aOutRange = refresh(pDPObj);
4568 : {
4569 : // Expected output table content. 0 = empty cell
4570 : const char* aOutputCheck[][2] = {
4571 : { "Name", 0 },
4572 : { "A", "6.67%" },
4573 : { "B", "13.33%" },
4574 : { "C", "26.67%" },
4575 : { "D", "53.33%" },
4576 : { "Total Result", "100.00%" },
4577 1 : };
4578 :
4579 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% of column)");
4580 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4581 : }
4582 :
4583 1 : pDPs->FreeTable(pDPObj);
4584 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
4585 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
4586 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
4587 :
4588 1 : m_pDoc->DeleteTab(1);
4589 2 : m_pDoc->DeleteTab(0);
4590 1 : }
4591 :
4592 1 : void Test::testPivotTableDocFunc()
4593 : {
4594 1 : m_pDoc->InsertTab(0, "Data");
4595 1 : m_pDoc->InsertTab(1, "Table");
4596 :
4597 : // Raw data
4598 : const char* aData[][2] = {
4599 : { "Name", "Value" },
4600 : { "Sun", "1" },
4601 : { "Oracle", "2" },
4602 : { "Red Hat", "4" },
4603 : { "SUSE", "8" },
4604 : { "Apple", "16" },
4605 : { "Microsoft", "32" },
4606 1 : };
4607 :
4608 : // Dimension definition
4609 : DPFieldDef aFields[] = {
4610 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
4611 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
4612 1 : };
4613 :
4614 1 : ScAddress aPos(1,1,0);
4615 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
4616 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
4617 :
4618 : ScDPObject* pDPObj = createDPFromRange(
4619 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
4620 :
4621 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create pivot table object.", pDPObj);
4622 :
4623 : // Craete a new pivot table output.
4624 1 : ScDBDocFunc aFunc(*m_xDocShRef);
4625 1 : bool bSuccess = aFunc.CreatePivotTable(*pDPObj, false, true);
4626 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create pivot table output via ScDBDocFunc.", bSuccess);
4627 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
4628 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get pivot table collection.", pDPs);
4629 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDPs->GetCount());
4630 1 : pDPObj = (*pDPs)[0];
4631 1 : CPPUNIT_ASSERT_MESSAGE("Failed to retrieve pivot table object from the collection", pDPObj);
4632 1 : ScRange aOutRange = pDPObj->GetOutRange();
4633 : {
4634 : // Expected output table content. 0 = empty cell
4635 : const char* aOutputCheck[][2] = {
4636 : { "Name", 0 },
4637 : { "Apple", "16" },
4638 : { "Microsoft", "32" },
4639 : { "Oracle", "2" },
4640 : { "Red Hat", "4" },
4641 : { "Sun", "1" },
4642 : { "SUSE", "8" },
4643 : { "Total Result", "63" },
4644 1 : };
4645 :
4646 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Pivot table created via ScDBDocFunc");
4647 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
4648 : }
4649 :
4650 : // Remove this pivot table output. This should also clear the pivot cache
4651 : // it was referencing.
4652 1 : bSuccess = aFunc.RemovePivotTable(*pDPObj, false, true);
4653 1 : CPPUNIT_ASSERT_MESSAGE("Failed to remove pivot table output via ScDBDocFunc.", bSuccess);
4654 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pDPs->GetCount());
4655 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pDPs->GetSheetCaches().size());
4656 :
4657 1 : m_pDoc->DeleteTab(1);
4658 1 : m_pDoc->DeleteTab(0);
4659 1 : }
4660 :
4661 1 : void Test::testSheetCopy()
4662 : {
4663 1 : m_pDoc->InsertTab(0, "TestTab");
4664 1 : m_pDoc->SetString(ScAddress(0,0,0), "copy me");
4665 1 : CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
4666 : SCROW nRow1, nRow2;
4667 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4668 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4669 :
4670 : // Copy and test the result.
4671 1 : m_pDoc->CopyTab(0, 1);
4672 1 : CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
4673 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4674 1 : CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4675 1 : m_pDoc->DeleteTab(1);
4676 :
4677 1 : m_pDoc->SetRowHidden(5, 10, 0, true);
4678 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4679 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4680 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
4681 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4682 1 : bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
4683 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4684 :
4685 : // Copy the sheet once again.
4686 1 : m_pDoc->CopyTab(0, 1);
4687 1 : CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
4688 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4689 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4690 1 : bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
4691 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4692 1 : bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
4693 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4694 1 : m_pDoc->DeleteTab(1);
4695 1 : m_pDoc->DeleteTab(0);
4696 1 : }
4697 :
4698 1 : void Test::testSheetMove()
4699 : {
4700 1 : OUString aTabName("TestTab1");
4701 1 : m_pDoc->InsertTab(0, aTabName);
4702 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount(), static_cast<SCTAB>(1));
4703 : SCROW nRow1, nRow2;
4704 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4705 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4706 :
4707 : //test if inserting before another sheet works
4708 1 : m_pDoc->InsertTab(0, OUString("TestTab2"));
4709 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
4710 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4711 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4712 :
4713 : // Move and test the result.
4714 1 : m_pDoc->MoveTab(0, 1);
4715 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
4716 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4717 1 : CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4718 2 : OUString aName;
4719 1 : m_pDoc->GetName(0, aName);
4720 1 : CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab1" );
4721 :
4722 1 : m_pDoc->SetRowHidden(5, 10, 0, true);
4723 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4724 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4725 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
4726 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4727 1 : bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
4728 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4729 :
4730 : // Move the sheet once again.
4731 1 : m_pDoc->MoveTab(1, 0);
4732 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
4733 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
4734 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
4735 1 : bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
4736 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
4737 1 : bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
4738 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
4739 1 : m_pDoc->GetName(0, aName);
4740 1 : CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab2" );
4741 1 : m_pDoc->DeleteTab(1);
4742 2 : m_pDoc->DeleteTab(0);
4743 1 : }
4744 :
4745 5 : ScDocShell* findLoadedDocShellByName(const OUString& rName)
4746 : {
4747 5 : TypeId aType(TYPE(ScDocShell));
4748 5 : ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
4749 263 : while (pShell)
4750 : {
4751 256 : SfxMedium* pMedium = pShell->GetMedium();
4752 256 : if (pMedium)
4753 : {
4754 256 : OUString aName = pMedium->GetName();
4755 256 : if (aName.equals(rName))
4756 3 : return pShell;
4757 : }
4758 253 : pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
4759 : }
4760 2 : return NULL;
4761 : }
4762 :
4763 2 : ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
4764 : {
4765 2 : ScRange aRange;
4766 :
4767 2 : vector<SCROW> aRows;
4768 2 : pCacheTab->getAllRows(aRows);
4769 2 : vector<SCROW>::const_iterator itrRow = aRows.begin(), itrRowEnd = aRows.end();
4770 2 : bool bFirst = true;
4771 11 : for (; itrRow != itrRowEnd; ++itrRow)
4772 : {
4773 9 : SCROW nRow = *itrRow;
4774 9 : vector<SCCOL> aCols;
4775 9 : pCacheTab->getAllCols(nRow, aCols);
4776 9 : vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end();
4777 27 : for (; itrCol != itrColEnd; ++itrCol)
4778 : {
4779 18 : SCCOL nCol = *itrCol;
4780 18 : if (bFirst)
4781 : {
4782 2 : aRange.aStart = ScAddress(nCol, nRow, 0);
4783 2 : aRange.aEnd = aRange.aStart;
4784 2 : bFirst = false;
4785 : }
4786 : else
4787 : {
4788 16 : if (nCol < aRange.aStart.Col())
4789 0 : aRange.aStart.SetCol(nCol);
4790 16 : else if (aRange.aEnd.Col() < nCol)
4791 2 : aRange.aEnd.SetCol(nCol);
4792 :
4793 16 : if (nRow < aRange.aStart.Row())
4794 0 : aRange.aStart.SetRow(nRow);
4795 16 : else if (aRange.aEnd.Row() < nRow)
4796 7 : aRange.aEnd.SetRow(nRow);
4797 : }
4798 : }
4799 9 : }
4800 2 : return aRange;
4801 : }
4802 :
4803 1 : void Test::testExternalRef()
4804 : {
4805 1 : ScDocShellRef xExtDocSh = new ScDocShell;
4806 2 : OUString aExtDocName("file:///extdata.fake");
4807 2 : OUString aExtSh1Name("Data1");
4808 2 : OUString aExtSh2Name("Data2");
4809 2 : OUString aExtSh3Name("Data3");
4810 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
4811 1 : xExtDocSh->DoInitNew(pMed);
4812 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
4813 1 : findLoadedDocShellByName(aExtDocName) != NULL);
4814 :
4815 : // Populate the external source document.
4816 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
4817 1 : pExtDoc->InsertTab(0, aExtSh1Name);
4818 1 : pExtDoc->InsertTab(1, aExtSh2Name);
4819 1 : pExtDoc->InsertTab(2, aExtSh3Name);
4820 :
4821 2 : OUString name("Name");
4822 2 : OUString value("Value");
4823 2 : OUString andy("Andy");
4824 2 : OUString bruce("Bruce");
4825 2 : OUString charlie("Charlie");
4826 2 : OUString david("David");
4827 2 : OUString edward("Edward");
4828 2 : OUString frank("Frank");
4829 2 : OUString george("George");
4830 2 : OUString henry("Henry");
4831 :
4832 : // Sheet 1
4833 1 : pExtDoc->SetString(0, 0, 0, name);
4834 1 : pExtDoc->SetString(0, 1, 0, andy);
4835 1 : pExtDoc->SetString(0, 2, 0, bruce);
4836 1 : pExtDoc->SetString(0, 3, 0, charlie);
4837 1 : pExtDoc->SetString(0, 4, 0, david);
4838 1 : pExtDoc->SetString(1, 0, 0, value);
4839 1 : double val = 10;
4840 1 : pExtDoc->SetValue(1, 1, 0, val);
4841 1 : val = 11;
4842 1 : pExtDoc->SetValue(1, 2, 0, val);
4843 1 : val = 12;
4844 1 : pExtDoc->SetValue(1, 3, 0, val);
4845 1 : val = 13;
4846 1 : pExtDoc->SetValue(1, 4, 0, val);
4847 :
4848 : // Sheet 2 remains empty.
4849 :
4850 : // Sheet 3
4851 1 : pExtDoc->SetString(0, 0, 2, name);
4852 1 : pExtDoc->SetString(0, 1, 2, edward);
4853 1 : pExtDoc->SetString(0, 2, 2, frank);
4854 1 : pExtDoc->SetString(0, 3, 2, george);
4855 1 : pExtDoc->SetString(0, 4, 2, henry);
4856 1 : pExtDoc->SetString(1, 0, 2, value);
4857 1 : val = 99;
4858 1 : pExtDoc->SetValue(1, 1, 2, val);
4859 1 : val = 98;
4860 1 : pExtDoc->SetValue(1, 2, 2, val);
4861 1 : val = 97;
4862 1 : pExtDoc->SetValue(1, 3, 2, val);
4863 1 : val = 96;
4864 1 : pExtDoc->SetValue(1, 4, 2, val);
4865 :
4866 : // Test external refernces on the main document while the external
4867 : // document is still in memory.
4868 1 : m_pDoc->InsertTab(0, OUString("Test Sheet"));
4869 1 : m_pDoc->SetString(0, 0, 0, OUString("='file:///extdata.fake'#Data1.A1"));
4870 2 : OUString test = m_pDoc->GetString(0, 0, 0);
4871 1 : CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(name));
4872 :
4873 : // After the initial access to the external document, the external ref
4874 : // manager should create sheet cache entries for *all* sheets from that
4875 : // document. Note that the doc may have more than 3 sheets but ensure
4876 : // that the first 3 are what we expect.
4877 1 : ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
4878 1 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
4879 2 : vector<OUString> aTabNames;
4880 1 : pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
4881 1 : CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
4882 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[0].equals(aExtSh1Name));
4883 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[1].equals(aExtSh2Name));
4884 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[2].equals(aExtSh3Name));
4885 :
4886 1 : m_pDoc->SetString(1, 0, 0, OUString("='file:///extdata.fake'#Data1.B1"));
4887 1 : test = m_pDoc->GetString(1, 0, 0);
4888 1 : CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(value));
4889 :
4890 1 : m_pDoc->SetString(0, 1, 0, OUString("='file:///extdata.fake'#Data1.A2"));
4891 1 : m_pDoc->SetString(0, 2, 0, OUString("='file:///extdata.fake'#Data1.A3"));
4892 1 : m_pDoc->SetString(0, 3, 0, OUString("='file:///extdata.fake'#Data1.A4"));
4893 1 : m_pDoc->SetString(0, 4, 0, OUString("='file:///extdata.fake'#Data1.A5"));
4894 1 : m_pDoc->SetString(0, 5, 0, OUString("='file:///extdata.fake'#Data1.A6"));
4895 :
4896 : {
4897 : // Referencing an empty cell should display '0'.
4898 1 : const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
4899 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4900 : {
4901 5 : test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
4902 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
4903 : }
4904 : }
4905 1 : m_pDoc->SetString(1, 1, 0, OUString("='file:///extdata.fake'#Data1.B2"));
4906 1 : m_pDoc->SetString(1, 2, 0, OUString("='file:///extdata.fake'#Data1.B3"));
4907 1 : m_pDoc->SetString(1, 3, 0, OUString("='file:///extdata.fake'#Data1.B4"));
4908 1 : m_pDoc->SetString(1, 4, 0, OUString("='file:///extdata.fake'#Data1.B5"));
4909 1 : m_pDoc->SetString(1, 5, 0, OUString("='file:///extdata.fake'#Data1.B6"));
4910 : {
4911 1 : double pChecks[] = { 10, 11, 12, 13, 0 };
4912 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4913 : {
4914 5 : m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
4915 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", val == pChecks[i]);
4916 : }
4917 : }
4918 :
4919 1 : m_pDoc->SetString(2, 0, 0, OUString("='file:///extdata.fake'#Data3.A1"));
4920 1 : m_pDoc->SetString(2, 1, 0, OUString("='file:///extdata.fake'#Data3.A2"));
4921 1 : m_pDoc->SetString(2, 2, 0, OUString("='file:///extdata.fake'#Data3.A3"));
4922 1 : m_pDoc->SetString(2, 3, 0, OUString("='file:///extdata.fake'#Data3.A4"));
4923 : {
4924 1 : const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
4925 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4926 : {
4927 4 : test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
4928 4 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
4929 : }
4930 : }
4931 :
4932 1 : m_pDoc->SetString(3, 0, 0, OUString("='file:///extdata.fake'#Data3.B1"));
4933 1 : m_pDoc->SetString(3, 1, 0, OUString("='file:///extdata.fake'#Data3.B2"));
4934 1 : m_pDoc->SetString(3, 2, 0, OUString("='file:///extdata.fake'#Data3.B3"));
4935 1 : m_pDoc->SetString(3, 3, 0, OUString("='file:///extdata.fake'#Data3.B4"));
4936 : {
4937 1 : const char* pChecks[] = { "Value", "99", "98", "97" };
4938 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
4939 : {
4940 4 : test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
4941 4 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
4942 : }
4943 : }
4944 :
4945 : // At this point, all accessed cell data from the external document should
4946 : // have been cached.
4947 : ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
4948 2 : nFileId, aExtSh1Name, false);
4949 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != NULL);
4950 1 : ScRange aCachedRange = getCachedRange(pCacheTab);
4951 2 : CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
4952 : aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
4953 1 : aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
4954 :
4955 : // Sheet2 is not referenced at all; the cache table shouldn't even exist.
4956 1 : pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
4957 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", pCacheTab.get() == NULL);
4958 :
4959 : // Sheet3's row 5 is not referenced; it should not be cached.
4960 1 : pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
4961 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != NULL);
4962 1 : aCachedRange = getCachedRange(pCacheTab);
4963 2 : CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
4964 : aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
4965 1 : aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
4966 :
4967 : // Unload the external document shell.
4968 1 : xExtDocSh->DoClose();
4969 2 : CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
4970 1 : findLoadedDocShellByName(aExtDocName) == NULL);
4971 :
4972 2 : m_pDoc->DeleteTab(0);
4973 1 : }
4974 :
4975 1 : void testExtRefFuncT(ScDocument* pDoc, ScDocument* pExtDoc)
4976 : {
4977 1 : clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
4978 1 : clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
4979 :
4980 1 : pExtDoc->SetString(0, 0, 0, OUString("'1.2"));
4981 1 : pExtDoc->SetString(0, 1, 0, OUString("Foo"));
4982 1 : pExtDoc->SetValue(0, 2, 0, 12.3);
4983 1 : pDoc->SetString(0, 0, 0, OUString("=T('file:///extdata.fake'#Data.A1)"));
4984 1 : pDoc->SetString(0, 1, 0, OUString("=T('file:///extdata.fake'#Data.A2)"));
4985 1 : pDoc->SetString(0, 2, 0, OUString("=T('file:///extdata.fake'#Data.A3)"));
4986 1 : pDoc->CalcAll();
4987 :
4988 1 : OUString aRes = pDoc->GetString(0, 0, 0);
4989 1 : CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "1.2" );
4990 1 : aRes = pDoc->GetString(0, 1, 0);
4991 1 : CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "Foo" );
4992 1 : aRes = pDoc->GetString(0, 2, 0);
4993 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
4994 1 : }
4995 :
4996 1 : void Test::testExternalRefFunctions()
4997 : {
4998 1 : ScDocShellRef xExtDocSh = new ScDocShell;
4999 2 : OUString aExtDocName("file:///extdata.fake");
5000 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
5001 1 : xExtDocSh->DoInitNew(pMed);
5002 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
5003 1 : findLoadedDocShellByName(aExtDocName) != NULL);
5004 :
5005 1 : ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
5006 1 : CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
5007 1 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
5008 1 : const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
5009 2 : CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
5010 1 : pFileName && pFileName->equals(aExtDocName));
5011 :
5012 : // Populate the external source document.
5013 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
5014 1 : pExtDoc->InsertTab(0, OUString("Data"));
5015 1 : double val = 1;
5016 1 : pExtDoc->SetValue(0, 0, 0, val);
5017 : // leave cell B1 empty.
5018 1 : val = 2;
5019 1 : pExtDoc->SetValue(0, 1, 0, val);
5020 1 : pExtDoc->SetValue(1, 1, 0, val);
5021 1 : val = 3;
5022 1 : pExtDoc->SetValue(0, 2, 0, val);
5023 1 : pExtDoc->SetValue(1, 2, 0, val);
5024 1 : val = 4;
5025 1 : pExtDoc->SetValue(0, 3, 0, val);
5026 1 : pExtDoc->SetValue(1, 3, 0, val);
5027 :
5028 1 : m_pDoc->InsertTab(0, OUString("Test"));
5029 :
5030 : struct {
5031 : const char* pFormula; double fResult;
5032 : } aChecks[] = {
5033 : { "=SUM('file:///extdata.fake'#Data.A1:A4)", 10 },
5034 : { "=SUM('file:///extdata.fake'#Data.B1:B4)", 9 },
5035 : { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
5036 : { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
5037 : { "=COUNT('file:///extdata.fake'#Data.A1:A4)", 4 },
5038 : { "=COUNT('file:///extdata.fake'#Data.B1:B4)", 3 }
5039 1 : };
5040 :
5041 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
5042 : {
5043 6 : m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
5044 6 : m_pDoc->CalcAll();
5045 6 : m_pDoc->GetValue(0, 0, 0, val);
5046 6 : CPPUNIT_ASSERT_MESSAGE("unexpected result involving external ranges.", val == aChecks[i].fResult);
5047 : }
5048 :
5049 1 : pRefMgr->clearCache(nFileId);
5050 1 : testExtRefFuncT(m_pDoc, pExtDoc);
5051 :
5052 : // Unload the external document shell.
5053 1 : xExtDocSh->DoClose();
5054 2 : CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
5055 1 : findLoadedDocShellByName(aExtDocName) == NULL);
5056 :
5057 2 : m_pDoc->DeleteTab(0);
5058 1 : }
5059 :
5060 1 : void Test::testDataArea()
5061 : {
5062 1 : m_pDoc->InsertTab(0, OUString("Data"));
5063 :
5064 : // Totally empty sheet should be rightfully considered empty in all accounts.
5065 1 : CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
5066 1 : CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
5067 :
5068 : // Now, set borders in some cells....
5069 1 : ::editeng::SvxBorderLine aLine(NULL, 50, table::BorderLineStyle::SOLID);
5070 1 : SvxBoxItem aBorderItem(ATTR_BORDER);
5071 1 : aBorderItem.SetLine(&aLine, BOX_LINE_LEFT);
5072 1 : aBorderItem.SetLine(&aLine, BOX_LINE_RIGHT);
5073 101 : for (SCROW i = 0; i < 100; ++i)
5074 : // Set borders from row 1 to 100.
5075 100 : m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
5076 :
5077 : // Now the sheet is considered non-empty for printing purposes, but still
5078 : // be empty in all the other cases.
5079 2 : CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
5080 1 : !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
5081 2 : CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
5082 1 : m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
5083 :
5084 : // Adding a real cell content should turn the block non-empty.
5085 1 : m_pDoc->SetString(0, 0, 0, OUString("Some text"));
5086 2 : CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
5087 1 : !m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
5088 :
5089 : // TODO: Add more tests for normal data area calculation.
5090 :
5091 1 : m_pDoc->DeleteTab(0);
5092 1 : }
5093 :
5094 1 : void Test::testStreamValid()
5095 : {
5096 1 : m_pDoc->InsertTab(0, OUString("Sheet1"));
5097 1 : m_pDoc->InsertTab(1, OUString("Sheet2"));
5098 1 : m_pDoc->InsertTab(2, OUString("Sheet3"));
5099 1 : m_pDoc->InsertTab(3, OUString("Sheet4"));
5100 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", m_pDoc->GetTableCount(), static_cast<SCTAB>(4));
5101 :
5102 1 : OUString a1("A1");
5103 2 : OUString a2("A2");
5104 2 : OUString test;
5105 :
5106 : // Put values into Sheet1.
5107 1 : m_pDoc->SetString(0, 0, 0, a1);
5108 1 : m_pDoc->SetString(0, 1, 0, a2);
5109 1 : test = m_pDoc->GetString(0, 0, 0);
5110 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A1", test.equals(a1));
5111 1 : test = m_pDoc->GetString(0, 1, 0);
5112 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A2", test.equals(a2));
5113 :
5114 : // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
5115 1 : m_pDoc->SetString(0, 0, 1, OUString("=Sheet1.A1"));
5116 1 : m_pDoc->SetString(0, 1, 1, OUString("=Sheet1.A2"));
5117 1 : m_pDoc->SetString(0, 0, 2, OUString("=Sheet1.A1"));
5118 1 : m_pDoc->SetString(0, 0, 3, OUString("=Sheet1.A2"));
5119 :
5120 1 : test = m_pDoc->GetString(0, 0, 1);
5121 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A1", test.equals(a1));
5122 1 : test = m_pDoc->GetString(0, 1, 1);
5123 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A2", test.equals(a2));
5124 1 : test = m_pDoc->GetString(0, 0, 2);
5125 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a1));
5126 1 : test = m_pDoc->GetString(0, 0, 3);
5127 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a2));
5128 :
5129 : // Set all sheet streams valid after all the initial cell values are in
5130 : // place. In reality we need to have real XML streams stored in order to
5131 : // claim they are valid, but we are just testing the flag values here.
5132 1 : m_pDoc->SetStreamValid(0, true);
5133 1 : m_pDoc->SetStreamValid(1, true);
5134 1 : m_pDoc->SetStreamValid(2, true);
5135 1 : m_pDoc->SetStreamValid(3, true);
5136 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
5137 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
5138 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
5139 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
5140 :
5141 : // Now, insert a new row at row 2 position on Sheet1. This will move cell
5142 : // A2 downward but cell A1 remains unmoved.
5143 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 2);
5144 1 : test = m_pDoc->GetString(0, 0, 0);
5145 1 : CPPUNIT_ASSERT_MESSAGE("Cell A1 should not have moved.", test.equals(a1));
5146 1 : test = m_pDoc->GetString(0, 3, 0);
5147 1 : CPPUNIT_ASSERT_MESSAGE("the old cell A2 should now be at A4.", test.equals(a2));
5148 2 : ScRefCellValue aCell;
5149 1 : aCell.assign(*m_pDoc, ScAddress(0,1,0));
5150 1 : CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
5151 1 : aCell.assign(*m_pDoc, ScAddress(0,2,0));
5152 1 : CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
5153 :
5154 : // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
5155 : // invalidated, whereas Sheet3's stream should still be valid.
5156 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
5157 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
5158 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
5159 1 : CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
5160 :
5161 1 : m_pDoc->DeleteTab(3);
5162 1 : m_pDoc->DeleteTab(2);
5163 1 : m_pDoc->DeleteTab(1);
5164 2 : m_pDoc->DeleteTab(0);
5165 1 : }
5166 :
5167 1 : void Test::testFunctionLists()
5168 : {
5169 : const char* aDataBase[] = {
5170 : "DAVERAGE",
5171 : "DCOUNT",
5172 : "DCOUNTA",
5173 : "DGET",
5174 : "DMAX",
5175 : "DMIN",
5176 : "DPRODUCT",
5177 : "DSTDEV",
5178 : "DSTDEVP",
5179 : "DSUM",
5180 : "DVAR",
5181 : "DVARP",
5182 : 0
5183 1 : };
5184 :
5185 : const char* aDateTime[] = {
5186 : "DATE",
5187 : "DATEDIF",
5188 : "DATEVALUE",
5189 : "DAY",
5190 : "DAYS",
5191 : "DAYS360",
5192 : "EASTERSUNDAY",
5193 : "HOUR",
5194 : "MINUTE",
5195 : "MONTH",
5196 : "NOW",
5197 : "SECOND",
5198 : "TIME",
5199 : "TIMEVALUE",
5200 : "TODAY",
5201 : "WEEKDAY",
5202 : "WEEKNUM",
5203 : "YEAR",
5204 : 0
5205 1 : };
5206 :
5207 : const char* aFinancial[] = {
5208 : "CUMIPMT",
5209 : "CUMPRINC",
5210 : "DB",
5211 : "DDB",
5212 : "DURATION",
5213 : "EFFECTIVE",
5214 : "FV",
5215 : "IPMT",
5216 : "IRR",
5217 : "ISPMT",
5218 : "MIRR",
5219 : "NOMINAL",
5220 : "NPER",
5221 : "NPV",
5222 : "PMT",
5223 : "PPMT",
5224 : "PV",
5225 : "RATE",
5226 : "RRI",
5227 : "SLN",
5228 : "SYD",
5229 : "VDB",
5230 : 0
5231 1 : };
5232 :
5233 : const char* aInformation[] = {
5234 : "CELL",
5235 : "CURRENT",
5236 : "FORMULA",
5237 : "INFO",
5238 : "ISBLANK",
5239 : "ISERR",
5240 : "ISERROR",
5241 : "ISFORMULA",
5242 : "ISLOGICAL",
5243 : "ISNA",
5244 : "ISNONTEXT",
5245 : "ISNUMBER",
5246 : "ISREF",
5247 : "ISTEXT",
5248 : "N",
5249 : "NA",
5250 : "TYPE",
5251 : 0
5252 1 : };
5253 :
5254 : const char* aLogical[] = {
5255 : "AND",
5256 : "FALSE",
5257 : "IF",
5258 : "IFERROR",
5259 : "IFNA",
5260 : "NOT",
5261 : "OR",
5262 : "TRUE",
5263 : "XOR",
5264 : 0
5265 1 : };
5266 :
5267 : const char* aMathematical[] = {
5268 : "ABS",
5269 : "ACOS",
5270 : "ACOSH",
5271 : "ACOT",
5272 : "ACOTH",
5273 : "ASIN",
5274 : "ASINH",
5275 : "ATAN",
5276 : "ATAN2",
5277 : "ATANH",
5278 : "AVERAGEIF",
5279 : "AVERAGEIFS",
5280 : "BITAND",
5281 : "BITLSHIFT",
5282 : "BITOR",
5283 : "BITRSHIFT",
5284 : "BITXOR",
5285 : "CEILING",
5286 : "COMBIN",
5287 : "COMBINA",
5288 : "CONVERT",
5289 : "COS",
5290 : "COSH",
5291 : "COT",
5292 : "COTH",
5293 : "COUNTBLANK",
5294 : "COUNTIF",
5295 : "COUNTIFS",
5296 : "CSC",
5297 : "CSCH",
5298 : "DEGREES",
5299 : "EUROCONVERT",
5300 : "EVEN",
5301 : "EXP",
5302 : "FACT",
5303 : "FLOOR",
5304 : "GCD",
5305 : "INT",
5306 : "ISEVEN",
5307 : "ISODD",
5308 : "LCM",
5309 : "LN",
5310 : "LOG",
5311 : "LOG10",
5312 : "MOD",
5313 : "ODD",
5314 : "PI",
5315 : "POWER",
5316 : "PRODUCT",
5317 : "RADIANS",
5318 : "RAND",
5319 : "ROUND",
5320 : "ROUNDDOWN",
5321 : "ROUNDUP",
5322 : "SEC",
5323 : "SECH",
5324 : "SIGN",
5325 : "SIN",
5326 : "SINH",
5327 : "SQRT",
5328 : "SUBTOTAL",
5329 : "SUM",
5330 : "SUMIF",
5331 : "SUMIFS",
5332 : "SUMSQ",
5333 : "TAN",
5334 : "TANH",
5335 : "TRUNC",
5336 : 0
5337 1 : };
5338 :
5339 : const char* aArray[] = {
5340 : "FREQUENCY",
5341 : "GROWTH",
5342 : "LINEST",
5343 : "LOGEST",
5344 : "MDETERM",
5345 : "MINVERSE",
5346 : "MMULT",
5347 : "MUNIT",
5348 : "SUMPRODUCT",
5349 : "SUMX2MY2",
5350 : "SUMX2PY2",
5351 : "SUMXMY2",
5352 : "TRANSPOSE",
5353 : "TREND",
5354 : 0
5355 1 : };
5356 :
5357 : const char* aStatistical[] = {
5358 : "AVEDEV",
5359 : "AVERAGE",
5360 : "AVERAGEA",
5361 : "B",
5362 : "BETADIST",
5363 : "BETAINV",
5364 : "BINOMDIST",
5365 : "CHIDIST",
5366 : "CHIINV",
5367 : "CHISQDIST",
5368 : "CHISQINV",
5369 : "CHITEST",
5370 : "CONFIDENCE",
5371 : "CORREL",
5372 : "COUNT",
5373 : "COUNTA",
5374 : "COVAR",
5375 : "CRITBINOM",
5376 : "DEVSQ",
5377 : "EXPONDIST",
5378 : "FDIST",
5379 : "FINV",
5380 : "FISHER",
5381 : "FISHERINV",
5382 : "FORECAST",
5383 : "FTEST",
5384 : "GAMMA",
5385 : "GAMMADIST",
5386 : "GAMMAINV",
5387 : "GAMMALN",
5388 : "GAUSS",
5389 : "GEOMEAN",
5390 : "HARMEAN",
5391 : "HYPGEOMDIST",
5392 : "INTERCEPT",
5393 : "KURT",
5394 : "LARGE",
5395 : "LOGINV",
5396 : "LOGNORMDIST",
5397 : "MAX",
5398 : "MAXA",
5399 : "MEDIAN",
5400 : "MIN",
5401 : "MINA",
5402 : "MODE",
5403 : "NEGBINOMDIST",
5404 : "NORMDIST",
5405 : "NORMINV",
5406 : "NORMSDIST",
5407 : "NORMSINV",
5408 : "PEARSON",
5409 : "PERCENTILE",
5410 : "PERCENTRANK",
5411 : "PERMUT",
5412 : "PERMUTATIONA",
5413 : "PHI",
5414 : "POISSON",
5415 : "PROB",
5416 : "QUARTILE",
5417 : "RANK",
5418 : "RSQ",
5419 : "SKEW",
5420 : "SKEWP",
5421 : "SLOPE",
5422 : "SMALL",
5423 : "STANDARDIZE",
5424 : "STDEV",
5425 : "STDEVA",
5426 : "STDEVP",
5427 : "STDEVPA",
5428 : "STEYX",
5429 : "TDIST",
5430 : "TINV",
5431 : "TRIMMEAN",
5432 : "TTEST",
5433 : "VAR",
5434 : "VARA",
5435 : "VARP",
5436 : "VARPA",
5437 : "WEIBULL",
5438 : "ZTEST",
5439 : 0
5440 1 : };
5441 :
5442 : const char* aSpreadsheet[] = {
5443 : "ADDRESS",
5444 : "AREAS",
5445 : "CHOOSE",
5446 : "COLUMN",
5447 : "COLUMNS",
5448 : "DDE",
5449 : "ERRORTYPE",
5450 : "GETPIVOTDATA",
5451 : "HLOOKUP",
5452 : "HYPERLINK",
5453 : "INDEX",
5454 : "INDIRECT",
5455 : "LOOKUP",
5456 : "MATCH",
5457 : "OFFSET",
5458 : "ROW",
5459 : "ROWS",
5460 : "SHEET",
5461 : "SHEETS",
5462 : "STYLE",
5463 : "VLOOKUP",
5464 : 0
5465 1 : };
5466 :
5467 : const char* aText[] = {
5468 : "ARABIC",
5469 : "ASC",
5470 : "BAHTTEXT",
5471 : "BASE",
5472 : "CHAR",
5473 : "CLEAN",
5474 : "CODE",
5475 : "CONCATENATE",
5476 : "DECIMAL",
5477 : "DOLLAR",
5478 : "EXACT",
5479 : "FILTERXML",
5480 : "FIND",
5481 : "FIXED",
5482 : "JIS",
5483 : "LEFT",
5484 : "LEFTB",
5485 : "LEN",
5486 : "LENB",
5487 : "LOWER",
5488 : "MID",
5489 : "MIDB",
5490 : "NUMBERVALUE",
5491 : "PROPER",
5492 : "REPLACE",
5493 : "REPT",
5494 : "RIGHT",
5495 : "RIGHTB",
5496 : "ROMAN",
5497 : "SEARCH",
5498 : "SUBSTITUTE",
5499 : "T",
5500 : "TEXT",
5501 : "TRIM",
5502 : "UNICHAR",
5503 : "UNICODE",
5504 : "UPPER",
5505 : "VALUE",
5506 : "WEBSERVICE",
5507 : 0
5508 1 : };
5509 :
5510 : struct {
5511 : const char* Category; const char** Functions;
5512 : } aTests[] = {
5513 : { "Database", aDataBase },
5514 : { "Date&Time", aDateTime },
5515 : { "Financial", aFinancial },
5516 : { "Information", aInformation },
5517 : { "Logical", aLogical },
5518 : { "Mathematical", aMathematical },
5519 : { "Array", aArray },
5520 : { "Statistical", aStatistical },
5521 : { "Spreadsheet", aSpreadsheet },
5522 : { "Text", aText },
5523 : { "Add-in", 0 },
5524 : { 0, 0 }
5525 1 : };
5526 :
5527 1 : ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
5528 1 : sal_uInt32 n = pFuncMgr->getCount();
5529 12 : for (sal_uInt32 i = 0; i < n; ++i)
5530 : {
5531 11 : const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
5532 11 : CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
5533 11 : sal_uInt32 nFuncCount = pCat->getCount();
5534 312 : for (sal_uInt32 j = 0; j < nFuncCount; ++j)
5535 : {
5536 301 : const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
5537 301 : CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", pFunc->getFunctionName(), OUString::createFromAscii(aTests[i].Functions[j]));
5538 : }
5539 : }
5540 1 : }
5541 :
5542 1 : void Test::testGraphicsInGroup()
5543 : {
5544 1 : OUString aTabName("TestTab");
5545 1 : m_pDoc->InsertTab(0, aTabName);
5546 1 : CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
5547 : SCROW nRow1, nRow2;
5548 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5549 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
5550 :
5551 1 : m_pDoc->InitDrawLayer();
5552 1 : ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
5553 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
5554 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
5555 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
5556 :
5557 : {
5558 : //Add a square
5559 1 : Rectangle aOrigRect(2,2,100,100);
5560 1 : SdrRectObj *pObj = new SdrRectObj(aOrigRect);
5561 1 : pPage->InsertObject(pObj);
5562 1 : const Rectangle &rNewRect = pObj->GetLogicRect();
5563 1 : CPPUNIT_ASSERT_MESSAGE("must have equal position and size", aOrigRect == rNewRect);
5564 :
5565 1 : ScDrawLayer::SetPageAnchored(*pObj);
5566 :
5567 : //Use a range of rows guaranteed to include all of the square
5568 1 : m_pDoc->ShowRows(0, 100, 0, false);
5569 1 : m_pDoc->SetDrawPageSize(0);
5570 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
5571 1 : m_pDoc->ShowRows(0, 100, 0, true);
5572 1 : m_pDoc->SetDrawPageSize(0);
5573 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
5574 :
5575 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5576 1 : CPPUNIT_ASSERT_MESSAGE("That shouldn't change size or positioning", aOrigRect == rNewRect);
5577 :
5578 1 : m_pDoc->ShowRows(0, 100, 0, false);
5579 1 : m_pDoc->SetDrawPageSize(0);
5580 2 : CPPUNIT_ASSERT_MESSAGE("Left and Right should be unchanged",
5581 1 : aOrigRect.Left() == rNewRect.Left() && aOrigRect.Right() == rNewRect.Right());
5582 2 : CPPUNIT_ASSERT_MESSAGE("Height should be minimum allowed height",
5583 1 : (rNewRect.Bottom() - rNewRect.Top()) <= 1);
5584 1 : m_pDoc->ShowRows(0, 100, 0, true);
5585 1 : m_pDoc->SetDrawPageSize(0);
5586 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
5587 : }
5588 :
5589 : {
5590 : // Add a circle.
5591 1 : Rectangle aOrigRect = Rectangle(10,10,210,210); // 200 x 200
5592 1 : SdrCircObj* pObj = new SdrCircObj(OBJ_CIRC, aOrigRect);
5593 1 : pPage->InsertObject(pObj);
5594 1 : const Rectangle& rNewRect = pObj->GetLogicRect();
5595 2 : CPPUNIT_ASSERT_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
5596 1 : aOrigRect == rNewRect);
5597 :
5598 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5599 2 : CPPUNIT_ASSERT_MESSAGE("Size changed when cell anchored. Not good.",
5600 1 : aOrigRect == rNewRect);
5601 :
5602 : // Insert 2 rows at the top. This should push the circle object down.
5603 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
5604 1 : m_pDoc->SetDrawPageSize(0);
5605 :
5606 : // Make sure the size of the circle is still identical.
5607 2 : CPPUNIT_ASSERT_MESSAGE("Size of the circle has changed, but shouldn't!",
5608 1 : aOrigRect.GetSize() == rNewRect.GetSize());
5609 :
5610 : // Delete 2 rows at the top. This should bring the circle object to its original position.
5611 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
5612 1 : m_pDoc->SetDrawPageSize(0);
5613 1 : CPPUNIT_ASSERT_MESSAGE("Failed to move back to its original position.", aOrigRect == rNewRect);
5614 : }
5615 :
5616 : {
5617 : // Add a line.
5618 1 : basegfx::B2DPolygon aTempPoly;
5619 1 : Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
5620 1 : Rectangle aOrigRect(10,200,110,300); // 100 x 100
5621 1 : aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
5622 1 : aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
5623 1 : SdrPathObj* pObj = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
5624 1 : pObj->NbcSetLogicRect(aOrigRect);
5625 1 : pPage->InsertObject(pObj);
5626 1 : const Rectangle& rNewRect = pObj->GetLogicRect();
5627 1 : CPPUNIT_ASSERT_MESSAGE("Size differ.", aOrigRect == rNewRect);
5628 :
5629 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5630 2 : CPPUNIT_ASSERT_MESSAGE("Size changed when cell-anchored. Not good.",
5631 1 : aOrigRect == rNewRect);
5632 :
5633 : // Insert 2 rows at the top and delete them immediately.
5634 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
5635 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
5636 1 : m_pDoc->SetDrawPageSize(0);
5637 2 : CPPUNIT_ASSERT_MESSAGE("Size of a line object changed after row insertion and removal.",
5638 1 : aOrigRect == rNewRect);
5639 :
5640 1 : sal_Int32 n = pObj->GetPointCount();
5641 1 : CPPUNIT_ASSERT_MESSAGE("There should be exactly 2 points in a line object.", n == 2);
5642 2 : CPPUNIT_ASSERT_MESSAGE("Line shape has changed.",
5643 2 : aStartPos == pObj->GetPoint(0) && aEndPos == pObj->GetPoint(1));
5644 : }
5645 :
5646 1 : m_pDoc->DeleteTab(0);
5647 1 : }
5648 :
5649 1 : void Test::testGraphicsOnSheetMove()
5650 : {
5651 1 : m_pDoc->InsertTab(0, OUString("Tab1"));
5652 1 : m_pDoc->InsertTab(1, OUString("Tab2"));
5653 1 : CPPUNIT_ASSERT_MESSAGE("There should be only 2 sheets to begin with", m_pDoc->GetTableCount() == 2);
5654 :
5655 1 : m_pDoc->InitDrawLayer();
5656 1 : ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
5657 1 : CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
5658 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
5659 1 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
5660 :
5661 : // Insert an object.
5662 1 : Rectangle aObjRect(2,2,100,100);
5663 1 : SdrObject* pObj = new SdrRectObj(aObjRect);
5664 1 : pPage->InsertObject(pObj);
5665 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
5666 :
5667 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(1));
5668 :
5669 1 : const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
5670 1 : CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
5671 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
5672 :
5673 1 : pPage = pDrawLayer->GetPage(1);
5674 1 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
5675 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(0));
5676 :
5677 : // Insert a new sheet at left-end, and make sure the object has moved to
5678 : // the 2nd page.
5679 1 : m_pDoc->InsertTab(0, OUString("NewTab"));
5680 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(3));
5681 1 : pPage = pDrawLayer->GetPage(0);
5682 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
5683 1 : pPage = pDrawLayer->GetPage(1);
5684 1 : CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5685 1 : pPage = pDrawLayer->GetPage(2);
5686 1 : CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage && pPage->GetObjCount() == 0);
5687 :
5688 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
5689 :
5690 : // Now, delete the sheet that just got inserted. The object should be back
5691 : // on the 1st sheet.
5692 1 : m_pDoc->DeleteTab(0);
5693 1 : pPage = pDrawLayer->GetPage(0);
5694 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5695 2 : CPPUNIT_ASSERT_MESSAGE("Size and position of the object shouldn't change.",
5696 1 : pObj->GetLogicRect() == aObjRect);
5697 :
5698 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
5699 :
5700 : // Move the 1st sheet to the last position.
5701 1 : m_pDoc->MoveTab(0, 1);
5702 1 : pPage = pDrawLayer->GetPage(0);
5703 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
5704 1 : pPage = pDrawLayer->GetPage(1);
5705 1 : CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5706 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
5707 :
5708 : // Copy the 2nd sheet, which has one drawing object to the last position.
5709 1 : m_pDoc->CopyTab(1, 2);
5710 1 : pPage = pDrawLayer->GetPage(2);
5711 1 : CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage && pPage->GetObjCount() == 1);
5712 1 : pObj = pPage->GetObj(0);
5713 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
5714 1 : pData = ScDrawLayer::GetObjData(pObj);
5715 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
5716 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 2 && pData->maEnd.Tab() == 2);
5717 :
5718 1 : m_pDoc->DeleteTab(2);
5719 1 : m_pDoc->DeleteTab(1);
5720 1 : m_pDoc->DeleteTab(0);
5721 1 : }
5722 :
5723 1 : void Test::testPostIts()
5724 : {
5725 1 : OUString aHello("Hello world");
5726 2 : OUString aJimBob("Jim Bob");
5727 2 : OUString aTabName("PostIts");
5728 2 : OUString aTabName2("Table2");
5729 1 : m_pDoc->InsertTab(0, aTabName);
5730 :
5731 1 : ScAddress rAddr(2, 2, 0); // cell C3
5732 1 : ScPostIt *pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
5733 1 : pNote->SetText(rAddr, aHello);
5734 1 : pNote->SetAuthor(aJimBob);
5735 :
5736 1 : ScPostIt *pGetNote = m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr);
5737 1 : CPPUNIT_ASSERT_MESSAGE("note should be itself", pGetNote == pNote );
5738 :
5739 : // Insert one row at row 1.
5740 1 : bool bInsertRow = m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 1);
5741 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
5742 :
5743 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
5744 1 : rAddr.IncRow(); // cell C4
5745 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5746 :
5747 : // Insert column at column A.
5748 1 : bool bInsertCol = m_pDoc->InsertCol(0, 0, MAXROW, 0, 1, 1);
5749 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
5750 :
5751 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
5752 1 : rAddr.IncCol(); // cell D4
5753 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5754 :
5755 : // Insert a new sheet to shift the current sheet to the right.
5756 1 : m_pDoc->InsertTab(0, aTabName2);
5757 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
5758 1 : rAddr.IncTab(); // Move to the next sheet.
5759 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5760 :
5761 1 : m_pDoc->DeleteTab(0);
5762 1 : rAddr.IncTab(-1);
5763 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5764 :
5765 : // Insert cell at C4. This should NOT shift the note position.
5766 1 : bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
5767 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
5768 1 : CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5769 :
5770 : // Delete cell at C4. Again, this should NOT shift the note position.
5771 1 : m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
5772 1 : CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5773 :
5774 : // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
5775 1 : m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
5776 1 : rAddr.IncRow(-1); // cell D3
5777 1 : CPPUNIT_ASSERT_MESSAGE("Note at D4 should have shifted up to D3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5778 :
5779 : // Delete column C. This should shift the note one cell left.
5780 1 : m_pDoc->DeleteCol(0, 0, MAXROW, 0, 2, 1);
5781 1 : rAddr.IncCol(-1); // cell C3
5782 1 : CPPUNIT_ASSERT_MESSAGE("Note at D3 should have shifted left to C3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
5783 :
5784 2 : m_pDoc->DeleteTab(0);
5785 1 : }
5786 :
5787 1 : void Test::testToggleRefFlag()
5788 : {
5789 : // In this test, there is no need to insert formula string into a cell in
5790 : // the document, as ScRefFinder does not depend on the content of the
5791 : // document except for the sheet names.
5792 :
5793 1 : OUString aTabName("Test");
5794 1 : m_pDoc->InsertTab(0, aTabName);
5795 :
5796 : {
5797 : // Calc A1: basic 2D reference
5798 :
5799 1 : OUString aFormula("=B100");
5800 1 : ScAddress aPos(1, 5, 0);
5801 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
5802 :
5803 : // Original
5804 1 : CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
5805 :
5806 : // column relative / row relative -> column absolute / row absolute
5807 1 : aFinder.ToggleRel(0, aFormula.getLength());
5808 1 : aFormula = aFinder.GetText();
5809 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B$100" );
5810 :
5811 : // column absolute / row absolute -> column relative / row absolute
5812 1 : aFinder.ToggleRel(0, aFormula.getLength());
5813 1 : aFormula = aFinder.GetText();
5814 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B$100" );
5815 :
5816 : // column relative / row absolute -> column absolute / row relative
5817 1 : aFinder.ToggleRel(0, aFormula.getLength());
5818 1 : aFormula = aFinder.GetText();
5819 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B100" );
5820 :
5821 : // column absolute / row relative -> column relative / row relative
5822 1 : aFinder.ToggleRel(0, aFormula.getLength());
5823 1 : aFormula = aFinder.GetText();
5824 2 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B100" );
5825 : }
5826 :
5827 : {
5828 : // Excel R1C1: basic 2D reference
5829 :
5830 1 : OUString aFormula("=R2C1");
5831 1 : ScAddress aPos(3, 5, 0);
5832 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
5833 :
5834 : // Original
5835 1 : CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
5836 :
5837 : // column absolute / row absolute -> column relative / row absolute
5838 1 : aFinder.ToggleRel(0, aFormula.getLength());
5839 1 : aFormula = aFinder.GetText();
5840 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R2C[-3]"), aFormula);
5841 :
5842 : // column relative / row absolute - > column absolute / row relative
5843 1 : aFinder.ToggleRel(0, aFormula.getLength());
5844 1 : aFormula = aFinder.GetText();
5845 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C1"), aFormula);
5846 :
5847 : // column absolute / row relative -> column relative / row relative
5848 1 : aFinder.ToggleRel(0, aFormula.getLength());
5849 1 : aFormula = aFinder.GetText();
5850 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C[-3]"), aFormula);
5851 :
5852 : // column relative / row relative -> column absolute / row absolute
5853 1 : aFinder.ToggleRel(0, aFormula.getLength());
5854 1 : aFormula = aFinder.GetText();
5855 2 : CPPUNIT_ASSERT_EQUAL(OUString("=R2C1"), aFormula);
5856 : }
5857 :
5858 : {
5859 : // Excel R1C1: Selection at the end of the formula string and does not
5860 : // overlap the formula string at all (inspired by fdo#39135).
5861 1 : OUString aFormula("=R1C1");
5862 1 : ScAddress aPos(1, 1, 0);
5863 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
5864 :
5865 : // Original
5866 1 : CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
5867 :
5868 : // Make the column relative.
5869 1 : sal_Int32 n = aFormula.getLength();
5870 1 : aFinder.ToggleRel(n, n);
5871 1 : aFormula = aFinder.GetText();
5872 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R1C[-1]"), aFormula);
5873 :
5874 : // Make the row relative.
5875 1 : n = aFormula.getLength();
5876 1 : aFinder.ToggleRel(n, n);
5877 1 : aFormula = aFinder.GetText();
5878 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C1"), aFormula);
5879 :
5880 : // Make both relative.
5881 1 : n = aFormula.getLength();
5882 1 : aFinder.ToggleRel(n, n);
5883 1 : aFormula = aFinder.GetText();
5884 1 : CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C[-1]"), aFormula);
5885 :
5886 : // Back to the original.
5887 1 : n = aFormula.getLength();
5888 1 : aFinder.ToggleRel(n, n);
5889 1 : aFormula = aFinder.GetText();
5890 2 : CPPUNIT_ASSERT_EQUAL(OUString("=R1C1"), aFormula);
5891 : }
5892 :
5893 : {
5894 : // Calc A1:
5895 1 : OUString aFormula("=A1+4");
5896 1 : ScAddress aPos(1, 1, 0);
5897 2 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
5898 :
5899 : // Original
5900 1 : CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
5901 :
5902 : // Set the cursor over the 'A1' part and toggle.
5903 1 : aFinder.ToggleRel(2, 2);
5904 1 : aFormula = aFinder.GetText();
5905 1 : CPPUNIT_ASSERT_EQUAL(OUString("=$A$1+4"), aFormula);
5906 :
5907 1 : aFinder.ToggleRel(2, 2);
5908 1 : aFormula = aFinder.GetText();
5909 1 : CPPUNIT_ASSERT_EQUAL(OUString("=A$1+4"), aFormula);
5910 :
5911 1 : aFinder.ToggleRel(2, 2);
5912 1 : aFormula = aFinder.GetText();
5913 1 : CPPUNIT_ASSERT_EQUAL(OUString("=$A1+4"), aFormula);
5914 :
5915 1 : aFinder.ToggleRel(2, 2);
5916 1 : aFormula = aFinder.GetText();
5917 2 : CPPUNIT_ASSERT_EQUAL(OUString("=A1+4"), aFormula);
5918 : }
5919 :
5920 : // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
5921 : // partial selection within formula string.
5922 :
5923 1 : m_pDoc->DeleteTab(0);
5924 1 : }
5925 :
5926 1 : void Test::testAutofilter()
5927 : {
5928 1 : OUString aTabName("Test");
5929 2 : OUString aDBName("NONAME");
5930 :
5931 1 : m_pDoc->InsertTab( 0, aTabName );
5932 :
5933 : // cell contents (0 = empty cell)
5934 : const char* aData[][3] = {
5935 : { "C1", "C2", "C3" },
5936 : { "0", "1", "A" },
5937 : { "1", "2", 0 },
5938 : { "1", "2", "B" },
5939 : { "0", "2", "B" }
5940 1 : };
5941 :
5942 1 : SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
5943 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
5944 :
5945 : // Populate cells.
5946 6 : for (SCROW i = 0; i < nRows; ++i)
5947 20 : for (SCCOL j = 0; j < nCols; ++j)
5948 15 : if (aData[i][j])
5949 14 : m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
5950 :
5951 1 : ScDBData* pDBData = new ScDBData(aDBName, 0, 0, 0, nCols-1, nRows-1);
5952 1 : m_pDoc->SetAnonymousDBData(0,pDBData);
5953 :
5954 1 : pDBData->SetAutoFilter(true);
5955 1 : ScRange aRange;
5956 1 : pDBData->GetArea(aRange);
5957 1 : m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
5958 1 : aRange.aEnd.Col(), aRange.aStart.Row(),
5959 3 : aRange.aStart.Tab(), SC_MF_AUTO);
5960 :
5961 : //create the query param
5962 2 : ScQueryParam aParam;
5963 1 : pDBData->GetQueryParam(aParam);
5964 1 : ScQueryEntry& rEntry = aParam.GetEntry(0);
5965 1 : rEntry.bDoQuery = true;
5966 1 : rEntry.nField = 0;
5967 1 : rEntry.eOp = SC_EQUAL;
5968 1 : rEntry.GetQueryItem().mfVal = 0;
5969 : // add queryParam to database range.
5970 1 : pDBData->SetQueryParam(aParam);
5971 :
5972 : // perform the query.
5973 1 : m_pDoc->Query(0, aParam, true);
5974 :
5975 : //control output
5976 : SCROW nRow1, nRow2;
5977 1 : bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
5978 1 : CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden && nRow1 == 2 && nRow2 == 3);
5979 :
5980 : // Remove filtering.
5981 1 : rEntry.Clear();
5982 1 : m_pDoc->Query(0, aParam, true);
5983 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5984 1 : CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
5985 :
5986 : // Filter for non-empty cells by column C.
5987 1 : rEntry.bDoQuery = true;
5988 1 : rEntry.nField = 2;
5989 1 : rEntry.SetQueryByNonEmpty();
5990 1 : m_pDoc->Query(0, aParam, true);
5991 :
5992 : // only row 3 should be hidden. The rest should be visible.
5993 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
5994 1 : CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 1);
5995 1 : bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
5996 1 : CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden && nRow1 == 2 && nRow2 == 2);
5997 1 : bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
5998 1 : CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden && nRow1 == 3 && nRow2 == MAXROW);
5999 :
6000 : // Now, filter for empty cells by column C.
6001 1 : rEntry.SetQueryByEmpty();
6002 1 : m_pDoc->Query(0, aParam, true);
6003 :
6004 : // Now, only row 1 and 3, and 6 and down should be visible.
6005 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
6006 1 : CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 0);
6007 1 : bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
6008 1 : CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden && nRow1 == 1 && nRow2 == 1);
6009 1 : bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
6010 1 : CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden && nRow1 == 2 && nRow2 == 2);
6011 1 : bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
6012 1 : CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden && nRow1 == 3 && nRow2 == 4);
6013 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
6014 1 : CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden && nRow1 == 5 && nRow2 == MAXROW);
6015 :
6016 2 : m_pDoc->DeleteTab(0);
6017 1 : }
6018 :
6019 1 : void Test::testCopyPaste()
6020 : {
6021 1 : m_pDoc->InsertTab(0, OUString("Sheet1"));
6022 1 : m_pDoc->InsertTab(1, OUString("Sheet2"));
6023 : //test copy&paste + ScUndoPaste
6024 : //copy local and global range names in formulas
6025 : //string cells and value cells
6026 1 : m_pDoc->SetValue(0, 0, 0, 1);
6027 1 : m_pDoc->SetValue(3, 0, 0, 0);
6028 1 : m_pDoc->SetValue(3, 1, 0, 1);
6029 1 : m_pDoc->SetValue(3, 2, 0, 2);
6030 1 : m_pDoc->SetValue(3, 3, 0, 3);
6031 1 : m_pDoc->SetString(2, 0, 0, OUString("test"));
6032 1 : ScAddress aAdr (0, 0, 0);
6033 :
6034 : //create some range names, local and global
6035 1 : ScRangeData* pLocal1 = new ScRangeData(m_pDoc, OUString("local1"), aAdr);
6036 1 : ScRangeData* pLocal2 = new ScRangeData(m_pDoc, OUString("local2"), aAdr);
6037 1 : ScRangeData* pGlobal = new ScRangeData(m_pDoc, OUString("global"), aAdr);
6038 1 : ScRangeName* pGlobalRangeName = new ScRangeName();
6039 1 : pGlobalRangeName->insert(pGlobal);
6040 1 : ScRangeName* pLocalRangeName1 = new ScRangeName();
6041 1 : pLocalRangeName1->insert(pLocal1);
6042 1 : pLocalRangeName1->insert(pLocal2);
6043 1 : m_pDoc->SetRangeName(pGlobalRangeName);
6044 1 : m_pDoc->SetRangeName(0, pLocalRangeName1);
6045 :
6046 : // Add formula to B1.
6047 1 : OUString aFormulaString("=local1+global+SUM($C$1:$D$4)");
6048 1 : m_pDoc->SetString(1, 0, 0, aFormulaString);
6049 :
6050 1 : double fValue = m_pDoc->GetValue(ScAddress(1,0,0));
6051 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 8", fValue, 8);
6052 :
6053 : //copy Sheet1.A1:C1 to Sheet2.A2:C2
6054 1 : ScRange aRange(0,0,0,2,0,0);
6055 2 : ScClipParam aClipParam(aRange, false);
6056 2 : ScMarkData aMark;
6057 1 : aMark.SetMarkArea(aRange);
6058 2 : ScDocument aClipDoc(SCDOCMODE_CLIP);
6059 1 : m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
6060 :
6061 1 : sal_uInt16 nFlags = IDF_ALL;
6062 1 : aRange = ScRange(0,1,1,2,1,1);//target: Sheet2.A2:C2
6063 1 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
6064 1 : pUndoDoc->InitUndo(m_pDoc, 1, 1, true, true);
6065 2 : ScMarkData aMarkData2;
6066 1 : aMarkData2.SetMarkArea(aRange);
6067 1 : ScRefUndoData* pRefUndoData= new ScRefUndoData(m_pDoc);
6068 : ScUndoPaste aUndo(
6069 2 : &m_xDocShRef, aRange, aMarkData2, pUndoDoc, NULL, IDF_ALL, pRefUndoData, false);
6070 1 : m_pDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, &aClipDoc);
6071 :
6072 : //check values after copying
6073 2 : OUString aString;
6074 1 : fValue = m_pDoc->GetValue(ScAddress(1,1,1));
6075 1 : m_pDoc->GetFormula(1,1,1, aString);
6076 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("copied formula should return 2", fValue, 2);
6077 1 : CPPUNIT_ASSERT_MESSAGE("formula string was not copied correctly", aString == aFormulaString);
6078 1 : fValue = m_pDoc->GetValue(ScAddress(0,1,1));
6079 1 : CPPUNIT_ASSERT_MESSAGE("copied value should be 1", fValue == 1);
6080 :
6081 : //chack local range name after copying
6082 1 : pLocal1 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL1"));
6083 1 : CPPUNIT_ASSERT_MESSAGE("local range name 1 should be copied", pLocal1);
6084 1 : ScRange aRangeLocal1;
6085 1 : pLocal1->IsValidReference(aRangeLocal1);
6086 1 : CPPUNIT_ASSERT_MESSAGE("local range 1 should still point to Sheet1.A1",aRangeLocal1 == ScRange(0,0,0,0,0,0));
6087 1 : pLocal2 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
6088 1 : CPPUNIT_ASSERT_MESSAGE("local2 should not be copied", pLocal2 == NULL);
6089 :
6090 :
6091 : //check undo and redo
6092 1 : aUndo.Undo();
6093 1 : fValue = m_pDoc->GetValue(ScAddress(1,1,1));
6094 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", fValue, 0);
6095 1 : aString = m_pDoc->GetString(2, 1, 1);
6096 1 : CPPUNIT_ASSERT_MESSAGE("after undo string should be removed", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("")));
6097 :
6098 1 : aUndo.Redo();
6099 1 : fValue = m_pDoc->GetValue(ScAddress(1,1,1));
6100 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 2 after redo", fValue, 2);
6101 1 : aString = m_pDoc->GetString(2, 1, 1);
6102 1 : CPPUNIT_ASSERT_MESSAGE("Cell Sheet2.C2 should contain: test", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("test")));
6103 1 : m_pDoc->GetFormula(1,1,1, aString);
6104 1 : CPPUNIT_ASSERT_MESSAGE("Formula should be correct again", aString == aFormulaString);
6105 :
6106 1 : m_pDoc->DeleteTab(1);
6107 2 : m_pDoc->DeleteTab(0);
6108 1 : }
6109 :
6110 1 : void Test::testMergedCells()
6111 : {
6112 : //test merge and unmerge
6113 : //TODO: an undo/redo test for this would be a good idea
6114 1 : m_pDoc->InsertTab(0, OUString("Sheet1"));
6115 1 : m_pDoc->DoMerge(0, 1, 1, 3, 3, false);
6116 1 : SCCOL nEndCol = 1;
6117 1 : SCROW nEndRow = 1;
6118 1 : m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0, false);
6119 1 : CPPUNIT_ASSERT_MESSAGE("did not merge cells", nEndCol == 3 && nEndRow == 3);
6120 1 : ScRange aRange(0,2,0,MAXCOL,2,0);
6121 1 : ScMarkData aMark;
6122 1 : aMark.SetMarkArea(aRange);
6123 1 : m_xDocShRef->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS, true, true);
6124 1 : m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0, false);
6125 1 : cout << nEndRow << nEndCol;
6126 1 : CPPUNIT_ASSERT_MESSAGE("did not increase merge area", nEndCol == 3 && nEndRow == 4);
6127 1 : m_pDoc->DeleteTab(0);
6128 1 : }
6129 :
6130 :
6131 1 : void Test::testRenameTable()
6132 : {
6133 : //test set rename table
6134 : //TODO: set name1 and name2 and do an undo to check if name 1 is set now
6135 : //TODO: also check if new name for table is same as another table
6136 :
6137 1 : m_pDoc->InsertTab(0, "Sheet1");
6138 1 : m_pDoc->InsertTab(1, "Sheet2");
6139 :
6140 : //test case 1 , rename table2 to sheet 1, it should return error
6141 1 : OUString nameToSet = "Sheet1";
6142 1 : ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
6143 1 : CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
6144 :
6145 : //test case 2 , simple rename to check name
6146 1 : nameToSet = "test1";
6147 1 : m_xDocShRef->GetDocFunc().RenameTable(0,nameToSet,false,true);
6148 2 : OUString nameJustSet;
6149 1 : m_pDoc->GetName(0,nameJustSet);
6150 1 : CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
6151 :
6152 : //test case 3 , rename again
6153 2 : OUString anOldName;
6154 1 : m_pDoc->GetName(0,anOldName);
6155 :
6156 1 : nameToSet = "test2";
6157 1 : rDocFunc.RenameTable(0,nameToSet,false,true);
6158 1 : m_pDoc->GetName(0,nameJustSet);
6159 1 : CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
6160 :
6161 : //test case 4 , check if undo works
6162 1 : SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShRef,0,anOldName,nameToSet);
6163 1 : pUndo->Undo();
6164 1 : m_pDoc->GetName(0,nameJustSet);
6165 1 : CPPUNIT_ASSERT_MESSAGE("the correct name is not set after undo", nameJustSet == anOldName);
6166 :
6167 1 : pUndo->Redo();
6168 1 : m_pDoc->GetName(0,nameJustSet);
6169 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after redo", nameJustSet == nameToSet);
6170 :
6171 1 : m_pDoc->DeleteTab(0);
6172 2 : m_pDoc->DeleteTab(1);
6173 1 : }
6174 :
6175 :
6176 :
6177 1 : void Test::testSetBackgroundColor()
6178 : {
6179 : //test set background color
6180 : //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
6181 :
6182 1 : m_pDoc->InsertTab(0, "Sheet1");
6183 1 : Color aColor;
6184 :
6185 : //test yellow
6186 1 : aColor=Color(COL_YELLOW);
6187 1 : m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
6188 2 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set",
6189 1 : m_pDoc->GetTabBgColor(0) == aColor);
6190 :
6191 :
6192 1 : Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
6193 1 : aColor.SetColor(COL_BLUE);//set BLUE
6194 1 : m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
6195 2 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set the second time",
6196 1 : m_pDoc->GetTabBgColor(0) == aColor);
6197 :
6198 : //now check for undo
6199 1 : SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShRef,0, aOldTabBgColor, aColor);
6200 1 : pUndo->Undo();
6201 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aOldTabBgColor);
6202 1 : pUndo->Redo();
6203 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aColor);
6204 1 : m_pDoc->DeleteTab(0);
6205 1 : }
6206 :
6207 :
6208 :
6209 1 : void Test::testUpdateReference()
6210 : {
6211 : //test that formulas are correctly updated during sheet delete
6212 : //TODO: add tests for relative references, updating of named ranges, ...
6213 1 : OUString aSheet1("Sheet1");
6214 2 : OUString aSheet2("Sheet2");
6215 2 : OUString aSheet3("Sheet3");
6216 2 : OUString aSheet4("Sheet4");
6217 1 : m_pDoc->InsertTab(0, aSheet1);
6218 1 : m_pDoc->InsertTab(1, aSheet2);
6219 1 : m_pDoc->InsertTab(2, aSheet3);
6220 1 : m_pDoc->InsertTab(3, aSheet4);
6221 :
6222 1 : m_pDoc->SetValue(0,0,2, 1);
6223 1 : m_pDoc->SetValue(1,0,2, 2);
6224 1 : m_pDoc->SetValue(1,1,3, 4);
6225 1 : m_pDoc->SetString(2,0,2, OUString("=A1+B1"));
6226 1 : m_pDoc->SetString(2,1,2, OUString("=Sheet4.B2+A1"));
6227 :
6228 : double aValue;
6229 1 : m_pDoc->GetValue(2,0,2, aValue);
6230 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 3);
6231 1 : m_pDoc->GetValue(2,1,2, aValue);
6232 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 5);
6233 :
6234 : //test deleting both sheets: one is not directly before the sheet, the other one is
6235 1 : m_pDoc->DeleteTab(0);
6236 1 : m_pDoc->GetValue(2,0,1, aValue);
6237 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 3);
6238 1 : m_pDoc->GetValue(2,1,1, aValue);
6239 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 5);
6240 :
6241 1 : m_pDoc->DeleteTab(0);
6242 1 : m_pDoc->GetValue(2,0,0, aValue);
6243 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 3);
6244 1 : m_pDoc->GetValue(2,1,0, aValue);
6245 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 5);
6246 :
6247 : //test adding two sheets
6248 1 : m_pDoc->InsertTab(0, aSheet2);
6249 1 : m_pDoc->GetValue(2,0,1, aValue);
6250 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 3);
6251 1 : m_pDoc->GetValue(2,1,1, aValue);
6252 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 5);
6253 :
6254 1 : m_pDoc->InsertTab(0, aSheet1);
6255 1 : m_pDoc->GetValue(2,0,2, aValue);
6256 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 3);
6257 1 : m_pDoc->GetValue(2,1,2, aValue);
6258 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 5);
6259 :
6260 : //test new DeleteTabs/InsertTabs methods
6261 1 : m_pDoc->DeleteTabs(0, 2);
6262 1 : m_pDoc->GetValue(2, 0, 0, aValue);
6263 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 3);
6264 1 : m_pDoc->GetValue(2, 1, 0, aValue);
6265 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 5);
6266 :
6267 2 : std::vector<OUString> aSheets;
6268 1 : aSheets.push_back(aSheet1);
6269 1 : aSheets.push_back(aSheet2);
6270 1 : m_pDoc->InsertTabs(0, aSheets, false, true);
6271 1 : m_pDoc->GetValue(2, 0, 2, aValue);
6272 2 : OUString aFormula;
6273 1 : m_pDoc->GetFormula(2,0,2, aFormula);
6274 1 : std::cout << "formel: " << OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
6275 1 : std::cout << std::endl << aValue << std::endl;
6276 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 3);
6277 1 : m_pDoc->GetValue(2, 1, 2, aValue);
6278 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 5);
6279 :
6280 1 : m_pDoc->DeleteTab(3);
6281 1 : m_pDoc->DeleteTab(2);
6282 1 : m_pDoc->DeleteTab(1);
6283 2 : m_pDoc->DeleteTab(0);
6284 1 : }
6285 :
6286 1 : void Test::testSearchCells()
6287 : {
6288 1 : m_pDoc->InsertTab(0, "Test");
6289 :
6290 1 : m_pDoc->SetString(ScAddress(0,0,0), "A");
6291 1 : m_pDoc->SetString(ScAddress(0,1,0), "B");
6292 1 : m_pDoc->SetString(ScAddress(0,2,0), "A");
6293 : // Leave A4 blank.
6294 1 : m_pDoc->SetString(ScAddress(0,4,0), "A");
6295 1 : m_pDoc->SetString(ScAddress(0,5,0), "B");
6296 1 : m_pDoc->SetString(ScAddress(0,6,0), "C");
6297 :
6298 1 : SvxSearchItem aItem(SID_SEARCH_ITEM);
6299 1 : aItem.SetSearchString(OUString("A"));
6300 1 : aItem.SetCommand(SVX_SEARCHCMD_FIND_ALL);
6301 2 : ScMarkData aMarkData;
6302 1 : aMarkData.SelectOneTable(0);
6303 1 : SCCOL nCol = 0;
6304 1 : SCROW nRow = 0;
6305 1 : SCTAB nTab = 0;
6306 2 : ScRangeList aMatchedRanges;
6307 2 : OUString aUndoStr;
6308 1 : m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr);
6309 :
6310 1 : CPPUNIT_ASSERT_MESSAGE("There should be exactly 3 matching cells.", aMatchedRanges.size() == 3);
6311 1 : ScAddress aHit(0,0,0);
6312 1 : CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.In(aHit));
6313 1 : aHit.SetRow(2);
6314 1 : CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.In(aHit));
6315 1 : aHit.SetRow(4);
6316 1 : CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.In(aHit));
6317 :
6318 2 : m_pDoc->DeleteTab(0);
6319 1 : }
6320 :
6321 1 : void Test::testSharedFormulas()
6322 : {
6323 1 : m_pDoc->InsertTab(0, "Test");
6324 :
6325 1 : ScAddress aPos(1, 9, 0); // B10
6326 1 : m_pDoc->SetString(aPos, "=A10*2"); // Insert into B10.
6327 1 : const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
6328 1 : CPPUNIT_ASSERT_MESSAGE("Expected to be a non-shared cell.", pFC && !pFC->IsShared());
6329 :
6330 1 : aPos.SetRow(10); // B11
6331 1 : m_pDoc->SetString(aPos, "=A11*2");
6332 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6333 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6334 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
6335 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6336 :
6337 1 : aPos.SetRow(8); // B9
6338 1 : m_pDoc->SetString(aPos, "=A9*2");
6339 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6340 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6341 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
6342 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
6343 :
6344 1 : aPos.SetRow(12); // B13
6345 1 : m_pDoc->SetString(aPos, "=A13*2");
6346 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6347 1 : CPPUNIT_ASSERT_MESSAGE("This formula cell shouldn't be shared yet.", pFC && !pFC->IsShared());
6348 :
6349 : // Insert a formula to B12, and B9:B13 should be shared.
6350 1 : aPos.SetRow(11); // B12
6351 1 : m_pDoc->SetString(aPos, "=A12*2");
6352 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6353 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6354 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
6355 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedLength());
6356 :
6357 : // Insert formulas to B15:B16.
6358 1 : aPos.SetRow(14); // B15
6359 1 : m_pDoc->SetString(aPos, "=A15*2");
6360 1 : aPos.SetRow(15); // B16
6361 1 : m_pDoc->SetString(aPos, "=A16*2");
6362 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6363 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6364 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(14), pFC->GetSharedTopRow());
6365 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
6366 :
6367 : // Insert a formula to B14, and B9:B16 should be shared.
6368 1 : aPos.SetRow(13); // B14
6369 1 : m_pDoc->SetString(aPos, "=A14*2");
6370 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6371 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6372 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
6373 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
6374 :
6375 : // Insert an incompatible formula to B12, to split the shared range to B9:B11 and B13:B16.
6376 1 : aPos.SetRow(11); // B12
6377 1 : m_pDoc->SetString(aPos, "=$A$1*4");
6378 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6379 1 : CPPUNIT_ASSERT_MESSAGE("This cell shouldn't be shared.", pFC && !pFC->IsShared());
6380 :
6381 1 : aPos.SetRow(8); // B9
6382 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6383 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6384 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
6385 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
6386 :
6387 1 : aPos.SetRow(12); // B13
6388 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6389 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6390 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
6391 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
6392 :
6393 : // Extend B13:B16 to B13:B20.
6394 1 : aPos.SetRow(16); // B17
6395 1 : m_pDoc->SetString(aPos, "=A17*2");
6396 1 : aPos.IncRow();
6397 1 : m_pDoc->SetString(aPos, "=A18*2");
6398 1 : aPos.IncRow();
6399 1 : m_pDoc->SetString(aPos, "=A19*2");
6400 1 : aPos.IncRow();
6401 1 : m_pDoc->SetString(aPos, "=A20*2");
6402 1 : pFC = m_pDoc->GetFormulaCell(aPos);
6403 1 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6404 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
6405 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
6406 :
6407 : #if 0
6408 : // Insert empty rows at B16 to split B13:B20 into B13:B15 and B21:B25.
6409 : m_pDoc->InsertRow(1, 0, 1, 0, 15, 5);
6410 :
6411 : aPos.SetRow(12); // B13
6412 : pFC = m_pDoc->GetFormulaCell(aPos);
6413 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6414 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
6415 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
6416 :
6417 : aPos.SetRow(23); // B24
6418 : pFC = m_pDoc->GetFormulaCell(aPos);
6419 : CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
6420 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(20), pFC->GetSharedTopRow());
6421 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedLength());
6422 : #endif
6423 :
6424 1 : m_pDoc->DeleteTab(0);
6425 1 : }
6426 :
6427 : namespace {
6428 :
6429 6 : bool checkFormulaPosition(ScDocument& rDoc, const ScAddress& rPos)
6430 : {
6431 6 : OUString aStr;
6432 6 : rPos.Format(aStr, SCA_VALID);
6433 6 : const ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
6434 6 : if (!pFC)
6435 : {
6436 0 : cerr << "Formula cell expected at " << aStr << " but not found." << endl;
6437 0 : return false;
6438 : }
6439 :
6440 6 : if (pFC->aPos != rPos)
6441 : {
6442 0 : OUString aStr2;
6443 0 : pFC->aPos.Format(aStr2, SCA_VALID);
6444 0 : cerr << "Formula cell at " << aStr << " has incorrect position of " << aStr2 << endl;
6445 0 : return false;
6446 : }
6447 :
6448 6 : return true;
6449 : }
6450 :
6451 2 : void checkFormulaPositions(ScDocument& rDoc, const ScAddress& rPos, const SCROW* pRows, size_t nRowCount)
6452 : {
6453 2 : ScAddress aPos = rPos;
6454 8 : for (size_t i = 0; i < nRowCount; ++i)
6455 : {
6456 6 : SCROW nRow = pRows[i];
6457 6 : aPos.SetRow(nRow);
6458 :
6459 6 : if (!checkFormulaPosition(rDoc, aPos))
6460 : {
6461 0 : OUString aStr;
6462 0 : aPos.Format(aStr, SCA_VALID);
6463 0 : std::ostringstream os;
6464 0 : os << "Formula cell position failed at " << aStr;
6465 0 : CPPUNIT_FAIL(os.str().c_str());
6466 : }
6467 : }
6468 2 : }
6469 :
6470 : }
6471 :
6472 1 : void Test::testFormulaPosition()
6473 : {
6474 1 : m_pDoc->InsertTab(0, "Test");
6475 :
6476 1 : ScAddress aPos(0,0,0); // A1
6477 1 : m_pDoc->SetString(aPos, "=ROW()");
6478 1 : aPos.IncRow(); // A2
6479 1 : m_pDoc->SetString(aPos, "=ROW()");
6480 1 : aPos.SetRow(3); // A4;
6481 1 : m_pDoc->SetString(aPos, "=ROW()");
6482 :
6483 : {
6484 1 : SCROW aRows[] = { 0, 1, 3 };
6485 1 : checkFormulaPositions(*m_pDoc, aPos, aRows, SAL_N_ELEMENTS(aRows));
6486 : }
6487 :
6488 1 : m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
6489 : {
6490 1 : SCROW aRows[] = { 0, 6, 8 };
6491 1 : checkFormulaPositions(*m_pDoc, aPos, aRows, SAL_N_ELEMENTS(aRows));
6492 : }
6493 :
6494 1 : m_pDoc->DeleteTab(0);
6495 1 : }
6496 :
6497 : namespace {
6498 :
6499 4 : bool hasRange(const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange)
6500 : {
6501 4 : std::vector<ScTokenRef>::const_iterator it = rRefTokens.begin(), itEnd = rRefTokens.end();
6502 5 : for (; it != itEnd; ++it)
6503 : {
6504 5 : const ScTokenRef& p = *it;
6505 5 : if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
6506 0 : continue;
6507 :
6508 5 : switch (p->GetType())
6509 : {
6510 : case formula::svSingleRef:
6511 : {
6512 2 : ScSingleRefData aData = p->GetSingleRef();
6513 2 : if (rRange.aStart != rRange.aEnd)
6514 0 : break;
6515 :
6516 2 : ScAddress aThis(aData.nCol, aData.nRow, aData.nTab);
6517 2 : if (aThis == rRange.aStart)
6518 2 : return true;
6519 : }
6520 0 : break;
6521 : case formula::svDoubleRef:
6522 : {
6523 3 : ScComplexRefData aData = p->GetDoubleRef();
6524 3 : ScRange aThis(aData.Ref1.nCol, aData.Ref1.nRow, aData.Ref1.nTab, aData.Ref2.nCol, aData.Ref2.nRow, aData.Ref2.nTab);
6525 3 : if (aThis == rRange)
6526 2 : return true;
6527 : }
6528 1 : break;
6529 : default:
6530 : ;
6531 : }
6532 : }
6533 0 : return false;
6534 : }
6535 :
6536 : }
6537 :
6538 1 : void Test::testJumpToPrecedentsDependents()
6539 : {
6540 : // Precedent is another cell that the cell references, while dependent is
6541 : // another cell that references it.
6542 1 : m_pDoc->InsertTab(0, OUString("Test"));
6543 :
6544 1 : m_pDoc->SetString(2, 0, 0, OUString("=A1+A2+B3")); // C1
6545 1 : m_pDoc->SetString(2, 1, 0, OUString("=A1")); // C2
6546 1 : m_pDoc->CalcAll();
6547 :
6548 1 : std::vector<ScTokenRef> aRefTokens;
6549 1 : ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
6550 :
6551 : {
6552 : // C1's precedent should be A1:A2,B3.
6553 1 : ScRangeList aRange(ScRange(2, 0, 0));
6554 1 : rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
6555 2 : CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
6556 1 : hasRange(aRefTokens, ScRange(0, 0, 0, 0, 1, 0)));
6557 2 : CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
6558 2 : hasRange(aRefTokens, ScRange(1, 2, 0)));
6559 : }
6560 :
6561 : {
6562 : // C2's precedent should be A1 only.
6563 1 : ScRangeList aRange(ScRange(2, 1, 0));
6564 1 : rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
6565 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
6566 1 : aRefTokens.size(), static_cast<size_t>(1));
6567 2 : CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
6568 2 : hasRange(aRefTokens, ScRange(0, 0, 0)));
6569 : }
6570 :
6571 : {
6572 : // A1's dependent should be C1:C2.
6573 1 : ScRangeList aRange(ScRange(0, 0, 0));
6574 1 : rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
6575 2 : CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
6576 2 : aRefTokens.size() == 1 && hasRange(aRefTokens, ScRange(2, 0, 0, 2, 1, 0)));
6577 : }
6578 :
6579 1 : m_pDoc->DeleteTab(0);
6580 1 : }
6581 :
6582 1 : void Test::testAutoFill()
6583 : {
6584 1 : m_pDoc->InsertTab(0, "test");
6585 :
6586 1 : m_pDoc->SetValue(0,0,0,1);
6587 :
6588 1 : ScMarkData aMarkData;
6589 1 : aMarkData.SelectTable(0, true);
6590 :
6591 1 : m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 5);
6592 7 : for (SCROW i = 0; i< 6; ++i)
6593 6 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
6594 :
6595 : // check that hidden rows are not affected by autofill
6596 : // set values for hidden rows
6597 1 : m_pDoc->SetValue(0,1,0,10);
6598 1 : m_pDoc->SetValue(0,2,0,10);
6599 :
6600 1 : m_pDoc->SetRowHidden(1, 2, 0, true);
6601 1 : m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 8);
6602 :
6603 1 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
6604 1 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
6605 6 : for (SCROW i = 3; i< 8; ++i)
6606 5 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
6607 :
6608 1 : m_pDoc->Fill( 0, 0, 0, 8, NULL, aMarkData, 5, FILL_TO_RIGHT );
6609 6 : for (SCCOL i = 0; i < 5; ++i)
6610 : {
6611 45 : for(SCROW j = 0; j < 8; ++j)
6612 : {
6613 40 : if (j > 2)
6614 : {
6615 25 : ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
6616 : }
6617 15 : else if (j == 0)
6618 : {
6619 5 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
6620 : }
6621 10 : else if (j == 1 || j== 2)
6622 : {
6623 10 : if(i == 0)
6624 2 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
6625 : else
6626 8 : ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
6627 : }
6628 : }
6629 : }
6630 :
6631 : // test auto fill user data lists
6632 1 : m_pDoc->SetString( 0, 100, 0, "January" );
6633 1 : m_pDoc->Fill( 0, 100, 0, 100, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
6634 2 : OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
6635 1 : CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("February") );
6636 1 : aTestValue = m_pDoc->GetString( 0, 102, 0 );
6637 1 : CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("March") );
6638 :
6639 : // test that two same user data list entries will not result in incremental fill
6640 1 : m_pDoc->SetString( 0, 101, 0, "January" );
6641 1 : m_pDoc->Fill( 0, 100, 0, 101, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
6642 3 : for ( SCROW i = 102; i <= 103; ++i )
6643 : {
6644 2 : aTestValue = m_pDoc->GetString( 0, i, 0 );
6645 2 : CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("January") );
6646 : }
6647 2 : m_pDoc->DeleteTab(0);
6648 1 : }
6649 :
6650 1 : void Test::testCopyPasteFormulas()
6651 : {
6652 1 : m_pDoc->InsertTab(0, "Sheet1");
6653 1 : m_pDoc->InsertTab(1, "Sheet2");
6654 :
6655 1 : m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
6656 1 : m_pDoc->SetString(0,1,0, "=$A$1+B2" );
6657 1 : m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
6658 1 : m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
6659 1 : m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
6660 :
6661 : // to prevent ScEditableTester in ScDocFunc::MoveBlock
6662 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,0,0), 1.0);
6663 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,1,0), 1.0);
6664 1 : ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
6665 1 : bool bMoveDone = rDocFunc.MoveBlock(ScRange(0,0,0,0,4,0), ScAddress( 10, 10, 0), false, false, false, true);
6666 :
6667 : // check that moving was succesful, mainly for editable tester
6668 1 : CPPUNIT_ASSERT(bMoveDone);
6669 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,10,0), 1.0);
6670 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,11,0), 1.0);
6671 1 : OUString aFormula;
6672 1 : m_pDoc->GetFormula(10,10,0, aFormula);
6673 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($A$1)"));
6674 1 : m_pDoc->GetFormula(10,11,0, aFormula);
6675 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$A$1+L12"));
6676 1 : m_pDoc->GetFormula(10,12,0, aFormula);
6677 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K11"));
6678 1 : m_pDoc->GetFormula(10,13,0, aFormula);
6679 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.$A$1"));
6680 1 : m_pDoc->GetFormula(10,14,0, aFormula);
6681 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K$1"));
6682 1 : }
6683 :
6684 1 : void Test::testCopyPasteFormulasExternalDoc()
6685 : {
6686 1 : OUString aDocName("file:///source.fake");
6687 1 : SfxMedium* pMedium = new SfxMedium(aDocName, STREAM_STD_READWRITE);
6688 1 : m_xDocShRef->DoInitNew(pMedium);
6689 1 : m_pDoc = m_xDocShRef->GetDocument();
6690 :
6691 2 : ScDocShellRef xExtDocSh = new ScDocShell;
6692 2 : OUString aExtDocName("file:///extdata.fake");
6693 2 : OUString aExtSh1Name("ExtSheet1");
6694 2 : OUString aExtSh2Name("ExtSheet2");
6695 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
6696 1 : xExtDocSh->DoInitNew(pMed);
6697 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
6698 1 : findLoadedDocShellByName(aExtDocName) != NULL);
6699 :
6700 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
6701 1 : pExtDoc->InsertTab(0, aExtSh1Name);
6702 1 : pExtDoc->InsertTab(1, aExtSh2Name);
6703 :
6704 1 : m_pDoc->InsertTab(0, "Sheet1");
6705 1 : m_pDoc->InsertTab(1, "Sheet2");
6706 :
6707 1 : m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
6708 1 : m_pDoc->SetString(0,1,0, "=$A$1+B2" );
6709 1 : m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
6710 1 : m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
6711 1 : m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
6712 1 : m_pDoc->SetString(0,5,0, "=$Sheet1.$A$1");
6713 :
6714 1 : ScRange aRange(0,0,0,0,5,0);
6715 2 : ScClipParam aClipParam(aRange, false);
6716 2 : ScMarkData aMark;
6717 1 : aMark.SetMarkArea(aRange);
6718 1 : ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
6719 1 : m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
6720 :
6721 1 : sal_uInt16 nFlags = IDF_ALL;
6722 1 : aRange = ScRange(1,1,1,1,6,1);
6723 2 : ScMarkData aMarkData2;
6724 1 : aMarkData2.SetMarkArea(aRange);
6725 1 : pExtDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
6726 :
6727 2 : OUString aFormula;
6728 1 : pExtDoc->GetFormula(1,1,1, aFormula);
6729 : //adjust absolute refs pointing to the copy area
6730 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($B$2)"));
6731 1 : pExtDoc->GetFormula(1,2,1, aFormula);
6732 : //adjust absolute refs and keep relative refs
6733 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$B$2+C3"));
6734 1 : pExtDoc->GetFormula(1,3,1, aFormula);
6735 : // make absolute sheet refs external refs
6736 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B2"));
6737 1 : pExtDoc->GetFormula(1,4,1, aFormula);
6738 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.$A$1"));
6739 1 : pExtDoc->GetFormula(1,5,1, aFormula);
6740 1 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B$1"));
6741 1 : pExtDoc->GetFormula(1,6,1, aFormula);
6742 2 : CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$ExtSheet2.$B$2"));
6743 1 : }
6744 :
6745 1 : void Test::testFindAreaPosVertical()
6746 : {
6747 : const char* aData[][3] = {
6748 : { 0, "1", "1" },
6749 : { "1", 0, "1" },
6750 : { "1", "1", "1" },
6751 : { 0, "1", "1" },
6752 : { "1", "1", "1" },
6753 : { "1", 0, "1" },
6754 : { "1", "1", "1" },
6755 1 : };
6756 :
6757 1 : m_pDoc->InsertTab(0, "Test1");
6758 1 : clearRange( m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
6759 1 : ScAddress aPos(0,0,0);
6760 1 : ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6761 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6762 :
6763 1 : m_pDoc->SetRowHidden(4,4,0,true);
6764 1 : bool bHidden = m_pDoc->RowHidden(4,0);
6765 1 : CPPUNIT_ASSERT(bHidden);
6766 :
6767 1 : SCCOL nCol = 0;
6768 1 : SCROW nRow = 0;
6769 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6770 :
6771 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
6772 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6773 :
6774 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6775 :
6776 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
6777 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6778 :
6779 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6780 :
6781 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
6782 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6783 :
6784 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6785 :
6786 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
6787 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6788 :
6789 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6790 :
6791 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(MAXROW), nRow);
6792 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
6793 :
6794 1 : nCol = 1;
6795 1 : nRow = 2;
6796 :
6797 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6798 :
6799 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
6800 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
6801 :
6802 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
6803 :
6804 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
6805 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
6806 :
6807 1 : nCol = 2;
6808 1 : nRow = 6;
6809 1 : m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
6810 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6811 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
6812 :
6813 :
6814 1 : m_pDoc->DeleteTab(0);
6815 1 : }
6816 :
6817 1 : void Test::testFindAreaPosColRight()
6818 : {
6819 : const char* aData[][7] = {
6820 : { "", "1", "1", "", "1", "1", "1" },
6821 1 : { "", "", "1", "1", "1", "", "1" }, };
6822 :
6823 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
6824 1 : OUString aTabName1("test1");
6825 1 : pDoc->InsertTab(0, aTabName1);
6826 1 : clearRange( pDoc, ScRange(0, 0, 0, 7, SAL_N_ELEMENTS(aData), 0));
6827 1 : ScAddress aPos(0,0,0);
6828 1 : ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6829 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6830 :
6831 1 : pDoc->SetColHidden(4,4,0,true);
6832 1 : bool bHidden = pDoc->ColHidden(4,0);
6833 1 : CPPUNIT_ASSERT(bHidden);
6834 :
6835 1 : SCCOL nCol = 0;
6836 1 : SCROW nRow = 0;
6837 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6838 :
6839 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6840 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
6841 :
6842 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6843 :
6844 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6845 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
6846 :
6847 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6848 :
6849 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6850 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
6851 :
6852 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6853 :
6854 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6855 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
6856 :
6857 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6858 :
6859 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
6860 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(MAXCOL), nCol);
6861 :
6862 1 : nCol = 2;
6863 1 : nRow = 1;
6864 :
6865 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6866 :
6867 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
6868 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
6869 :
6870 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
6871 :
6872 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
6873 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
6874 :
6875 1 : pDoc->DeleteTab(0);
6876 1 : }
6877 :
6878 : // regression test fo fdo#53814, sorting doens't work as expected
6879 : // if cells in the sort are referenced by formulas
6880 1 : void Test::testSortWithFormulaRefs()
6881 : {
6882 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
6883 1 : OUString aTabName1("List1");
6884 2 : OUString aTabName2("List2");
6885 1 : pDoc->InsertTab(0, aTabName1);
6886 1 : pDoc->InsertTab(1, aTabName2);
6887 :
6888 : const char* aFormulaData[6] = {
6889 : "=IF($List1.A2<>\"\",$List1.A2,\"\")",
6890 : "=IF($List1.A3<>\"\",$List1.A3,\"\")",
6891 : "=IF($List1.A4<>\"\",$List1.A4,\"\")",
6892 : "=IF($List1.A5<>\"\",$List1.A5,\"\")",
6893 : "=IF($List1.A6<>\"\",$List1.A6,\"\")",
6894 : "=IF($List1.A7<>\"\",$List1.A7,\"\")",
6895 1 : };
6896 :
6897 : const char* aTextData[4] = {
6898 : "bob",
6899 : "tim",
6900 : "brian",
6901 : "larry",
6902 1 : };
6903 :
6904 : const char* aResults[ 6 ] = {
6905 : "bob",
6906 : "brian",
6907 : "larry",
6908 : "tim",
6909 : "",
6910 : "",
6911 1 : };
6912 : // insert data to sort
6913 1 : SCROW nStart = 1, nEnd = 4;
6914 5 : for ( SCROW i = nStart; i <= nEnd; ++i )
6915 4 : pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
6916 : // insert forumulas
6917 1 : nStart = 0;
6918 1 : nEnd = SAL_N_ELEMENTS(aFormulaData);
6919 7 : for ( SCROW i = nStart; i < nEnd; ++i )
6920 6 : pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
6921 :
6922 2 : ScSortParam aSortData;
6923 1 : aSortData.nCol1 = 0;
6924 1 : aSortData.nCol2 = 0;
6925 1 : aSortData.nRow1 = 1;
6926 1 : aSortData.nRow2 = 7;
6927 1 : aSortData.maKeyState[0].bDoSort = true;
6928 1 : aSortData.maKeyState[0].nField = 0;
6929 :
6930 1 : pDoc->Sort(0, aSortData, false, NULL);
6931 :
6932 1 : nEnd = SAL_N_ELEMENTS( aResults );
6933 7 : for ( SCROW i = nStart; i < nEnd; ++i )
6934 : {
6935 6 : OUString sResult = pDoc->GetString( 0, i + 1, 0);
6936 6 : CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[ i ] ), sResult );
6937 6 : }
6938 1 : pDoc->DeleteTab(0);
6939 2 : pDoc->DeleteTab(1);
6940 1 : }
6941 :
6942 1 : void Test::testSort()
6943 : {
6944 1 : OUString aTabName1("test1");
6945 1 : m_pDoc->InsertTab(0, aTabName1);
6946 :
6947 1 : ScRange aDataRange;
6948 1 : ScAddress aPos(0,0,0);
6949 : {
6950 : const char* aData[][2] = {
6951 : { "2", "4" },
6952 : { "4", "1" },
6953 : { "1", "2" },
6954 : { "1", "23" },
6955 1 : };
6956 :
6957 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
6958 1 : aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
6959 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
6960 : }
6961 :
6962 : // Insert note in cell B2.
6963 2 : OUString aHello("Hello");
6964 2 : OUString aJimBob("Jim Bob");
6965 1 : ScAddress rAddr(1, 1, 0);
6966 1 : ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
6967 1 : pNote->SetText(rAddr, aHello);
6968 1 : pNote->SetAuthor(aJimBob);
6969 :
6970 2 : ScSortParam aSortData;
6971 1 : aSortData.nCol1 = 1;
6972 1 : aSortData.nCol2 = 1;
6973 1 : aSortData.nRow1 = 0;
6974 1 : aSortData.nRow2 = 2;
6975 1 : aSortData.maKeyState[0].bDoSort = true;
6976 1 : aSortData.maKeyState[0].nField = 1;
6977 1 : aSortData.maKeyState[0].bAscending = true;
6978 :
6979 1 : m_pDoc->Sort(0, aSortData, false, NULL);
6980 1 : double nVal = m_pDoc->GetValue(1,0,0);
6981 1 : ASSERT_DOUBLES_EQUAL(nVal, 1.0);
6982 :
6983 : // check that note is also moved
6984 1 : pNote = m_pDoc->GetNotes(0)->findByAddress( 1, 0 );
6985 1 : CPPUNIT_ASSERT(pNote);
6986 :
6987 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
6988 : {
6989 : // 0 = empty cell
6990 : const char* aData[][1] = {
6991 : { "Title" },
6992 : { 0 },
6993 : { 0 },
6994 : { "12" },
6995 : { "b" },
6996 : { "1" },
6997 : { "9" },
6998 : { "123" }
6999 1 : };
7000 :
7001 1 : aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
7002 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
7003 : }
7004 :
7005 1 : aSortData.nCol1 = aDataRange.aStart.Col();
7006 1 : aSortData.nCol2 = aDataRange.aEnd.Col();
7007 1 : aSortData.nRow1 = aDataRange.aStart.Row();
7008 1 : aSortData.nRow2 = aDataRange.aEnd.Row();
7009 1 : aSortData.bHasHeader = true;
7010 1 : aSortData.maKeyState[0].nField = 0;
7011 1 : m_pDoc->Sort(0, aSortData, false, NULL);
7012 :
7013 : // Title should stay at the top, numbers should be sorted numerically,
7014 : // numbers always come before strings, and empty cells always occur at the
7015 : // end.
7016 1 : CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos));
7017 1 : aPos.IncRow();
7018 1 : CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos));
7019 1 : aPos.IncRow();
7020 1 : CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos));
7021 1 : aPos.IncRow();
7022 1 : CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos));
7023 1 : aPos.IncRow();
7024 1 : CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos));
7025 1 : aPos.IncRow();
7026 1 : CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos));
7027 1 : aPos.IncRow();
7028 1 : CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos));
7029 :
7030 2 : m_pDoc->DeleteTab(0);
7031 1 : }
7032 :
7033 1 : void Test::testShiftCells()
7034 : {
7035 1 : m_pDoc->InsertTab(0, "foo");
7036 :
7037 1 : OUString aTestVal("Some Text");
7038 :
7039 : // Text into cell E5.
7040 1 : m_pDoc->SetString(4, 3, 0, aTestVal);
7041 :
7042 : // Insert cell at D5. This should shift the string cell to right.
7043 1 : m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
7044 2 : OUString aStr = m_pDoc->GetString(5, 3, 0);
7045 1 : CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
7046 1 : CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 3, 4, 3, 4));
7047 :
7048 : // Delete cell D5, to shift the text cell back into D5.
7049 1 : m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
7050 1 : aStr = m_pDoc->GetString(4, 3, 0);
7051 1 : CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
7052 1 : CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 4, 4, 4, 4));
7053 :
7054 2 : m_pDoc->DeleteTab(0);
7055 1 : }
7056 :
7057 1 : void Test::testDeleteRow()
7058 : {
7059 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
7060 1 : OUString aSheet1("Sheet1");
7061 1 : pDoc->InsertTab(0, aSheet1);
7062 :
7063 2 : OUString aHello("Hello");
7064 2 : OUString aJimBob("Jim Bob");
7065 1 : ScAddress rAddr(1, 1, 0);
7066 1 : ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
7067 1 : pNote->SetText(rAddr, aHello);
7068 1 : pNote->SetAuthor(aJimBob);
7069 :
7070 1 : pDoc->DeleteRow(0, 0, MAXCOL, 0, 1, 1);
7071 :
7072 1 : CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
7073 2 : pDoc->DeleteTab(0);
7074 1 : }
7075 :
7076 1 : void Test::testDeleteCol()
7077 : {
7078 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
7079 1 : OUString aSheet1("Sheet1");
7080 1 : pDoc->InsertTab(0, aSheet1);
7081 :
7082 2 : OUString aHello("Hello");
7083 2 : OUString aJimBob("Jim Bob");
7084 1 : ScAddress rAddr(1, 1, 0);
7085 1 : ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
7086 1 : pNote->SetText(rAddr, aHello);
7087 1 : pNote->SetAuthor(aJimBob);
7088 :
7089 1 : pDoc->DeleteCol(0, 0, MAXROW, 0, 1, 1);
7090 :
7091 1 : CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
7092 2 : pDoc->DeleteTab(0);
7093 1 : }
7094 :
7095 1 : void Test::testAnchoredRotatedShape()
7096 : {
7097 1 : OUString aTabName("TestTab");
7098 1 : m_pDoc->InsertTab(0, aTabName);
7099 : SCROW nRow1, nRow2;
7100 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
7101 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
7102 :
7103 1 : m_pDoc->InitDrawLayer();
7104 1 : ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
7105 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
7106 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
7107 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
7108 1 : m_pDoc->SetRowHeightRange( 0, MAXROW, 0, sc::HMMToTwips( 1000 ) );
7109 1 : const long TOLERANCE = 30; //30 hmm
7110 1024 : for ( SCCOL nCol = 0; nCol < MAXCOL; ++nCol )
7111 1023 : m_pDoc->SetColWidth( nCol, 0, sc::HMMToTwips( 1000 ) );
7112 : {
7113 : //Add a rect
7114 1 : Rectangle aRect( 4000, 5000, 10000, 7000 );
7115 :
7116 1 : Rectangle aRotRect( 6000, 3000, 8000, 9000 );
7117 1 : SdrRectObj *pObj = new SdrRectObj(aRect);
7118 1 : pPage->InsertObject(pObj);
7119 1 : Point aRef1(pObj->GetSnapRect().Center());
7120 1 : int nAngle = 9000; //90 deg.
7121 1 : double nSin=sin(nAngle*nPi180);
7122 1 : double nCos=cos(nAngle*nPi180);
7123 1 : pObj->Rotate(aRef1,nAngle,nSin,nCos);
7124 :
7125 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
7126 :
7127 1 : Rectangle aSnap = pObj->GetSnapRect();
7128 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
7129 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
7130 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Left(), aSnap.Left(), TOLERANCE ) );
7131 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Top(), aSnap.Top(), TOLERANCE ) );
7132 :
7133 1 : ScDrawObjData aAnchor;
7134 1 : ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
7135 :
7136 1 : aAnchor.maStart = pData->maStart;
7137 1 : aAnchor.maEnd = pData->maEnd;
7138 :
7139 1 : m_pDoc->SetDrawPageSize(0);
7140 :
7141 : // increase row 5 by 2000 hmm
7142 1 : m_pDoc->SetRowHeight( 5, 0, sc::HMMToTwips( 3000 ) );
7143 : // increase col 6 by 1000 hmm
7144 1 : m_pDoc->SetColWidth( 6, 0, sc::HMMToTwips( 2000 ) );
7145 :
7146 1 : aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
7147 1 : aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
7148 :
7149 1 : m_pDoc->SetDrawPageSize(0);
7150 :
7151 1 : aSnap = pObj->GetSnapRect();
7152 :
7153 : // ensure that width and height have been adjusted accordingly
7154 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
7155 1 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
7156 :
7157 : // ensure that anchor start and end addresses haven't changed
7158 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
7159 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
7160 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
7161 1 : CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
7162 : }
7163 1 : m_pDoc->DeleteTab(0);
7164 1 : }
7165 :
7166 1 : void Test::testCellTextWidth()
7167 : {
7168 1 : m_pDoc->InsertTab(0, "Test");
7169 :
7170 1 : ScAddress aTopCell(0, 0, 0);
7171 :
7172 : // Sheet is empty.
7173 1 : boost::scoped_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
7174 1 : CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
7175 :
7176 : // Sheet only has one cell.
7177 1 : m_pDoc->SetString(0, 0, 0, "Only one cell");
7178 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
7179 1 : CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
7180 1 : SCROW nTestRow = 0;
7181 1 : CPPUNIT_ASSERT_EQUAL(nTestRow, pIter->getPos());
7182 :
7183 : // Setting a text width here should commit it to the column.
7184 1 : sal_uInt16 nTestVal = 432;
7185 1 : pIter->setValue(nTestVal);
7186 1 : CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
7187 :
7188 : // Set values to row 2 through 6.
7189 6 : for (SCROW i = 2; i <= 6; ++i)
7190 5 : m_pDoc->SetString(0, i, 0, "foo");
7191 :
7192 : // Set values to row 10 through 18.
7193 10 : for (SCROW i = 10; i <= 18; ++i)
7194 9 : m_pDoc->SetString(0, i, 0, "foo");
7195 :
7196 : {
7197 : // Full range.
7198 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
7199 1 : SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
7200 1 : size_t n = SAL_N_ELEMENTS(aRows);
7201 16 : for (size_t i = 0; i < n; ++i, pIter->next())
7202 : {
7203 15 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
7204 15 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
7205 : }
7206 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
7207 : }
7208 :
7209 : {
7210 : // Specify start and end rows (6 - 16)
7211 1 : ScAddress aStart = aTopCell;
7212 1 : aStart.SetRow(6);
7213 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
7214 1 : SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
7215 1 : size_t n = SAL_N_ELEMENTS(aRows);
7216 9 : for (size_t i = 0; i < n; ++i, pIter->next())
7217 : {
7218 8 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
7219 8 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
7220 : }
7221 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
7222 : }
7223 :
7224 : // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
7225 1 : clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
7226 :
7227 : {
7228 : // Full range again.
7229 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
7230 1 : SCROW aRows[] = { 0, 2, 18 };
7231 1 : size_t n = SAL_N_ELEMENTS(aRows);
7232 4 : for (size_t i = 0; i < n; ++i, pIter->next())
7233 : {
7234 3 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
7235 3 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
7236 : }
7237 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
7238 : }
7239 :
7240 : // Delete row 2 which shifts all cells below row 2 upward. After this, we
7241 : // should only have cells at rows 0 and 17.
7242 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, MAXTAB, 2, 1);
7243 : {
7244 : // Full range again.
7245 1 : pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
7246 1 : SCROW aRows[] = { 0, 17 };
7247 1 : size_t n = SAL_N_ELEMENTS(aRows);
7248 3 : for (size_t i = 0; i < n; ++i, pIter->next())
7249 : {
7250 2 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
7251 2 : CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
7252 : }
7253 1 : CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
7254 : }
7255 :
7256 1 : m_pDoc->DeleteTab(0);
7257 1 : }
7258 :
7259 1 : void Test::testFormulaGrouping()
7260 : {
7261 1 : if ( !getenv("SC_FORMULAGROUP") )
7262 2 : return;
7263 :
7264 : static const struct {
7265 : const char *pFormula[3];
7266 : bool bGroup[3];
7267 : } aGroupTests[] = {
7268 : { { "=B1", "=B2", "" }, // relative reference
7269 : { true, true, false } },
7270 : { { "=B1", "=B2", "=B3" },
7271 : { true, true, true } },
7272 : { { "=B1", "", "=B3" }, // a gap
7273 : { false, false, false } },
7274 : { { "=$B$1", "=$B$1", "" }, // absolute reference
7275 : { true, true, false } },
7276 : { { "=$Z$10", "=$Z$10", "=$Z$10" },
7277 : { true, true, true } },
7278 : { { "=C1+$Z$10", "=C2+$Z$10", "=C3+$Z$10" }, // mixture
7279 : { true, true, true } },
7280 : { { "=C1+$Z$11", "=C2+$Z$12", "=C3+$Z$12" }, // mixture
7281 : { false, true, true } },
7282 : { { "=SUM(B1)", "", "=SUM(B3)" }, // a gap
7283 : { false, false, false } },
7284 : };
7285 :
7286 0 : m_pDoc->InsertTab( 0, "sheet" );
7287 :
7288 0 : for (size_t i = 0; i < SAL_N_ELEMENTS(aGroupTests); ++i)
7289 : {
7290 0 : for (size_t j = 0; j < SAL_N_ELEMENTS(aGroupTests[0].pFormula); ++j)
7291 : {
7292 0 : OUString aFormula = OUString::createFromAscii(aGroupTests[i].pFormula[j]);
7293 0 : m_pDoc->SetString(0, static_cast<SCROW>(j), 0, aFormula);
7294 0 : }
7295 0 : m_pDoc->RebuildFormulaGroups();
7296 :
7297 0 : for (size_t j = 0; j < SAL_N_ELEMENTS(aGroupTests[0].pFormula); ++j)
7298 : {
7299 0 : ScRefCellValue aCell;
7300 0 : aCell.assign(*m_pDoc, ScAddress(0, static_cast<SCROW>(j), 0));
7301 0 : if (aCell.isEmpty())
7302 : {
7303 0 : CPPUNIT_ASSERT_MESSAGE("invalid empty cell", !aGroupTests[i].bGroup[j]);
7304 0 : continue;
7305 : }
7306 0 : CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", !aCell.isEmpty());
7307 0 : CPPUNIT_ASSERT_MESSAGE("Cell wrong type.",
7308 0 : aCell.meType == CELLTYPE_FORMULA);
7309 0 : ScFormulaCell *pCur = aCell.mpFormula;
7310 :
7311 0 : if( !!pCur->GetCellGroup().get() ^ aGroupTests[i].bGroup[j] )
7312 : {
7313 0 : cout << "expected group test " << i << " at row " << j << " to be "
7314 0 : << aGroupTests[i].bGroup[j] << " but is " << !!pCur->GetCellGroup().get() << endl;
7315 0 : CPPUNIT_FAIL("Failed");
7316 : }
7317 0 : }
7318 : }
7319 : }
7320 :
7321 1 : void Test::testCondFormatINSDEL()
7322 : {
7323 : // fdo#62206
7324 1 : m_pDoc->InsertTab(0, "Test");
7325 1 : ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
7326 :
7327 1 : ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
7328 1 : ScRangeList aRangeList(ScRange(0,0,0,0,3,0));
7329 1 : pFormat->AddRange(aRangeList);
7330 3 : ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
7331 1 : pFormat->AddEntry(pEntry);
7332 :
7333 1 : m_pDoc->AddCondFormatData(pFormat->GetRange(), 0, 1);
7334 1 : pList->InsertNew(pFormat);
7335 :
7336 1 : m_pDoc->InsertCol(0,0,MAXROW,0,0,2);
7337 1 : const ScRangeList& rRange = pFormat->GetRange();
7338 1 : CPPUNIT_ASSERT(rRange == ScRange(2,0,0,2,3,0));
7339 :
7340 2 : OUString aExpr = pEntry->GetExpression(ScAddress(2,0,0), 0);
7341 1 : CPPUNIT_ASSERT_EQUAL(aExpr, OUString("D2"));
7342 :
7343 2 : m_pDoc->DeleteTab(0);
7344 1 : }
7345 :
7346 1 : CPPUNIT_TEST_SUITE_REGISTRATION(Test);
7347 :
7348 : }
7349 :
7350 4 : CPPUNIT_PLUGIN_IMPLEMENT();
7351 :
7352 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|