Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * Version: MPL 1.1 / GPLv3+ / LGPLv3+
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Initial Developer of the Original Code is
16 : * Novell, Inc.
17 : * Portions created by the Initial Developer are Copyright (C) 2010 the
18 : * Initial Developer. All Rights Reserved.
19 : *
20 : * Contributor(s): Michael Meeks <michael.meeks@novell.com>
21 : * Kohei Yoshida <kyoshida@novell.com>
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
25 : * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
26 : * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
27 : * instead of those above.
28 : */
29 : #include <sal/config.h>
30 : #include <test/bootstrapfixture.hxx>
31 :
32 : #include <rtl/strbuf.hxx>
33 : #include <osl/file.hxx>
34 :
35 : #include "scdll.hxx"
36 : #include "document.hxx"
37 : #include "stringutil.hxx"
38 : #include "scmatrix.hxx"
39 : #include "drwlayer.hxx"
40 : #include "scitems.hxx"
41 : #include "reffind.hxx"
42 : #include "markdata.hxx"
43 : #include "clipparam.hxx"
44 : #include "refundo.hxx"
45 : #include "undoblk.hxx"
46 : #include "undotab.hxx"
47 : #include "queryentry.hxx"
48 : #include "postit.hxx"
49 : #include "attrib.hxx"
50 : #include "dbdata.hxx"
51 : #include "reftokenhelper.hxx"
52 : #include "userdat.hxx"
53 :
54 : #include "docsh.hxx"
55 : #include "docfunc.hxx"
56 : #include "dbdocfun.hxx"
57 : #include "funcdesc.hxx"
58 : #include "externalrefmgr.hxx"
59 :
60 : #include "dpshttab.hxx"
61 : #include "dpobject.hxx"
62 : #include "dpsave.hxx"
63 : #include "dpdimsave.hxx"
64 : #include "dpcache.hxx"
65 : #include "dpfilteredcache.hxx"
66 : #include "calcconfig.hxx"
67 : #include "interpre.hxx"
68 :
69 : #include "formula/IFunctionDescription.hxx"
70 :
71 : #include <basegfx/polygon/b2dpolygon.hxx>
72 : #include <editeng/boxitem.hxx>
73 :
74 : #include <svx/svdograf.hxx>
75 : #include <svx/svdpage.hxx>
76 : #include <svx/svdocirc.hxx>
77 : #include <svx/svdopath.hxx>
78 :
79 : #include <sfx2/docfile.hxx>
80 :
81 : #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
82 : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
83 : #include <com/sun/star/sheet/GeneralFunction.hpp>
84 :
85 : #include <iostream>
86 : #include <sstream>
87 : #include <vector>
88 :
89 : #define CALC_DEBUG_OUTPUT 0
90 :
91 : #include "helper/debughelper.hxx"
92 : #include "helper/qahelper.hxx"
93 :
94 : const int indeterminate = 2;
95 :
96 : using namespace ::com::sun::star;
97 : using ::rtl::OUString;
98 : using ::rtl::OUStringBuffer;
99 : using ::std::cout;
100 : using ::std::cerr;
101 : using ::std::endl;
102 : using ::std::vector;
103 :
104 : namespace {
105 :
106 108 : class Test : public test::BootstrapFixture {
107 : public:
108 : Test();
109 :
110 : virtual void setUp();
111 : virtual void tearDown();
112 :
113 : void testCollator();
114 : void testRangeList();
115 : void testInput();
116 : void testCellFunctions();
117 :
118 : /**
119 : * Make sure the SHEETS function gets properly updated during sheet
120 : * insertion and removal.
121 : */
122 : void testSheetsFunc();
123 : void testVolatileFunc();
124 : void testFormulaDepTracking();
125 : void testFuncParam();
126 : void testNamedRange();
127 : void testCSV();
128 : void testMatrix();
129 : void testEnterMixedMatrix();
130 :
131 : /**
132 : * Basic test for pivot tables.
133 : */
134 : void testPivotTable();
135 :
136 : /**
137 : * Test against unwanted automatic format detection on field names and
138 : * field members in pivot tables.
139 : */
140 : void testPivotTableLabels();
141 :
142 : /**
143 : * Make sure that we set cells displaying date values numeric cells,
144 : * rather than text cells. Grouping by date or number functionality
145 : * depends on this.
146 : */
147 : void testPivotTableDateLabels();
148 :
149 : /**
150 : * Test for pivot table's filtering functionality by page fields.
151 : */
152 : void testPivotTableFilters();
153 :
154 : /**
155 : * Test for pivot table's named source range.
156 : */
157 : void testPivotTableNamedSource();
158 :
159 : /**
160 : * Test for pivot table cache. Each dimension in the pivot cache stores
161 : * only unique values that are sorted in ascending order.
162 : */
163 : void testPivotTableCache();
164 :
165 : /**
166 : * Test for pivot table containing data fields that reference the same
167 : * source field but different functions.
168 : */
169 : void testPivotTableDuplicateDataFields();
170 :
171 : void testPivotTableNormalGrouping();
172 : void testPivotTableNumberGrouping();
173 : void testPivotTableDateGrouping();
174 : void testPivotTableEmptyRows();
175 : void testPivotTableTextNumber();
176 :
177 : /**
178 : * Test for checking that pivot table treats strings in a case insensitive
179 : * manner.
180 : */
181 : void testPivotTableCaseInsensitiveStrings();
182 :
183 : /**
184 : * Test for pivot table's handling of double-precision numbers that are
185 : * very close together.
186 : */
187 : void testPivotTableNumStability();
188 :
189 : void testSheetCopy();
190 : void testSheetMove();
191 : void testExternalRef();
192 : void testExternalRefFunctions();
193 : void testDataArea();
194 : void testAutofilter();
195 : void testCopyPaste();
196 : void testMergedCells();
197 : void testUpdateReference();
198 :
199 : /**
200 : * Make sure the sheet streams are invalidated properly.
201 : */
202 : void testStreamValid();
203 :
204 : /**
205 : * Test built-in cell functions to make sure their categories and order
206 : * are correct.
207 : */
208 : void testFunctionLists();
209 :
210 : void testGraphicsInGroup();
211 : void testGraphicsOnSheetMove();
212 :
213 : void testPostIts();
214 :
215 : /**
216 : * Test toggling relative/absolute flag of cell and cell range references.
217 : * This corresponds with hitting Shift-F4 while the cursor is on a formula
218 : * cell.
219 : */
220 : void testToggleRefFlag();
221 :
222 : /**
223 : * Test to make sure correct precedent / dependent cells are obtained when
224 : * preparing to jump to them.
225 : */
226 : void testJumpToPrecedentsDependents();
227 :
228 : void testSetBackgroundColor();
229 : void testRenameTable();
230 :
231 : void testAutoFill();
232 : void testCopyPasteFormulas();
233 : void testCopyPasteFormulasExternalDoc();
234 :
235 : void testFindAreaPosRowDown();
236 : void testFindAreaPosColRight();
237 : void testSort();
238 : void testSortWithFormulaRefs();
239 : void testShiftCells();
240 : void testDeleteRow();
241 : void testDeleteCol();
242 :
243 2 : CPPUNIT_TEST_SUITE(Test);
244 1 : CPPUNIT_TEST(testCollator);
245 1 : CPPUNIT_TEST(testRangeList);
246 1 : CPPUNIT_TEST(testInput);
247 1 : CPPUNIT_TEST(testCellFunctions);
248 1 : CPPUNIT_TEST(testSheetsFunc);
249 1 : CPPUNIT_TEST(testVolatileFunc);
250 1 : CPPUNIT_TEST(testFormulaDepTracking);
251 1 : CPPUNIT_TEST(testFuncParam);
252 1 : CPPUNIT_TEST(testNamedRange);
253 1 : CPPUNIT_TEST(testCSV);
254 1 : CPPUNIT_TEST(testMatrix);
255 1 : CPPUNIT_TEST(testEnterMixedMatrix);
256 1 : CPPUNIT_TEST(testPivotTable);
257 1 : CPPUNIT_TEST(testPivotTableLabels);
258 1 : CPPUNIT_TEST(testPivotTableDateLabels);
259 1 : CPPUNIT_TEST(testPivotTableFilters);
260 1 : CPPUNIT_TEST(testPivotTableNamedSource);
261 1 : CPPUNIT_TEST(testPivotTableCache);
262 1 : CPPUNIT_TEST(testPivotTableDuplicateDataFields);
263 1 : CPPUNIT_TEST(testPivotTableNormalGrouping);
264 1 : CPPUNIT_TEST(testPivotTableNumberGrouping);
265 1 : CPPUNIT_TEST(testPivotTableDateGrouping);
266 1 : CPPUNIT_TEST(testPivotTableEmptyRows);
267 1 : CPPUNIT_TEST(testPivotTableTextNumber);
268 1 : CPPUNIT_TEST(testPivotTableCaseInsensitiveStrings);
269 1 : CPPUNIT_TEST(testPivotTableNumStability);
270 1 : CPPUNIT_TEST(testSheetCopy);
271 1 : CPPUNIT_TEST(testSheetMove);
272 1 : CPPUNIT_TEST(testExternalRef);
273 1 : CPPUNIT_TEST(testExternalRefFunctions);
274 1 : CPPUNIT_TEST(testDataArea);
275 1 : CPPUNIT_TEST(testGraphicsInGroup);
276 1 : CPPUNIT_TEST(testGraphicsOnSheetMove);
277 1 : CPPUNIT_TEST(testPostIts);
278 1 : CPPUNIT_TEST(testStreamValid);
279 1 : CPPUNIT_TEST(testFunctionLists);
280 1 : CPPUNIT_TEST(testToggleRefFlag);
281 1 : CPPUNIT_TEST(testAutofilter);
282 1 : CPPUNIT_TEST(testCopyPaste);
283 1 : CPPUNIT_TEST(testMergedCells);
284 1 : CPPUNIT_TEST(testUpdateReference);
285 1 : CPPUNIT_TEST(testJumpToPrecedentsDependents);
286 1 : CPPUNIT_TEST(testSetBackgroundColor);
287 1 : CPPUNIT_TEST(testRenameTable);
288 1 : CPPUNIT_TEST(testAutoFill);
289 1 : CPPUNIT_TEST(testCopyPasteFormulas);
290 1 : CPPUNIT_TEST(testCopyPasteFormulasExternalDoc);
291 1 : CPPUNIT_TEST(testFindAreaPosRowDown);
292 1 : CPPUNIT_TEST(testFindAreaPosColRight);
293 1 : CPPUNIT_TEST(testSort);
294 1 : CPPUNIT_TEST(testSortWithFormulaRefs);
295 1 : CPPUNIT_TEST(testShiftCells);
296 1 : CPPUNIT_TEST(testDeleteRow);
297 1 : CPPUNIT_TEST(testDeleteCol);
298 2 : CPPUNIT_TEST_SUITE_END();
299 :
300 : private:
301 : ScDocument *m_pDoc;
302 : ScDocShellRef m_xDocShRef;
303 : };
304 :
305 15 : void clearRange(ScDocument* pDoc, const ScRange& rRange)
306 : {
307 15 : ScMarkData aMarkData;
308 15 : aMarkData.SetMarkArea(rRange);
309 : pDoc->DeleteArea(
310 15 : rRange.aStart.Col(), rRange.aStart.Row(),
311 30 : rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, IDF_CONTENTS);
312 15 : }
313 :
314 23 : void printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption)
315 : {
316 23 : SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
317 23 : SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
318 23 : SheetPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1);
319 246 : for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
320 : {
321 857 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
322 : {
323 634 : rtl::OUString aVal;
324 634 : pDoc->GetString(nCol, nRow, rRange.aStart.Tab(), aVal);
325 634 : printer.set(nRow-nRow1, nCol-nCol1, aVal);
326 634 : }
327 : }
328 23 : printer.print(pCaption);
329 23 : }
330 :
331 : template<size_t _Size>
332 12 : ScRange insertRangeData(ScDocument* pDoc, const ScAddress& rPos, const char* aData[][_Size], size_t nRowCount)
333 : {
334 47 : for (size_t i = 0; i < _Size; ++i)
335 : {
336 371 : for (size_t j = 0; j < nRowCount; ++j)
337 : {
338 336 : SCCOL nCol = i + rPos.Col();
339 336 : SCROW nRow = j + rPos.Row();
340 336 : pDoc->SetString(nCol, nRow, rPos.Tab(), OUString(aData[j][i], strlen(aData[j][i]), RTL_TEXTENCODING_UTF8));
341 : }
342 : }
343 :
344 12 : ScRange aRange(rPos);
345 12 : aRange.aEnd.SetCol(rPos.Col()+_Size-1);
346 12 : aRange.aEnd.SetRow(rPos.Row()+nRowCount-1);
347 12 : printRange(pDoc, aRange, "Range data content");
348 12 : return aRange;
349 : }
350 :
351 : /**
352 : * Temporarily switch on/off auto calculation mode.
353 : */
354 : class AutoCalcSwitch
355 : {
356 : ScDocument* mpDoc;
357 : bool mbOldValue;
358 : public:
359 2 : AutoCalcSwitch(ScDocument* pDoc, bool bAutoCalc) : mpDoc(pDoc), mbOldValue(pDoc->GetAutoCalc())
360 : {
361 2 : mpDoc->SetAutoCalc(bAutoCalc);
362 2 : }
363 :
364 2 : ~AutoCalcSwitch()
365 : {
366 2 : mpDoc->SetAutoCalc(mbOldValue);
367 2 : }
368 : };
369 :
370 : /**
371 : * Temporarily set formula grammar.
372 : */
373 : class FormulaGrammarSwitch
374 : {
375 : ScDocument* mpDoc;
376 : formula::FormulaGrammar::Grammar meOldGrammar;
377 : public:
378 2 : FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
379 2 : mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
380 : {
381 2 : mpDoc->SetGrammar(eGrammar);
382 2 : }
383 :
384 2 : ~FormulaGrammarSwitch()
385 : {
386 2 : mpDoc->SetGrammar(meOldGrammar);
387 2 : }
388 : };
389 :
390 54 : Test::Test()
391 54 : : m_pDoc(0)
392 : {
393 54 : }
394 :
395 54 : void Test::setUp()
396 : {
397 54 : BootstrapFixture::setUp();
398 :
399 54 : ScDLL::Init();
400 : m_xDocShRef = new ScDocShell(
401 : SFXMODEL_STANDARD |
402 : SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
403 54 : SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
404 :
405 54 : m_pDoc = m_xDocShRef->GetDocument();
406 54 : }
407 :
408 54 : void Test::tearDown()
409 : {
410 54 : m_xDocShRef.Clear();
411 54 : BootstrapFixture::tearDown();
412 54 : }
413 :
414 1 : void Test::testCollator()
415 : {
416 1 : OUString s1("A");
417 1 : OUString s2("B");
418 1 : CollatorWrapper* p = ScGlobal::GetCollator();
419 1 : sal_Int32 nRes = p->compareString(s1, s2);
420 1 : CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
421 1 : }
422 :
423 1 : void Test::testRangeList()
424 : {
425 1 : m_pDoc->InsertTab(0, "foo");
426 :
427 1 : ScRangeList aRL;
428 1 : aRL.Append(ScRange(1,1,0,3,10,0));
429 1 : CPPUNIT_ASSERT_MESSAGE("List should have one range.", aRL.size() == 1);
430 1 : const ScRange* p = aRL[0];
431 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
432 1 : CPPUNIT_ASSERT_MESSAGE("Wrong range.", p->aStart == ScAddress(1,1,0) && p->aEnd == ScAddress(3,10,0));
433 :
434 : // TODO: Add more tests here.
435 :
436 1 : m_pDoc->DeleteTab(0);
437 1 : }
438 :
439 1 : void Test::testInput()
440 : {
441 1 : rtl::OUString aTabName("foo");
442 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
443 1 : m_pDoc->InsertTab (0, aTabName));
444 :
445 1 : OUString numstr("'10.5");
446 1 : OUString str("'apple'");
447 1 : OUString test;
448 :
449 1 : m_pDoc->SetString(0, 0, 0, numstr);
450 1 : m_pDoc->GetString(0, 0, 0, test);
451 1 : bool bTest = test == "10.5";
452 1 : CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
453 1 : m_pDoc->SetString(0, 0, 0, str);
454 1 : m_pDoc->GetString(0, 0, 0, test);
455 1 : bTest = test == "'apple'";
456 1 : CPPUNIT_ASSERT_MESSAGE("Text content should have retained the first apostrophe.", bTest);
457 :
458 : // Customized string handling policy.
459 1 : ScSetStringParam aParam;
460 1 : aParam.mbDetectNumberFormat = false;
461 1 : aParam.meSetTextNumFormat = ScSetStringParam::Always;
462 1 : aParam.mbHandleApostrophe = false;
463 1 : m_pDoc->SetString(0, 0, 0, "000123", &aParam);
464 1 : m_pDoc->GetString(0, 0, 0, test);
465 1 : CPPUNIT_ASSERT_MESSAGE("Text content should have been treated as string, not number.", test == "000123");
466 :
467 1 : m_pDoc->DeleteTab(0);
468 1 : }
469 :
470 1 : void testFuncSUM(ScDocument* pDoc)
471 : {
472 1 : double val = 1;
473 : double result;
474 1 : pDoc->SetValue (0, 0, 0, val);
475 1 : pDoc->SetValue (0, 1, 0, val);
476 1 : pDoc->SetString (0, 2, 0, rtl::OUString("=SUM(A1:A2)"));
477 1 : pDoc->CalcAll();
478 1 : pDoc->GetValue (0, 2, 0, result);
479 1 : CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 2.0);
480 1 : }
481 :
482 1 : void testFuncPRODUCT(ScDocument* pDoc)
483 : {
484 1 : double val = 1;
485 : double result;
486 1 : pDoc->SetValue(0, 0, 0, val);
487 1 : val = 2;
488 1 : pDoc->SetValue(0, 1, 0, val);
489 1 : val = 3;
490 1 : pDoc->SetValue(0, 2, 0, val);
491 1 : pDoc->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
492 1 : pDoc->CalcAll();
493 1 : pDoc->GetValue(0, 3, 0, result);
494 1 : CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT failed", result == 6.0);
495 :
496 1 : pDoc->SetString(0, 4, 0, OUString("=PRODUCT({1;2;3})"));
497 1 : pDoc->CalcAll();
498 1 : pDoc->GetValue(0, 4, 0, result);
499 1 : CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT with inline array failed", result == 6.0);
500 1 : }
501 :
502 1 : void testFuncN(ScDocument* pDoc)
503 : {
504 : double result;
505 :
506 : // Clear the area first.
507 1 : clearRange(pDoc, ScRange(0, 0, 0, 1, 20, 0));
508 :
509 : // Put values to reference.
510 1 : double val = 0;
511 1 : pDoc->SetValue(0, 0, 0, val);
512 1 : pDoc->SetString(0, 2, 0, OUString("Text"));
513 1 : val = 1;
514 1 : pDoc->SetValue(0, 3, 0, val);
515 1 : val = -1;
516 1 : pDoc->SetValue(0, 4, 0, val);
517 1 : val = 12.3;
518 1 : pDoc->SetValue(0, 5, 0, val);
519 1 : pDoc->SetString(0, 6, 0, OUString("'12.3"));
520 :
521 : // Cell references
522 1 : pDoc->SetString(1, 0, 0, OUString("=N(A1)"));
523 1 : pDoc->SetString(1, 1, 0, OUString("=N(A2)"));
524 1 : pDoc->SetString(1, 2, 0, OUString("=N(A3)"));
525 1 : pDoc->SetString(1, 3, 0, OUString("=N(A4)"));
526 1 : pDoc->SetString(1, 4, 0, OUString("=N(A5)"));
527 1 : pDoc->SetString(1, 5, 0, OUString("=N(A6)"));
528 1 : pDoc->SetString(1, 6, 0, OUString("=N(A9)"));
529 :
530 : // In-line values
531 1 : pDoc->SetString(1, 7, 0, OUString("=N(0)"));
532 1 : pDoc->SetString(1, 8, 0, OUString("=N(1)"));
533 1 : pDoc->SetString(1, 9, 0, OUString("=N(-1)"));
534 1 : pDoc->SetString(1, 10, 0, OUString("=N(123)"));
535 1 : pDoc->SetString(1, 11, 0, OUString("=N(\"\")"));
536 1 : pDoc->SetString(1, 12, 0, OUString("=N(\"12\")"));
537 1 : pDoc->SetString(1, 13, 0, OUString("=N(\"foo\")"));
538 :
539 : // Range references
540 1 : pDoc->SetString(2, 2, 0, OUString("=N(A1:A8)"));
541 1 : pDoc->SetString(2, 3, 0, OUString("=N(A1:A8)"));
542 1 : pDoc->SetString(2, 4, 0, OUString("=N(A1:A8)"));
543 1 : pDoc->SetString(2, 5, 0, OUString("=N(A1:A8)"));
544 :
545 : // Calculate and check the results.
546 1 : pDoc->CalcAll();
547 : double checks1[] = {
548 : 0, 0, 0, 1, -1, 12.3, 0, // cell reference
549 : 0, 1, -1, 123, 0, 0, 0 // in-line values
550 1 : };
551 15 : for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
552 : {
553 14 : pDoc->GetValue(1, i, 0, result);
554 14 : bool bGood = result == checks1[i];
555 14 : if (!bGood)
556 : {
557 0 : cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
558 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
559 : }
560 : }
561 : double checks2[] = {
562 : 0, 1, -1, 12.3 // range references
563 1 : };
564 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
565 : {
566 4 : pDoc->GetValue(1, i+2, 0, result);
567 4 : bool bGood = result == checks2[i];
568 4 : if (!bGood)
569 : {
570 0 : cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
571 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
572 : }
573 : }
574 1 : }
575 :
576 1 : void testFuncCOUNTIF(ScDocument* pDoc)
577 : {
578 : // COUNTIF (test case adopted from OOo i#36381)
579 :
580 : // Empty A1:A39 first.
581 1 : clearRange(pDoc, ScRange(0, 0, 0, 0, 40, 0));
582 :
583 : // Raw data (rows 1 through 9)
584 : const char* aData[] = {
585 : "1999",
586 : "2000",
587 : "0",
588 : "0",
589 : "0",
590 : "2002",
591 : "2001",
592 : "X",
593 : "2002"
594 1 : };
595 :
596 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
597 10 : for (SCROW i = 0; i < nRows; ++i)
598 9 : pDoc->SetString(0, i, 0, rtl::OUString::createFromAscii(aData[i]));
599 :
600 1 : printRange(pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
601 :
602 : // formulas and results
603 : struct {
604 : const char* pFormula; double fResult;
605 : } aChecks[] = {
606 : { "=COUNTIF(A1:A12;1999)", 1 },
607 : { "=COUNTIF(A1:A12;2002)", 2 },
608 : { "=COUNTIF(A1:A12;1998)", 0 },
609 : { "=COUNTIF(A1:A12;\">=1999\")", 5 },
610 : { "=COUNTIF(A1:A12;\">1999\")", 4 },
611 : { "=COUNTIF(A1:A12;\"<2001\")", 5 },
612 : { "=COUNTIF(A1:A12;\">0\")", 5 },
613 : { "=COUNTIF(A1:A12;\">=0\")", 8 },
614 : { "=COUNTIF(A1:A12;0)", 3 },
615 : { "=COUNTIF(A1:A12;\"X\")", 1 },
616 : { "=COUNTIF(A1:A12;)", 3 }
617 1 : };
618 :
619 1 : nRows = SAL_N_ELEMENTS(aChecks);
620 12 : for (SCROW i = 0; i < nRows; ++i)
621 : {
622 11 : SCROW nRow = 20 + i;
623 11 : pDoc->SetString(0, nRow, 0, rtl::OUString::createFromAscii(aChecks[i].pFormula));
624 : }
625 1 : pDoc->CalcAll();
626 :
627 12 : for (SCROW i = 0; i < nRows; ++i)
628 : {
629 : double result;
630 11 : SCROW nRow = 20 + i;
631 11 : pDoc->GetValue(0, nRow, 0, result);
632 11 : bool bGood = result == aChecks[i].fResult;
633 11 : if (!bGood)
634 : {
635 0 : cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
636 0 : << " expected=" << aChecks[i].fResult << " actual=" << result << endl;
637 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
638 : }
639 : }
640 :
641 : // Don't count empty strings when searching for a number.
642 :
643 : // Clear A1:A2.
644 1 : clearRange(pDoc, ScRange(0, 0, 0, 0, 1, 0));
645 :
646 1 : pDoc->SetString(0, 0, 0, rtl::OUString("=\"\""));
647 1 : pDoc->SetString(0, 1, 0, rtl::OUString("=COUNTIF(A1;1)"));
648 1 : pDoc->CalcAll();
649 :
650 1 : double result = pDoc->GetValue(0, 1, 0);
651 1 : CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
652 1 : }
653 :
654 1 : void testFuncVLOOKUP(ScDocument* pDoc)
655 : {
656 : // VLOOKUP
657 :
658 : // Clear A1:F40.
659 1 : clearRange(pDoc, ScRange(0, 0, 0, 5, 39, 0));
660 :
661 : // Raw data
662 : const char* aData[][2] = {
663 : { "Key", "Val" },
664 : { "10", "3" },
665 : { "20", "4" },
666 : { "30", "5" },
667 : { "40", "6" },
668 : { "50", "7" },
669 : { "60", "8" },
670 : { "70", "9" },
671 : { "B", "10" },
672 : { "B", "11" },
673 : { "C", "12" },
674 : { "D", "13" },
675 : { "E", "14" },
676 : { "F", "15" },
677 : { 0, 0 } // terminator
678 1 : };
679 :
680 : // Insert raw data into A1:B14.
681 15 : for (SCROW i = 0; aData[i][0]; ++i)
682 : {
683 14 : pDoc->SetString(0, i, 0, rtl::OUString::createFromAscii(aData[i][0]));
684 14 : pDoc->SetString(1, i, 0, rtl::OUString::createFromAscii(aData[i][1]));
685 : }
686 :
687 1 : printRange(pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
688 :
689 : // Formula data
690 : struct {
691 : const char* pLookup; const char* pFormula; const char* pRes;
692 : } aChecks[] = {
693 : { "Lookup", "Formula", 0 },
694 : { "12", "=VLOOKUP(D2;A2:B14;2;1)", "3" },
695 : { "29", "=VLOOKUP(D3;A2:B14;2;1)", "4" },
696 : { "31", "=VLOOKUP(D4;A2:B14;2;1)", "5" },
697 : { "45", "=VLOOKUP(D5;A2:B14;2;1)", "6" },
698 : { "56", "=VLOOKUP(D6;A2:B14;2;1)", "7" },
699 : { "65", "=VLOOKUP(D7;A2:B14;2;1)", "8" },
700 : { "78", "=VLOOKUP(D8;A2:B14;2;1)", "9" },
701 : { "Andy", "=VLOOKUP(D9;A2:B14;2;1)", "#N/A" },
702 : { "Bruce", "=VLOOKUP(D10;A2:B14;2;1)", "11" },
703 : { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)", "12" },
704 : { "David", "=VLOOKUP(D12;A2:B14;2;1)", "13" },
705 : { "Edward", "=VLOOKUP(D13;A2:B14;2;1)", "14" },
706 : { "Frank", "=VLOOKUP(D14;A2:B14;2;1)", "15" },
707 : { "Henry", "=VLOOKUP(D15;A2:B14;2;1)", "15" },
708 : { "100", "=VLOOKUP(D16;A2:B14;2;1)", "9" },
709 : { "1000", "=VLOOKUP(D17;A2:B14;2;1)", "9" },
710 : { "Zena", "=VLOOKUP(D18;A2:B14;2;1)", "15" }
711 1 : };
712 :
713 : // Insert formula data into D1:E18.
714 19 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
715 : {
716 18 : pDoc->SetString(3, i, 0, rtl::OUString::createFromAscii(aChecks[i].pLookup));
717 18 : pDoc->SetString(4, i, 0, rtl::OUString::createFromAscii(aChecks[i].pFormula));
718 : }
719 1 : pDoc->CalcAll();
720 1 : printRange(pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
721 :
722 : // Verify results.
723 19 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
724 : {
725 18 : if (i == 0)
726 : // Skip the header row.
727 1 : continue;
728 :
729 17 : rtl::OUString aRes;
730 17 : pDoc->GetString(4, i, 0, aRes);
731 17 : bool bGood = aRes.equalsAscii(aChecks[i].pRes);
732 17 : if (!bGood)
733 : {
734 0 : cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
735 0 : << "' expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
736 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
737 : }
738 17 : }
739 1 : }
740 :
741 : struct NumStrCheck {
742 : double fVal;
743 : const char* pRes;
744 : };
745 :
746 : struct StrStrCheck {
747 : const char* pVal;
748 : const char* pRes;
749 : };
750 :
751 : template<size_t _DataSize, size_t _FormulaSize, int _Type>
752 2 : void runTestMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
753 : {
754 2 : size_t nDataSize = _DataSize;
755 26 : for (size_t i = 0; i < nDataSize; ++i)
756 24 : pDoc->SetString(0, i, 0, rtl::OUString::createFromAscii(aData[i]));
757 :
758 33 : for (size_t i = 0; i < _FormulaSize; ++i)
759 : {
760 31 : pDoc->SetString(1, i, 0, rtl::OUString::createFromAscii(aChecks[i].pVal));
761 :
762 31 : rtl::OUStringBuffer aBuf;
763 31 : aBuf.appendAscii("=MATCH(B");
764 31 : aBuf.append(static_cast<sal_Int32>(i+1));
765 31 : aBuf.appendAscii(";A1:A");
766 31 : aBuf.append(static_cast<sal_Int32>(nDataSize));
767 31 : aBuf.appendAscii(";");
768 31 : aBuf.append(static_cast<sal_Int32>(_Type));
769 31 : aBuf.appendAscii(")");
770 31 : rtl::OUString aFormula = aBuf.makeStringAndClear();
771 31 : pDoc->SetString(2, i, 0, aFormula);
772 : }
773 :
774 2 : pDoc->CalcAll();
775 2 : printRange(pDoc, ScRange(0, 0, 0, 2, _FormulaSize-1, 0), "MATCH");
776 :
777 : // verify the results.
778 33 : for (size_t i = 0; i < _FormulaSize; ++i)
779 : {
780 31 : rtl::OUString aStr;
781 31 : pDoc->GetString(2, i, 0, aStr);
782 31 : if (!aStr.equalsAscii(aChecks[i].pRes))
783 : {
784 0 : cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
785 : << " criterion='" << aChecks[i].pVal << "'" << endl;
786 0 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
787 : }
788 : }
789 2 : }
790 :
791 1 : void testFuncMATCH(ScDocument* pDoc)
792 : {
793 1 : clearRange(pDoc, ScRange(0, 0, 0, 4, 40, 0));
794 : {
795 : // Ascending in-exact match
796 :
797 : // data range (A1:A9)
798 : const char* aData[] = {
799 : "1",
800 : "2",
801 : "3",
802 : "4",
803 : "5",
804 : "6",
805 : "7",
806 : "8",
807 : "9",
808 : "B",
809 : "B",
810 : "C",
811 1 : };
812 :
813 : // formula (B1:C12)
814 : StrStrCheck aChecks[] = {
815 : { "0.8", "#N/A" },
816 : { "1.2", "1" },
817 : { "2.3", "2" },
818 : { "3.9", "3" },
819 : { "4.1", "4" },
820 : { "5.99", "5" },
821 : { "6.1", "6" },
822 : { "7.2", "7" },
823 : { "8.569", "8" },
824 : { "9.59", "9" },
825 : { "10", "9" },
826 : { "100", "9" },
827 : { "Andy", "#N/A" },
828 : { "Bruce", "11" },
829 : { "Charlie", "12" }
830 1 : };
831 :
832 1 : runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(pDoc, aData, aChecks);
833 : }
834 :
835 : {
836 : // Descending in-exact match
837 :
838 : // data range (A1:A9)
839 : const char* aData[] = {
840 : "D",
841 : "C",
842 : "B",
843 : "9",
844 : "8",
845 : "7",
846 : "6",
847 : "5",
848 : "4",
849 : "3",
850 : "2",
851 : "1"
852 1 : };
853 :
854 : // formula (B1:C12)
855 : StrStrCheck aChecks[] = {
856 : { "10", "#N/A" },
857 : { "8.9", "4" },
858 : { "7.8", "5" },
859 : { "6.7", "6" },
860 : { "5.5", "7" },
861 : { "4.6", "8" },
862 : { "3.3", "9" },
863 : { "2.2", "10" },
864 : { "1.1", "11" },
865 : { "0.8", "12" },
866 : { "0", "12" },
867 : { "-2", "12" },
868 : { "Andy", "3" },
869 : { "Bruce", "2" },
870 : { "Charlie", "1" },
871 : { "David", "#N/A" }
872 1 : };
873 :
874 1 : runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(pDoc, aData, aChecks);
875 : }
876 1 : }
877 :
878 1 : void testFuncCELL(ScDocument* pDoc)
879 : {
880 1 : clearRange(pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
881 :
882 : {
883 1 : const char* pContent = "Some random text";
884 1 : pDoc->SetString(2, 9, 0, rtl::OUString::createFromAscii(pContent)); // Set this value to C10.
885 1 : double val = 1.2;
886 1 : pDoc->SetValue(2, 0, 0, val); // Set numeric value to C1;
887 :
888 : // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
889 : StrStrCheck aChecks[] = {
890 : { "=CELL(\"COL\";C10)", "3" },
891 : { "=CELL(\"ROW\";C10)", "10" },
892 : { "=CELL(\"SHEET\";C10)", "1" },
893 : { "=CELL(\"ADDRESS\";C10)", "$C$10" },
894 : { "=CELL(\"CONTENTS\";C10)", pContent },
895 : { "=CELL(\"COLOR\";C10)", "0" },
896 : { "=CELL(\"TYPE\";C9)", "b" },
897 : { "=CELL(\"TYPE\";C10)", "l" },
898 : { "=CELL(\"TYPE\";C1)", "v" },
899 : { "=CELL(\"PARENTHESES\";C10)", "0" }
900 1 : };
901 :
902 11 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
903 10 : pDoc->SetString(0, i, 0, rtl::OUString::createFromAscii(aChecks[i].pVal));
904 1 : pDoc->CalcAll();
905 :
906 11 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
907 : {
908 10 : rtl::OUString aVal = pDoc->GetString(0, i, 0);
909 10 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
910 10 : }
911 : }
912 1 : }
913 :
914 : /** See also test case document fdo#44456 sheet cpearson */
915 1 : void testFuncDATEDIF( ScDocument* pDoc )
916 : {
917 : const char* aData[][5] = {
918 : { "2007-01-01", "2007-01-10", "d", "9", "=DATEDIF(A1;B1;C1)" } ,
919 : { "2007-01-01", "2007-01-31", "m", "0", "=DATEDIF(A2;B2;C2)" } ,
920 : { "2007-01-01", "2007-02-01", "m", "1", "=DATEDIF(A3;B3;C3)" } ,
921 : { "2007-01-01", "2007-02-28", "m", "1", "=DATEDIF(A4;B4;C4)" } ,
922 : { "2007-01-01", "2007-12-31", "d", "364", "=DATEDIF(A5;B5;C5)" } ,
923 : { "2007-01-01", "2007-01-31", "y", "0", "=DATEDIF(A6;B6;C6)" } ,
924 : { "2007-01-01", "2008-07-01", "d", "547", "=DATEDIF(A7;B7;C7)" } ,
925 : { "2007-01-01", "2008-07-01", "m", "18", "=DATEDIF(A8;B8;C8)" } ,
926 : { "2007-01-01", "2008-07-01", "ym", "6", "=DATEDIF(A9;B9;C9)" } ,
927 : { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
928 : { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
929 : { "2007-01-01", "2007-01-31", "md", "30", "=DATEDIF(A12;B12;C12)" } ,
930 : { "2007-02-01", "2009-03-01", "md", "0", "=DATEDIF(A13;B13;C13)" } ,
931 : { "2008-02-01", "2009-03-01", "md", "0", "=DATEDIF(A14;B14;C14)" } ,
932 : { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" } // fail date1 > date2
933 1 : };
934 :
935 1 : clearRange( pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
936 1 : ScAddress aPos(0,0,0);
937 1 : ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
938 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
939 :
940 1 : pDoc->CalcAll();
941 :
942 16 : for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
943 : {
944 15 : rtl::OUString aVal = pDoc->GetString( 4, i, 0);
945 : //std::cout << "row "<< i << ": " << rtl::OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
946 15 : CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
947 15 : }
948 1 : }
949 :
950 1 : void testFuncINDIRECT(ScDocument* pDoc)
951 : {
952 1 : clearRange(pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
953 1 : rtl::OUString aTabName;
954 1 : bool bGood = pDoc->GetName(0, aTabName);
955 1 : CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
956 :
957 1 : rtl::OUString aTest = "Test", aRefErr = "#REF!";
958 1 : pDoc->SetString(0, 10, 0, aTest);
959 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", pDoc->GetString(0,10,0) == aTest);
960 :
961 1 : rtl::OUString aPrefix = "=INDIRECT(\"";
962 :
963 1 : rtl::OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
964 1 : pDoc->SetString(0, 0, 0, aFormula);
965 1 : aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
966 1 : pDoc->SetString(0, 1, 0, aFormula);
967 1 : aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
968 1 : pDoc->SetString(0, 2, 0, aFormula);
969 1 : aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
970 1 : pDoc->SetString(0, 3, 0, aFormula);
971 :
972 1 : pDoc->CalcAll();
973 : {
974 : // Default is to use the current formula syntax, which is Calc A1.
975 : const rtl::OUString* aChecks[] = {
976 : &aTest, &aRefErr, &aRefErr, &aTest
977 1 : };
978 :
979 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
980 : {
981 4 : rtl::OUString aVal = pDoc->GetString(0, i, 0);
982 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
983 4 : }
984 : }
985 :
986 1 : ScCalcConfig aConfig;
987 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_OOO;
988 1 : ScInterpreter::SetGlobalConfig(aConfig);
989 1 : pDoc->CalcAll();
990 : {
991 : // Explicit Calc A1 syntax
992 : const rtl::OUString* aChecks[] = {
993 : &aTest, &aRefErr, &aRefErr, &aTest
994 1 : };
995 :
996 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
997 : {
998 4 : rtl::OUString aVal = pDoc->GetString(0, i, 0);
999 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1000 4 : }
1001 : }
1002 :
1003 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_A1;
1004 1 : ScInterpreter::SetGlobalConfig(aConfig);
1005 1 : pDoc->CalcAll();
1006 : {
1007 : // Excel A1 syntax
1008 : const rtl::OUString* aChecks[] = {
1009 : &aRefErr, &aTest, &aRefErr, &aTest
1010 1 : };
1011 :
1012 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1013 : {
1014 4 : rtl::OUString aVal = pDoc->GetString(0, i, 0);
1015 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1016 4 : }
1017 : }
1018 :
1019 1 : aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_R1C1;
1020 1 : ScInterpreter::SetGlobalConfig(aConfig);
1021 1 : pDoc->CalcAll();
1022 : {
1023 : // Excel R1C1 syntax
1024 : const rtl::OUString* aChecks[] = {
1025 : &aRefErr, &aRefErr, &aTest, &aTest
1026 1 : };
1027 :
1028 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
1029 : {
1030 4 : rtl::OUString aVal = pDoc->GetString(0, i, 0);
1031 4 : CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
1032 4 : }
1033 1 : }
1034 1 : }
1035 :
1036 1 : void Test::testCellFunctions()
1037 : {
1038 1 : rtl::OUString aTabName("foo");
1039 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1040 1 : m_pDoc->InsertTab (0, aTabName));
1041 :
1042 1 : testFuncSUM(m_pDoc);
1043 1 : testFuncPRODUCT(m_pDoc);
1044 1 : testFuncN(m_pDoc);
1045 1 : testFuncCOUNTIF(m_pDoc);
1046 1 : testFuncVLOOKUP(m_pDoc);
1047 1 : testFuncMATCH(m_pDoc);
1048 1 : testFuncCELL(m_pDoc);
1049 1 : testFuncDATEDIF(m_pDoc);
1050 1 : testFuncINDIRECT(m_pDoc);
1051 :
1052 1 : m_pDoc->DeleteTab(0);
1053 1 : }
1054 :
1055 1 : void Test::testSheetsFunc()
1056 : {
1057 1 : rtl::OUString aTabName1("test1");
1058 1 : rtl::OUString aTabName2("test2");
1059 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1060 1 : m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
1061 :
1062 1 : m_pDoc->SetString(0, 0, 0, OUString("=SHEETS()"));
1063 1 : m_pDoc->CalcFormulaTree(false, false);
1064 : double original;
1065 1 : m_pDoc->GetValue(0, 0, 0, original);
1066 :
1067 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
1068 1 : static_cast<SCTAB>(original) == m_pDoc->GetTableCount());
1069 :
1070 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1071 1 : m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
1072 :
1073 : double modified;
1074 1 : m_pDoc->GetValue(0, 0, 0, modified);
1075 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
1076 1 : modified - original == 1.0);
1077 :
1078 1 : SCTAB nTabCount = m_pDoc->GetTableCount();
1079 1 : m_pDoc->DeleteTab(--nTabCount);
1080 :
1081 1 : m_pDoc->GetValue(0, 0, 0, modified);
1082 2 : CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
1083 1 : modified - original == 0.0);
1084 :
1085 1 : m_pDoc->DeleteTab(--nTabCount);
1086 1 : }
1087 :
1088 1 : void Test::testVolatileFunc()
1089 : {
1090 1 : rtl::OUString aTabName("foo");
1091 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1092 1 : m_pDoc->InsertTab (0, aTabName));
1093 :
1094 1 : double val = 1;
1095 1 : m_pDoc->SetValue(0, 0, 0, val);
1096 1 : m_pDoc->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
1097 : double now1;
1098 1 : m_pDoc->GetValue(0, 1, 0, now1);
1099 1 : CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
1100 :
1101 1 : val = 0;
1102 1 : m_pDoc->SetValue(0, 0, 0, val);
1103 1 : m_pDoc->CalcFormulaTree(false, false);
1104 : double zero;
1105 1 : m_pDoc->GetValue(0, 1, 0, zero);
1106 1 : CPPUNIT_ASSERT_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", zero == 0.0);
1107 :
1108 1 : val = 1;
1109 1 : m_pDoc->SetValue(0, 0, 0, val);
1110 1 : m_pDoc->CalcFormulaTree(false, false);
1111 : double now2;
1112 1 : m_pDoc->GetValue(0, 1, 0, now2);
1113 1 : CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
1114 :
1115 1 : m_pDoc->DeleteTab(0);
1116 1 : }
1117 :
1118 1 : void Test::testFormulaDepTracking()
1119 : {
1120 1 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
1121 :
1122 1 : AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation.
1123 :
1124 : // B2 listens on D2.
1125 1 : m_pDoc->SetString(1, 1, 0, "=D2");
1126 1 : double val = -999.0; // dummy initial value
1127 1 : m_pDoc->GetValue(1, 1, 0, val);
1128 1 : CPPUNIT_ASSERT_MESSAGE("Referencing an empty cell should yield zero.", val == 0.0);
1129 :
1130 : // Changing the value of D2 should trigger recalculation of B2.
1131 1 : m_pDoc->SetValue(3, 1, 0, 1.1);
1132 1 : m_pDoc->GetValue(1, 1, 0, val);
1133 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 1.1);
1134 :
1135 : // And again.
1136 1 : m_pDoc->SetValue(3, 1, 0, 2.2);
1137 1 : m_pDoc->GetValue(1, 1, 0, val);
1138 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 2.2);
1139 :
1140 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1141 :
1142 : // Now, let's test the range dependency tracking.
1143 :
1144 : // B2 listens on D2:E6.
1145 1 : m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
1146 1 : m_pDoc->GetValue(1, 1, 0, val);
1147 1 : CPPUNIT_ASSERT_MESSAGE("Summing an empty range should yield zero.", val == 0.0);
1148 :
1149 : // Set value to E3. This should trigger recalc on B2.
1150 1 : m_pDoc->SetValue(4, 2, 0, 2.4);
1151 1 : m_pDoc->GetValue(1, 1, 0, val);
1152 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", val == 2.4);
1153 :
1154 : // Set value to D5 to trigger recalc again. Note that this causes an
1155 : // addition of 1.2 + 2.4 which is subject to binary floating point
1156 : // rounding error. We need to use approxEqual to assess its value.
1157 :
1158 1 : m_pDoc->SetValue(3, 4, 0, 1.2);
1159 1 : m_pDoc->GetValue(1, 1, 0, val);
1160 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
1161 :
1162 : // Change the value of D2 (boundary case).
1163 1 : m_pDoc->SetValue(3, 1, 0, 1.0);
1164 1 : m_pDoc->GetValue(1, 1, 0, val);
1165 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
1166 :
1167 : // Change the value of E6 (another boundary case).
1168 1 : m_pDoc->SetValue(4, 5, 0, 2.0);
1169 1 : m_pDoc->GetValue(1, 1, 0, val);
1170 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
1171 :
1172 : // Change the value of D6 (another boundary case).
1173 1 : m_pDoc->SetValue(3, 5, 0, 3.0);
1174 1 : m_pDoc->GetValue(1, 1, 0, val);
1175 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
1176 :
1177 : // Change the value of E2 (another boundary case).
1178 1 : m_pDoc->SetValue(4, 1, 0, 0.4);
1179 1 : m_pDoc->GetValue(1, 1, 0, val);
1180 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
1181 :
1182 : // Change the existing non-empty value cell (E2).
1183 1 : m_pDoc->SetValue(4, 1, 0, 2.4);
1184 1 : m_pDoc->GetValue(1, 1, 0, val);
1185 1 : CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
1186 :
1187 1 : clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
1188 :
1189 : // Now, column-based dependency tracking. We now switch to the R1C1
1190 : // syntax which is easier to use for repeated relative references.
1191 :
1192 1 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1193 :
1194 1 : val = 0.0;
1195 10 : for (SCROW nRow = 1; nRow <= 9; ++nRow)
1196 : {
1197 : // Static value in column 1.
1198 9 : m_pDoc->SetValue(0, nRow, 0, ++val);
1199 :
1200 : // Formula in column 2 that references cell to the left.
1201 9 : m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
1202 :
1203 : // Formula in column 3 that references cell to the left.
1204 9 : m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
1205 : }
1206 :
1207 : // Check formula values.
1208 1 : val = 0.0;
1209 10 : for (SCROW nRow = 1; nRow <= 9; ++nRow)
1210 : {
1211 9 : ++val;
1212 9 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, nRow, 0) == val);
1213 9 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, nRow, 0) == val*2.0);
1214 : }
1215 :
1216 : // Intentionally insert a formula in column 1. This will break column 1's
1217 : // uniformity of consisting only of static value cells.
1218 1 : m_pDoc->SetString(0, 4, 0, "=R2C3");
1219 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(0, 4, 0) == 2.0);
1220 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, 4, 0) == 2.0);
1221 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, 4, 0) == 4.0);
1222 :
1223 1 : m_pDoc->DeleteTab(0);
1224 1 : }
1225 :
1226 1 : void Test::testFuncParam()
1227 : {
1228 1 : rtl::OUString aTabName("foo");
1229 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1230 1 : m_pDoc->InsertTab (0, aTabName));
1231 :
1232 : // First, the normal case, with no missing parameters.
1233 1 : m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;2;3)"));
1234 1 : m_pDoc->CalcFormulaTree(false, false);
1235 : double val;
1236 1 : m_pDoc->GetValue(0, 0, 0, val);
1237 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 2);
1238 :
1239 : // Now function with missing parameters. Missing values should be treated
1240 : // as zeros.
1241 1 : m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;;;)"));
1242 1 : m_pDoc->CalcFormulaTree(false, false);
1243 1 : m_pDoc->GetValue(0, 0, 0, val);
1244 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 0.25);
1245 :
1246 : // Conversion of string to numeric argument.
1247 1 : m_pDoc->SetString(0, 0, 0, OUString("=\"\"+3")); // empty string
1248 1 : m_pDoc->SetString(0, 1, 0, OUString("=\" \"+3")); // only blank
1249 1 : m_pDoc->SetString(0, 2, 0, OUString("=\" 4 \"+3")); // number in blanks
1250 1 : m_pDoc->SetString(0, 3, 0, OUString("=\" x \"+3")); // non-numeric => #VALUE! error
1251 :
1252 1 : rtl::OUString aVal;
1253 1 : ScCalcConfig aConfig;
1254 :
1255 : // With "Empty string as zero" option.
1256 1 : aConfig.mbEmptyStringAsZero = true;
1257 1 : ScInterpreter::SetGlobalConfig(aConfig);
1258 1 : m_pDoc->CalcAll();
1259 1 : m_pDoc->GetValue(0, 0, 0, val);
1260 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1261 1 : m_pDoc->GetValue(0, 1, 0, val);
1262 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
1263 1 : m_pDoc->GetValue(0, 2, 0, val);
1264 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
1265 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1266 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1267 :
1268 : // Without "Empty string as zero" option.
1269 1 : aConfig.mbEmptyStringAsZero = false;
1270 1 : ScInterpreter::SetGlobalConfig(aConfig);
1271 1 : m_pDoc->CalcAll();
1272 1 : aVal = m_pDoc->GetString( 0, 0, 0);
1273 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1274 1 : aVal = m_pDoc->GetString( 0, 1, 0);
1275 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1276 1 : m_pDoc->GetValue(0, 2, 0, val);
1277 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
1278 1 : aVal = m_pDoc->GetString( 0, 3, 0);
1279 1 : CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
1280 :
1281 1 : m_pDoc->DeleteTab(0);
1282 1 : }
1283 :
1284 1 : void Test::testNamedRange()
1285 : {
1286 : struct {
1287 : const char* pName; const char* pExpr; sal_uInt16 nIndex;
1288 : } aNames[] = {
1289 : { "Divisor", "$Sheet1.$A$1:$A$1048576", 1 },
1290 : { "MyRange1", "$Sheet1.$A$1:$A$100", 2 },
1291 : { "MyRange2", "$Sheet1.$B$1:$B$100", 3 },
1292 : { "MyRange3", "$Sheet1.$C$1:$C$100", 4 }
1293 1 : };
1294 :
1295 1 : rtl::OUString aTabName("Sheet1");
1296 2 : CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
1297 1 : m_pDoc->InsertTab (0, aTabName));
1298 :
1299 1 : m_pDoc->SetValue (0, 0, 0, 101);
1300 :
1301 1 : ScAddress aA1(0, 0, 0);
1302 1 : ScRangeName* pNewRanges = new ScRangeName();
1303 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1304 : {
1305 : ScRangeData* pNew = new ScRangeData(
1306 : m_pDoc,
1307 : rtl::OUString::createFromAscii(aNames[i].pName),
1308 : rtl::OUString::createFromAscii(aNames[i].pExpr),
1309 4 : aA1, 0, formula::FormulaGrammar::GRAM_ENGLISH);
1310 4 : pNew->SetIndex(aNames[i].nIndex);
1311 4 : bool bSuccess = pNewRanges->insert(pNew);
1312 4 : CPPUNIT_ASSERT_MESSAGE ("insertion failed", bSuccess);
1313 : }
1314 :
1315 : // Make sure the index lookup does the right thing.
1316 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1317 : {
1318 4 : const ScRangeData* p = pNewRanges->findByIndex(aNames[i].nIndex);
1319 4 : CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
1320 4 : rtl::OUString aName = p->GetName();
1321 4 : CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].pName));
1322 4 : }
1323 :
1324 : // Test usage in formula expression.
1325 1 : m_pDoc->SetRangeName(pNewRanges);
1326 1 : m_pDoc->SetString (1, 0, 0, rtl::OUString("=A1/Divisor"));
1327 1 : m_pDoc->CalcAll();
1328 :
1329 : double result;
1330 1 : m_pDoc->GetValue (1, 0, 0, result);
1331 1 : CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 1.0);
1332 :
1333 : // Test copy-ability of range names.
1334 1 : ScRangeName* pCopiedRanges = new ScRangeName(*pNewRanges);
1335 1 : m_pDoc->SetRangeName(pCopiedRanges);
1336 : // Make sure the index lookup still works.
1337 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
1338 : {
1339 4 : const ScRangeData* p = pCopiedRanges->findByIndex(aNames[i].nIndex);
1340 4 : CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
1341 4 : rtl::OUString aName = p->GetName();
1342 4 : CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].pName));
1343 4 : }
1344 :
1345 1 : m_pDoc->SetRangeName(NULL); // Delete the names.
1346 1 : m_pDoc->DeleteTab(0);
1347 1 : }
1348 :
1349 1 : void Test::testCSV()
1350 : {
1351 1 : const int English = 0, European = 1;
1352 : struct {
1353 : const char *pStr; int eSep; bool bResult; double nValue;
1354 : } aTests[] = {
1355 : { "foo", English, false, 0.0 },
1356 : { "1.0", English, true, 1.0 },
1357 : { "1,0", English, false, 0.0 },
1358 : { "1.0", European, false, 0.0 },
1359 : { "1.000", European, true, 1000.0 },
1360 : { "1,000", European, true, 1.0 },
1361 : { "1.000", English, true, 1.0 },
1362 : { "1,000", English, true, 1000.0 },
1363 : { " 1.0", English, true, 1.0 },
1364 : { " 1.0 ", English, true, 1.0 },
1365 : { "1.0 ", European, false, 0.0 },
1366 : { "1.000", European, true, 1000.0 },
1367 : { "1137.999", English, true, 1137.999 },
1368 : { "1.000.00", European, false, 0.0 }
1369 1 : };
1370 15 : for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
1371 14 : rtl::OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
1372 14 : double nValue = 0.0;
1373 : bool bResult = ScStringUtil::parseSimpleNumber
1374 : (aStr, aTests[i].eSep == English ? '.' : ',',
1375 : aTests[i].eSep == English ? ',' : '.',
1376 14 : nValue);
1377 14 : CPPUNIT_ASSERT_MESSAGE ("CSV numeric detection failure", bResult == aTests[i].bResult);
1378 14 : CPPUNIT_ASSERT_MESSAGE ("CSV numeric value failure", nValue == aTests[i].nValue);
1379 14 : }
1380 1 : }
1381 :
1382 : template<typename Evaluator>
1383 4 : void checkMatrixElements(const ScMatrix& rMat)
1384 : {
1385 : SCSIZE nC, nR;
1386 4 : rMat.GetDimensions(nC, nR);
1387 : Evaluator aEval;
1388 32 : for (SCSIZE i = 0; i < nC; ++i)
1389 : {
1390 508 : for (SCSIZE j = 0; j < nR; ++j)
1391 : {
1392 480 : aEval(i, j, rMat.Get(i, j));
1393 : }
1394 : }
1395 4 : }
1396 :
1397 : struct AllZeroMatrix
1398 : {
1399 40 : void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
1400 : {
1401 40 : CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
1402 40 : CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
1403 40 : }
1404 : };
1405 :
1406 : struct PartiallyFilledZeroMatrix
1407 : {
1408 40 : void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
1409 : {
1410 40 : CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
1411 40 : if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
1412 : {
1413 14 : CPPUNIT_ASSERT_MESSAGE("element value must be 3.0", rVal.fVal == 3.0);
1414 : }
1415 : else
1416 : {
1417 26 : CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
1418 : }
1419 40 : }
1420 : };
1421 :
1422 : struct AllEmptyMatrix
1423 : {
1424 200 : void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
1425 : {
1426 200 : CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
1427 200 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
1428 200 : }
1429 : };
1430 :
1431 : struct PartiallyFilledEmptyMatrix
1432 : {
1433 200 : void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
1434 : {
1435 200 : if (nCol == 1 && nRow == 1)
1436 : {
1437 1 : CPPUNIT_ASSERT_MESSAGE("element is not of boolean type", rVal.nType == SC_MATVAL_BOOLEAN);
1438 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == 1.0);
1439 : }
1440 199 : else if (nCol == 4 && nRow == 5)
1441 : {
1442 1 : CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_VALUE);
1443 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == -12.5);
1444 : }
1445 198 : else if (nCol == 8 && nRow == 2)
1446 : {
1447 1 : CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING);
1448 1 : CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr == "Test");
1449 : }
1450 197 : else if (nCol == 8 && nRow == 11)
1451 : {
1452 1 : CPPUNIT_ASSERT_MESSAGE("element is not of empty path type", rVal.nType == SC_MATVAL_EMPTYPATH);
1453 1 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
1454 : }
1455 : else
1456 : {
1457 196 : CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
1458 196 : CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
1459 : }
1460 200 : }
1461 : };
1462 :
1463 1 : void Test::testMatrix()
1464 : {
1465 1 : ScMatrixRef pMat;
1466 :
1467 : // First, test the zero matrix type.
1468 1 : pMat = new ScMatrix(0, 0, 0.0);
1469 : SCSIZE nC, nR;
1470 1 : pMat->GetDimensions(nC, nR);
1471 1 : CPPUNIT_ASSERT_MESSAGE("matrix is not empty", nC == 0 && nR == 0);
1472 1 : pMat->Resize(4, 10, 0.0);
1473 1 : pMat->GetDimensions(nC, nR);
1474 1 : CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 4 && nR == 10);
1475 2 : CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
1476 1 : !pMat->And() && !pMat->Or());
1477 :
1478 : // Resizing into a larger matrix should fill the void space with zeros.
1479 1 : checkMatrixElements<AllZeroMatrix>(*pMat);
1480 :
1481 1 : pMat->FillDouble(3.0, 1, 2, 2, 8);
1482 1 : checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
1483 1 : CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
1484 2 : CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
1485 1 : !pMat->And() && pMat->Or());
1486 1 : pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
1487 2 : CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
1488 1 : pMat->And() && pMat->Or());
1489 :
1490 : // Test the AND and OR evaluations.
1491 1 : pMat = new ScMatrix(2, 2, 0.0);
1492 :
1493 : // Only some of the elements are non-zero.
1494 1 : pMat->PutBoolean(true, 0, 0);
1495 1 : pMat->PutDouble(1.0, 1, 1);
1496 1 : CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
1497 1 : CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
1498 :
1499 : // All of the elements are non-zero.
1500 1 : pMat->PutBoolean(true, 0, 1);
1501 1 : pMat->PutDouble(2.3, 1, 0);
1502 1 : CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
1503 1 : CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
1504 :
1505 : // Now test the emtpy matrix type.
1506 1 : pMat = new ScMatrix(10, 20);
1507 1 : pMat->GetDimensions(nC, nR);
1508 1 : CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 10 && nR == 20);
1509 1 : checkMatrixElements<AllEmptyMatrix>(*pMat);
1510 :
1511 1 : pMat->PutBoolean(true, 1, 1);
1512 1 : pMat->PutDouble(-12.5, 4, 5);
1513 1 : rtl::OUString aStr("Test");
1514 1 : pMat->PutString(aStr, 8, 2);
1515 1 : pMat->PutEmptyPath(8, 11);
1516 1 : checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
1517 1 : }
1518 :
1519 1 : void Test::testEnterMixedMatrix()
1520 : {
1521 1 : m_pDoc->InsertTab(0, "foo");
1522 :
1523 : // Insert the source values in A1:B2.
1524 1 : m_pDoc->SetString(0, 0, 0, "A");
1525 1 : m_pDoc->SetString(1, 0, 0, "B");
1526 1 : double val = 1.0;
1527 1 : m_pDoc->SetValue(0, 1, 0, val);
1528 1 : val = 2.0;
1529 1 : m_pDoc->SetValue(1, 1, 0, val);
1530 :
1531 : // Create a matrix range in A4:B5 referencing A1:B2.
1532 1 : ScMarkData aMark;
1533 1 : aMark.SelectOneTable(0);
1534 1 : m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, "=A1:B2", NULL);
1535 :
1536 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
1537 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
1538 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
1539 1 : CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
1540 :
1541 1 : m_pDoc->DeleteTab(0);
1542 1 : }
1543 :
1544 : namespace {
1545 :
1546 : struct DPFieldDef
1547 : {
1548 : const char* pName;
1549 : sheet::DataPilotFieldOrientation eOrient;
1550 :
1551 : /**
1552 : * Function for data field. It's used only for data field. When 0, the
1553 : * default function (SUM) is used.
1554 : */
1555 : int eFunc;
1556 : };
1557 :
1558 : template<size_t _Size>
1559 5 : ScRange insertDPSourceData(ScDocument* pDoc, DPFieldDef aFields[], size_t nFieldCount, const char* aData[][_Size], size_t nDataCount)
1560 : {
1561 : // Insert field names in row 0.
1562 22 : for (size_t i = 0; i < nFieldCount; ++i)
1563 17 : pDoc->SetString(static_cast<SCCOL>(i), 0, 0, OUString(aFields[i].pName, strlen(aFields[i].pName), RTL_TEXTENCODING_UTF8));
1564 :
1565 : // Insert data into row 1 and downward.
1566 31 : for (size_t i = 0; i < nDataCount; ++i)
1567 : {
1568 26 : SCROW nRow = static_cast<SCROW>(i) + 1;
1569 120 : for (size_t j = 0; j < nFieldCount; ++j)
1570 : {
1571 94 : SCCOL nCol = static_cast<SCCOL>(j);
1572 94 : pDoc->SetString(
1573 : nCol, nRow, 0, OUString(aData[i][j], strlen(aData[i][j]), RTL_TEXTENCODING_UTF8));
1574 : }
1575 : }
1576 :
1577 5 : SCROW nRow1 = 0, nRow2 = 0;
1578 5 : SCCOL nCol1 = 0, nCol2 = 0;
1579 5 : pDoc->GetDataArea(0, nCol1, nRow1, nCol2, nRow2, true, false);
1580 5 : CPPUNIT_ASSERT_MESSAGE("Data is expected to start from (col=0,row=0).", nCol1 == 0 && nRow1 == 0);
1581 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected data range.",
1582 : nCol2 == static_cast<SCCOL>(nFieldCount - 1) && nRow2 == static_cast<SCROW>(nDataCount));
1583 :
1584 5 : ScRange aSrcRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1585 5 : printRange(pDoc, aSrcRange, "Data sheet content");
1586 5 : return aSrcRange;
1587 : }
1588 :
1589 : template<size_t _Size>
1590 24 : bool checkDPTableOutput(ScDocument* pDoc, const ScRange& aOutRange, const char* aOutputCheck[][_Size], const char* pCaption)
1591 : {
1592 24 : bool bResult = true;
1593 24 : const ScAddress& s = aOutRange.aStart;
1594 24 : const ScAddress& e = aOutRange.aEnd;
1595 24 : SheetPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1);
1596 24 : SCROW nOutRowSize = e.Row() - s.Row() + 1;
1597 24 : SCCOL nOutColSize = e.Col() - s.Col() + 1;
1598 191 : for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
1599 : {
1600 697 : for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
1601 : {
1602 530 : OUString aVal;
1603 530 : pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab(), aVal);
1604 530 : printer.set(nRow, nCol, aVal);
1605 530 : const char* p = aOutputCheck[nRow][nCol];
1606 530 : if (p)
1607 : {
1608 399 : OUString aCheckVal = OUString::createFromAscii(p);
1609 399 : bool bEqual = aCheckVal.equals(aVal);
1610 399 : if (!bEqual)
1611 : {
1612 0 : cout << "Expected: " << aCheckVal << " Actual: " << aVal << endl;
1613 0 : bResult = false;
1614 : }
1615 : }
1616 131 : else if (!aVal.isEmpty())
1617 : {
1618 0 : cout << "Empty cell expected" << endl;
1619 0 : bResult = false;
1620 : }
1621 : }
1622 : }
1623 24 : printer.print(pCaption);
1624 24 : return bResult;
1625 : }
1626 :
1627 14 : ScDPObject* createDPFromSourceDesc(
1628 : ScDocument* pDoc, const ScSheetSourceDesc& rDesc, DPFieldDef aFields[], size_t nFieldCount,
1629 : bool bFilterButton)
1630 : {
1631 14 : ScDPObject* pDPObj = new ScDPObject(pDoc);
1632 14 : pDPObj->SetSheetDesc(rDesc);
1633 14 : pDPObj->SetOutRange(ScAddress(0, 0, 1));
1634 :
1635 14 : ScDPSaveData aSaveData;
1636 : // Set data pilot table output options.
1637 14 : aSaveData.SetIgnoreEmptyRows(false);
1638 14 : aSaveData.SetRepeatIfEmpty(false);
1639 14 : aSaveData.SetColumnGrand(true);
1640 14 : aSaveData.SetRowGrand(true);
1641 14 : aSaveData.SetFilterButton(bFilterButton);
1642 14 : aSaveData.SetDrillDown(true);
1643 :
1644 : // Check the sanity of the source range.
1645 14 : const ScRange& rSrcRange = rDesc.GetSourceRange();
1646 14 : SCROW nRow1 = rSrcRange.aStart.Row();
1647 14 : SCROW nRow2 = rSrcRange.aEnd.Row();
1648 14 : CPPUNIT_ASSERT_MESSAGE("source range contains no data!", nRow2 - nRow1 > 1);
1649 :
1650 : // Set the dimension information.
1651 51 : for (size_t i = 0; i < nFieldCount; ++i)
1652 : {
1653 37 : OUString aDimName = rtl::OUString::createFromAscii(aFields[i].pName);
1654 37 : ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(aDimName);
1655 37 : pDim->SetOrientation(static_cast<sal_uInt16>(aFields[i].eOrient));
1656 37 : pDim->SetUsedHierarchy(0);
1657 :
1658 37 : if (aFields[i].eOrient == sheet::DataPilotFieldOrientation_DATA)
1659 : {
1660 16 : sheet::GeneralFunction eFunc = sheet::GeneralFunction_SUM;
1661 16 : if (aFields[i].eFunc)
1662 9 : eFunc = static_cast<sheet::GeneralFunction>(aFields[i].eFunc);
1663 :
1664 16 : pDim->SetFunction(eFunc);
1665 16 : pDim->SetReferenceValue(NULL);
1666 : }
1667 : else
1668 : {
1669 21 : sheet::DataPilotFieldSortInfo aSortInfo;
1670 21 : aSortInfo.IsAscending = true;
1671 21 : aSortInfo.Mode = 2;
1672 21 : pDim->SetSortInfo(&aSortInfo);
1673 :
1674 21 : sheet::DataPilotFieldLayoutInfo aLayInfo;
1675 21 : aLayInfo.LayoutMode = 0;
1676 21 : aLayInfo.AddEmptyLines = false;
1677 21 : pDim->SetLayoutInfo(&aLayInfo);
1678 21 : sheet::DataPilotFieldAutoShowInfo aShowInfo;
1679 21 : aShowInfo.IsEnabled = false;
1680 21 : aShowInfo.ShowItemsMode = 0;
1681 21 : aShowInfo.ItemCount = 0;
1682 21 : pDim->SetAutoShowInfo(&aShowInfo);
1683 : }
1684 37 : }
1685 :
1686 : // Don't forget the data layout dimension.
1687 14 : ScDPSaveDimension* pDim = aSaveData.GetDataLayoutDimension();
1688 14 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
1689 14 : pDim->SetShowEmpty(true);
1690 :
1691 14 : pDPObj->SetSaveData(aSaveData);
1692 14 : pDPObj->SetAlive(true);
1693 14 : pDPObj->InvalidateData();
1694 :
1695 14 : return pDPObj;
1696 : }
1697 :
1698 13 : ScDPObject* createDPFromRange(
1699 : ScDocument* pDoc, const ScRange& rRange, DPFieldDef aFields[], size_t nFieldCount,
1700 : bool bFilterButton)
1701 : {
1702 13 : ScSheetSourceDesc aSheetDesc(pDoc);
1703 13 : aSheetDesc.SetSourceRange(rRange);
1704 13 : return createDPFromSourceDesc(pDoc, aSheetDesc, aFields, nFieldCount, bFilterButton);
1705 : }
1706 :
1707 22 : ScRange refresh(ScDPObject* pDPObj)
1708 : {
1709 22 : bool bOverFlow = false;
1710 22 : ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
1711 22 : CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
1712 :
1713 22 : pDPObj->Output(aOutRange.aStart);
1714 22 : aOutRange = pDPObj->GetOutRange();
1715 22 : return aOutRange;
1716 : }
1717 :
1718 4 : ScRange refreshGroups(ScDPCollection* pDPs, ScDPObject* pDPObj)
1719 : {
1720 : // We need to first create group data in the cache, then the group data in
1721 : // the object.
1722 4 : std::set<ScDPObject*> aRefs;
1723 4 : bool bSuccess = pDPs->ReloadGroupsInCache(pDPObj, aRefs);
1724 4 : CPPUNIT_ASSERT_MESSAGE("Failed to reload group data in cache.", bSuccess);
1725 4 : CPPUNIT_ASSERT_MESSAGE("There should be only one table linked to this cache.", aRefs.size() == 1);
1726 4 : pDPObj->ReloadGroupTableData();
1727 :
1728 4 : return refresh(pDPObj);
1729 : }
1730 :
1731 : }
1732 :
1733 1 : void Test::testPivotTable()
1734 : {
1735 1 : m_pDoc->InsertTab(0, OUString("Data"));
1736 1 : m_pDoc->InsertTab(1, OUString("Table"));
1737 :
1738 : // Dimension definition
1739 : DPFieldDef aFields[] = {
1740 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
1741 : { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
1742 : { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
1743 1 : };
1744 :
1745 : // Raw data
1746 : const char* aData[][3] = {
1747 : { "Andy", "A", "30" },
1748 : { "Bruce", "A", "20" },
1749 : { "Charlie", "B", "45" },
1750 : { "David", "B", "12" },
1751 : { "Edward", "C", "8" },
1752 : { "Frank", "C", "15" },
1753 1 : };
1754 :
1755 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
1756 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
1757 :
1758 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
1759 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
1760 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
1761 :
1762 : ScDPObject* pDPObj = createDPFromRange(
1763 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
1764 :
1765 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
1766 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
1767 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
1768 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
1769 1 : pDPs->GetCount() == 1);
1770 1 : pDPObj->SetName(pDPs->CreateNewName());
1771 :
1772 1 : bool bOverFlow = false;
1773 1 : ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
1774 1 : CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
1775 :
1776 1 : pDPObj->Output(aOutRange.aStart);
1777 1 : aOutRange = pDPObj->GetOutRange();
1778 : {
1779 : // Expected output table content. 0 = empty cell
1780 : const char* aOutputCheck[][5] = {
1781 : { "Sum - Score", "Group", 0, 0, 0 },
1782 : { "Name", "A", "B", "C", "Total Result" },
1783 : { "Andy", "30", 0, 0, "30" },
1784 : { "Bruce", "20", 0, 0, "20" },
1785 : { "Charlie", 0, "45", 0, "45" },
1786 : { "David", 0, "12", 0, "12" },
1787 : { "Edward", 0, 0, "8", "8" },
1788 : { "Frank", 0, 0, "15", "15" },
1789 : { "Total Result", "50", "57", "23", "130" }
1790 1 : };
1791 :
1792 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
1793 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1794 : }
1795 1 : CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
1796 :
1797 : // Update the cell values.
1798 1 : double aData2[] = { 100, 200, 300, 400, 500, 600 };
1799 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aData2); ++i)
1800 : {
1801 6 : SCROW nRow = i + 1;
1802 6 : m_pDoc->SetValue(2, nRow, 0, aData2[i]);
1803 : }
1804 :
1805 1 : printRange(m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), "Data sheet content (modified)");
1806 :
1807 : // Now, create a copy of the datapilot object for the updated table, but
1808 : // don't reload the cache which should force the copy to use the old data
1809 : // from the cache.
1810 1 : ScDPObject* pDPObj2 = new ScDPObject(*pDPObj);
1811 1 : pDPs->InsertNewTable(pDPObj2);
1812 :
1813 1 : aOutRange = pDPObj2->GetOutRange();
1814 1 : pDPObj2->ClearTableData();
1815 1 : pDPObj2->Output(aOutRange.aStart);
1816 : {
1817 : // Expected output table content. 0 = empty cell
1818 : const char* aOutputCheck[][5] = {
1819 : { "Sum - Score", "Group", 0, 0, 0 },
1820 : { "Name", "A", "B", "C", "Total Result" },
1821 : { "Andy", "30", 0, 0, "30" },
1822 : { "Bruce", "20", 0, 0, "20" },
1823 : { "Charlie", 0, "45", 0, "45" },
1824 : { "David", 0, "12", 0, "12" },
1825 : { "Edward", 0, 0, "8", "8" },
1826 : { "Frank", 0, 0, "15", "15" },
1827 : { "Total Result", "50", "57", "23", "130" }
1828 1 : };
1829 :
1830 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (from old cache)");
1831 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1832 : }
1833 :
1834 1 : CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
1835 :
1836 : // Free the first datapilot object after the 2nd one gets reloaded, to
1837 : // prevent the data cache from being deleted before the reload.
1838 1 : pDPs->FreeTable(pDPObj);
1839 :
1840 1 : CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
1841 :
1842 : // This time clear the cache to refresh the data from the source range.
1843 1 : CPPUNIT_ASSERT_MESSAGE("This datapilot should be based on sheet data.", pDPObj2->IsSheetData());
1844 1 : std::set<ScDPObject*> aRefs;
1845 1 : sal_uLong nErrId = pDPs->ReloadCache(pDPObj2, aRefs);
1846 1 : CPPUNIT_ASSERT_MESSAGE("Cache reload failed.", nErrId == 0);
1847 2 : CPPUNIT_ASSERT_MESSAGE("Reloading a cache shouldn't remove any cache.",
1848 1 : pDPs->GetSheetCaches().size() == 1);
1849 :
1850 1 : pDPObj2->ClearTableData();
1851 1 : pDPObj2->Output(aOutRange.aStart);
1852 :
1853 : {
1854 : // Expected output table content. 0 = empty cell
1855 : const char* aOutputCheck[][5] = {
1856 : { "Sum - Score", "Group", 0, 0, 0 },
1857 : { "Name", "A", "B", "C", "Total Result" },
1858 : { "Andy", "100", 0, 0, "100" },
1859 : { "Bruce", "200", 0, 0, "200" },
1860 : { "Charlie", 0, "300", 0, "300" },
1861 : { "David", 0, "400", 0, "400" },
1862 : { "Edward", 0, 0, "500", "500" },
1863 : { "Frank", 0, 0, "600", "600" },
1864 : { "Total Result", "300", "700", "1100", "2100" }
1865 1 : };
1866 :
1867 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (refreshed)");
1868 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1869 : }
1870 :
1871 1 : CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
1872 :
1873 : // Swap the two sheets.
1874 1 : m_pDoc->MoveTab(1, 0);
1875 2 : CPPUNIT_ASSERT_MESSAGE("Swapping the sheets shouldn't remove the cache.",
1876 1 : pDPs->GetSheetCaches().size() == 1);
1877 1 : CPPUNIT_ASSERT_MESSAGE("Cache should have moved.", !pDPs->GetSheetCaches().hasCache(aSrcRange));
1878 1 : aSrcRange.aStart.SetTab(1);
1879 1 : aSrcRange.aEnd.SetTab(1);
1880 1 : CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
1881 :
1882 1 : pDPs->FreeTable(pDPObj2);
1883 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
1884 1 : pDPs->GetCount() == 0);
1885 :
1886 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more data cache.",
1887 1 : pDPs->GetSheetCaches().size() == 0);
1888 :
1889 : // Insert a brand new pivot table object once again, but this time, don't
1890 : // create the output to avoid creating a data cache.
1891 1 : m_pDoc->DeleteTab(1);
1892 1 : m_pDoc->InsertTab(1, OUString("Table"));
1893 :
1894 : pDPObj = createDPFromRange(
1895 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
1896 1 : bSuccess = pDPs->InsertNewTable(pDPObj);
1897 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
1898 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
1899 1 : pDPs->GetCount() == 1);
1900 1 : pDPObj->SetName(pDPs->CreateNewName());
1901 2 : CPPUNIT_ASSERT_MESSAGE("Data cache shouldn't exist yet before creating the table output.",
1902 1 : pDPs->GetSheetCaches().size() == 0);
1903 :
1904 : // Now, "refresh" the table. This should still return a reference to self
1905 : // even with the absence of data cache.
1906 1 : aRefs.clear();
1907 1 : pDPs->ReloadCache(pDPObj, aRefs);
1908 2 : CPPUNIT_ASSERT_MESSAGE("It should return the same object as a reference.",
1909 1 : aRefs.size() == 1 && *aRefs.begin() == pDPObj);
1910 :
1911 1 : pDPs->FreeTable(pDPObj);
1912 :
1913 1 : m_pDoc->DeleteTab(1);
1914 1 : m_pDoc->DeleteTab(0);
1915 1 : }
1916 :
1917 1 : void Test::testPivotTableLabels()
1918 : {
1919 1 : m_pDoc->InsertTab(0, OUString("Data"));
1920 1 : m_pDoc->InsertTab(1, OUString("Table"));
1921 :
1922 : // Dimension definition
1923 : DPFieldDef aFields[] = {
1924 : { "Software", sheet::DataPilotFieldOrientation_ROW, 0 },
1925 : { "Version", sheet::DataPilotFieldOrientation_COLUMN, 0 },
1926 : { "1.2.3", sheet::DataPilotFieldOrientation_DATA, 0 }
1927 1 : };
1928 :
1929 : // Raw data
1930 : const char* aData[][3] = {
1931 : { "LibreOffice", "3.3.0", "30" },
1932 : { "LibreOffice", "3.3.1", "20" },
1933 : { "LibreOffice", "3.4.0", "45" },
1934 1 : };
1935 :
1936 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
1937 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
1938 :
1939 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
1940 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
1941 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
1942 :
1943 : ScDPObject* pDPObj = createDPFromRange(
1944 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
1945 :
1946 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
1947 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
1948 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
1949 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
1950 1 : pDPs->GetCount() == 1);
1951 1 : pDPObj->SetName(pDPs->CreateNewName());
1952 :
1953 1 : ScRange aOutRange = refresh(pDPObj);
1954 : {
1955 : // Expected output table content. 0 = empty cell
1956 : const char* aOutputCheck[][5] = {
1957 : { "Sum - 1.2.3", "Version", 0, 0, 0 },
1958 : { "Software", "3.3.0", "3.3.1", "3.4.0", "Total Result" },
1959 : { "LibreOffice", "30", "20", "45", "95" },
1960 : { "Total Result", "30", "20", "45", "95" }
1961 1 : };
1962 :
1963 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
1964 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1965 : }
1966 :
1967 1 : pDPs->FreeTable(pDPObj);
1968 :
1969 1 : m_pDoc->DeleteTab(1);
1970 1 : m_pDoc->DeleteTab(0);
1971 1 : }
1972 :
1973 1 : void Test::testPivotTableDateLabels()
1974 : {
1975 1 : m_pDoc->InsertTab(0, OUString("Data"));
1976 1 : m_pDoc->InsertTab(1, OUString("Table"));
1977 :
1978 : // Dimension definition
1979 : DPFieldDef aFields[] = {
1980 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
1981 : { "Date", sheet::DataPilotFieldOrientation_COLUMN, 0 },
1982 : { "Value", sheet::DataPilotFieldOrientation_DATA, 0 }
1983 1 : };
1984 :
1985 : // Raw data
1986 : const char* aData[][3] = {
1987 : { "Zena", "2011-1-1", "30" },
1988 : { "Yodel", "2011-1-2", "20" },
1989 : { "Xavior", "2011-1-3", "45" }
1990 1 : };
1991 :
1992 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
1993 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
1994 :
1995 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
1996 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
1997 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
1998 :
1999 : ScDPObject* pDPObj = createDPFromRange(
2000 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
2001 :
2002 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2003 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2004 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2005 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2006 1 : pDPs->GetCount() == 1);
2007 1 : pDPObj->SetName(pDPs->CreateNewName());
2008 :
2009 1 : ScRange aOutRange = refresh(pDPObj);
2010 : {
2011 : // Expected output table content. 0 = empty cell
2012 : const char* aOutputCheck[][5] = {
2013 : { "Sum - Value", "Date", 0, 0, 0 },
2014 : { "Name", "2011-01-01", "2011-01-02", "2011-01-03", "Total Result" },
2015 : { "Xavior", 0, 0, "45", "45" },
2016 : { "Yodel", 0, "20", 0, "20" },
2017 : { "Zena", "30", 0, 0, "30" },
2018 : { "Total Result", "30", "20", "45", "95" }
2019 1 : };
2020 :
2021 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2022 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2023 : }
2024 :
2025 : {
2026 : const char* aChecks[] = {
2027 : "2011-01-01", "2011-01-02", "2011-01-03"
2028 1 : };
2029 :
2030 : // Make sure those cells that contain dates are numeric.
2031 1 : SCROW nRow = aOutRange.aStart.Row() + 1;
2032 1 : nCol1 = aOutRange.aStart.Col() + 1;
2033 1 : nCol2 = nCol1 + 2;
2034 4 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2035 : {
2036 3 : OUString aVal = m_pDoc->GetString(nCol, nRow, 1);
2037 3 : CPPUNIT_ASSERT_MESSAGE("Cell value is not as expected.", aVal.equalsAscii(aChecks[nCol-nCol1]));
2038 6 : CPPUNIT_ASSERT_MESSAGE("This cell contains a date value and is supposed to be numeric.",
2039 3 : m_pDoc->HasValueData(nCol, nRow, 1));
2040 3 : }
2041 : }
2042 :
2043 1 : pDPs->FreeTable(pDPObj);
2044 :
2045 1 : m_pDoc->DeleteTab(1);
2046 1 : m_pDoc->DeleteTab(0);
2047 1 : }
2048 :
2049 1 : void Test::testPivotTableFilters()
2050 : {
2051 1 : m_pDoc->InsertTab(0, OUString("Data"));
2052 1 : m_pDoc->InsertTab(1, OUString("Table"));
2053 :
2054 : // Dimension definition
2055 : DPFieldDef aFields[] = {
2056 : { "Name", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
2057 : { "Group1", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
2058 : { "Group2", sheet::DataPilotFieldOrientation_PAGE, 0 },
2059 : { "Val1", sheet::DataPilotFieldOrientation_DATA, 0 },
2060 : { "Val2", sheet::DataPilotFieldOrientation_DATA, 0 }
2061 1 : };
2062 :
2063 : // Raw data
2064 : const char* aData[][5] = {
2065 : { "A", "1", "A", "1", "10" },
2066 : { "B", "1", "A", "1", "10" },
2067 : { "C", "1", "B", "1", "10" },
2068 : { "D", "1", "B", "1", "10" },
2069 : { "E", "2", "A", "1", "10" },
2070 : { "F", "2", "A", "1", "10" },
2071 : { "G", "2", "B", "1", "10" },
2072 : { "H", "2", "B", "1", "10" }
2073 1 : };
2074 :
2075 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
2076 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
2077 :
2078 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
2079 1 : SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
2080 1 : SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
2081 :
2082 : ScDPObject* pDPObj = createDPFromRange(
2083 1 : m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, true);
2084 :
2085 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2086 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2087 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
2088 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2089 1 : pDPs->GetCount() == 1);
2090 1 : pDPObj->SetName(pDPs->CreateNewName());
2091 :
2092 1 : ScRange aOutRange = refresh(pDPObj);
2093 : {
2094 : // Expected output table content. 0 = empty cell
2095 : const char* aOutputCheck[][2] = {
2096 : { "Filter", 0 },
2097 : { "Group2", "- all -" },
2098 : { 0, 0 },
2099 : { "Data", 0 },
2100 : { "Sum - Val1", "8" },
2101 : { "Sum - Val2", "80" }
2102 1 : };
2103 :
2104 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (unfiltered)");
2105 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2106 : }
2107 :
2108 1 : AutoCalcSwitch aACSwitch(m_pDoc, true); // turn on auto calculation.
2109 :
2110 1 : ScAddress aFormulaAddr = aOutRange.aEnd;
2111 1 : aFormulaAddr.IncRow(2);
2112 2 : m_pDoc->SetString(aFormulaAddr.Col(), aFormulaAddr.Row(), aFormulaAddr.Tab(),
2113 3 : rtl::OUString("=B6"));
2114 1 : double fTest = m_pDoc->GetValue(aFormulaAddr);
2115 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 80.0);
2116 :
2117 : // Set current page of 'Group2' to 'A'.
2118 1 : pDPObj->BuildAllDimensionMembers();
2119 1 : ScDPSaveData aSaveData(*pDPObj->GetSaveData());
2120 : ScDPSaveDimension* pPageDim = aSaveData.GetDimensionByName(
2121 1 : OUString("Group2"));
2122 1 : CPPUNIT_ASSERT_MESSAGE("Dimension not found", pPageDim);
2123 1 : OUString aPage("A");
2124 1 : pPageDim->SetCurrentPage(&aPage);
2125 1 : pDPObj->SetSaveData(aSaveData);
2126 1 : aOutRange = refresh(pDPObj);
2127 : {
2128 : // Expected output table content. 0 = empty cell
2129 : const char* aOutputCheck[][2] = {
2130 : { "Filter", 0 },
2131 : { "Group2", "A" },
2132 : { 0, 0 },
2133 : { "Data", 0 },
2134 : { "Sum - Val1", "4" },
2135 : { "Sum - Val2", "40" }
2136 1 : };
2137 :
2138 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
2139 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2140 : }
2141 :
2142 1 : fTest = m_pDoc->GetValue(aFormulaAddr);
2143 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 40.0);
2144 :
2145 : // Set query filter.
2146 1 : ScSheetSourceDesc aDesc(*pDPObj->GetSheetDesc());
2147 1 : ScQueryParam aQueryParam(aDesc.GetQueryParam());
2148 1 : CPPUNIT_ASSERT_MESSAGE("There should be at least one query entry.", aQueryParam.GetEntryCount() > 0);
2149 1 : ScQueryEntry& rEntry = aQueryParam.GetEntry(0);
2150 1 : rEntry.bDoQuery = true;
2151 1 : rEntry.nField = 1; // Group1
2152 1 : rEntry.GetQueryItem().mfVal = 1;
2153 1 : aDesc.SetQueryParam(aQueryParam);
2154 1 : pDPObj->SetSheetDesc(aDesc);
2155 1 : aOutRange = refresh(pDPObj);
2156 : {
2157 : // Expected output table content. 0 = empty cell
2158 : const char* aOutputCheck[][2] = {
2159 : { "Filter", 0 },
2160 : { "Group2", "A" },
2161 : { 0, 0 },
2162 : { "Data", 0 },
2163 : { "Sum - Val1", "2" },
2164 : { "Sum - Val2", "20" }
2165 1 : };
2166 :
2167 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by query)");
2168 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2169 : }
2170 :
2171 1 : fTest = m_pDoc->GetValue(aFormulaAddr);
2172 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 20.0);
2173 :
2174 : // Set the current page of 'Group2' back to '- all -'. The query filter
2175 : // should still be in effect.
2176 1 : pPageDim->SetCurrentPage(NULL); // Remove the page.
2177 1 : pDPObj->SetSaveData(aSaveData);
2178 1 : aOutRange = refresh(pDPObj);
2179 : {
2180 : // Expected output table content. 0 = empty cell
2181 : const char* aOutputCheck[][2] = {
2182 : { "Filter", 0 },
2183 : { "Group2", "- all -" },
2184 : { 0, 0 },
2185 : { "Data", 0 },
2186 : { "Sum - Val1", "4" },
2187 : { "Sum - Val2", "40" }
2188 1 : };
2189 :
2190 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
2191 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2192 : }
2193 :
2194 :
2195 1 : pDPs->FreeTable(pDPObj);
2196 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
2197 1 : pDPs->GetCount() == 0);
2198 :
2199 1 : m_pDoc->DeleteTab(1);
2200 1 : m_pDoc->DeleteTab(0);
2201 1 : }
2202 :
2203 1 : void Test::testPivotTableNamedSource()
2204 : {
2205 1 : m_pDoc->InsertTab(0, OUString("Data"));
2206 1 : m_pDoc->InsertTab(1, OUString("Table"));
2207 :
2208 : // Dimension definition
2209 : DPFieldDef aFields[] = {
2210 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
2211 : { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
2212 : { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
2213 1 : };
2214 :
2215 : // Raw data
2216 : const char* aData[][3] = {
2217 : { "Andy", "A", "30" },
2218 : { "Bruce", "A", "20" },
2219 : { "Charlie", "B", "45" },
2220 : { "David", "B", "12" },
2221 : { "Edward", "C", "8" },
2222 : { "Frank", "C", "15" },
2223 1 : };
2224 :
2225 1 : size_t nFieldCount = SAL_N_ELEMENTS(aFields);
2226 1 : size_t nDataCount = SAL_N_ELEMENTS(aData);
2227 :
2228 : // Insert the raw data.
2229 1 : ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
2230 1 : rtl::OUString aRangeStr;
2231 1 : aSrcRange.Format(aRangeStr, SCR_ABS_3D, m_pDoc);
2232 :
2233 : // Name this range.
2234 1 : rtl::OUString aRangeName("MyData");
2235 1 : ScRangeName* pNames = m_pDoc->GetRangeName();
2236 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get global range name container.", pNames);
2237 : ScRangeData* pName = new ScRangeData(
2238 1 : m_pDoc, aRangeName, aRangeStr);
2239 1 : bool bSuccess = pNames->insert(pName);
2240 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bSuccess);
2241 :
2242 1 : ScSheetSourceDesc aSheetDesc(m_pDoc);
2243 1 : aSheetDesc.SetRangeName(aRangeName);
2244 1 : ScDPObject* pDPObj = createDPFromSourceDesc(m_pDoc, aSheetDesc, aFields, nFieldCount, false);
2245 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create a new pivot table object.", pDPObj);
2246 :
2247 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2248 1 : bSuccess = pDPs->InsertNewTable(pDPObj);
2249 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
2250 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2251 1 : pDPs->GetCount() == 1);
2252 1 : pDPObj->SetName(pDPs->CreateNewName());
2253 :
2254 1 : ScRange aOutRange = refresh(pDPObj);
2255 : {
2256 : // Expected output table content. 0 = empty cell
2257 : const char* aOutputCheck[][5] = {
2258 : { "Sum - Score", "Group", 0, 0, 0 },
2259 : { "Name", "A", "B", "C", "Total Result" },
2260 : { "Andy", "30", 0, 0, "30" },
2261 : { "Bruce", "20", 0, 0, "20" },
2262 : { "Charlie", 0, "45", 0, "45" },
2263 : { "David", 0, "12", 0, "12" },
2264 : { "Edward", 0, 0, "8", "8" },
2265 : { "Frank", 0, 0, "15", "15" },
2266 : { "Total Result", "50", "57", "23", "130" }
2267 1 : };
2268 :
2269 1 : bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2270 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2271 : }
2272 :
2273 2 : CPPUNIT_ASSERT_MESSAGE("There should be one named range data cache.",
2274 1 : pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
2275 :
2276 : // Move the table with pivot table to the left of the source data sheet.
2277 1 : m_pDoc->MoveTab(1, 0);
2278 1 : rtl::OUString aTabName;
2279 1 : m_pDoc->GetName(0, aTabName);
2280 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong sheet name.", aTabName == "Table" );
2281 2 : CPPUNIT_ASSERT_MESSAGE("Pivot table output is on the wrong sheet!",
2282 1 : pDPObj->GetOutRange().aStart.Tab() == 0);
2283 :
2284 2 : CPPUNIT_ASSERT_MESSAGE("Moving the pivot table to another sheet shouldn't have changed the cache state.",
2285 1 : pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
2286 :
2287 1 : const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
2288 1 : CPPUNIT_ASSERT_MESSAGE("Sheet source description doesn't exist.", pDesc);
2289 2 : CPPUNIT_ASSERT_MESSAGE("Named source range has been altered unexpectedly!",
2290 1 : pDesc->GetRangeName().equals(aRangeName));
2291 :
2292 1 : CPPUNIT_ASSERT_MESSAGE("Cache should exist.", pDPs->GetNameCaches().hasCache(aRangeName));
2293 :
2294 1 : pDPs->FreeTable(pDPObj);
2295 1 : CPPUNIT_ASSERT_MESSAGE("There should be no more tables.", pDPs->GetCount() == 0);
2296 2 : CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more cache stored.",
2297 1 : pDPs->GetNameCaches().size() == 0);
2298 :
2299 1 : pNames->clear();
2300 1 : m_pDoc->DeleteTab(1);
2301 1 : m_pDoc->DeleteTab(0);
2302 1 : }
2303 :
2304 1 : void Test::testPivotTableCache()
2305 : {
2306 1 : m_pDoc->InsertTab(0, OUString("Data"));
2307 :
2308 : // Raw data
2309 : const char* aData[][3] = {
2310 : { "F1", "F2", "F3" },
2311 : { "Z", "A", "30" },
2312 : { "R", "A", "20" },
2313 : { "A", "B", "45" },
2314 : { "F", "B", "12" },
2315 : { "Y", "C", "8" },
2316 : { "12", "C", "15" },
2317 1 : };
2318 :
2319 1 : ScAddress aPos(1,1,0);
2320 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
2321 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
2322 :
2323 1 : ScDPCache aCache(m_pDoc);
2324 1 : aCache.InitFromDoc(m_pDoc, aDataRange);
2325 1 : long nDimCount = aCache.GetColumnCount();
2326 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension count.", nDimCount == 3);
2327 1 : rtl::OUString aDimName = aCache.GetDimensionName(0);
2328 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F1"));
2329 1 : aDimName = aCache.GetDimensionName(1);
2330 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F2"));
2331 1 : aDimName = aCache.GetDimensionName(2);
2332 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F3"));
2333 :
2334 : // In each dimension, member ID values also represent their sort order (in
2335 : // source dimensions only, not in group dimensions). Value items are
2336 : // sorted before string ones. Also, no duplicate dimension members should
2337 : // exist.
2338 :
2339 : // Dimension 0 - a mix of strings and values.
2340 1 : long nMemCount = aCache.GetDimMemberCount(0);
2341 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
2342 1 : const ScDPItemData* pItem = aCache.GetItemDataById(0, 0);
2343 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2344 : pItem->GetType() == ScDPItemData::Value &&
2345 1 : pItem->GetValue() == 12);
2346 1 : pItem = aCache.GetItemDataById(0, 1);
2347 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2348 : pItem->GetType() == ScDPItemData::String &&
2349 1 : pItem->GetString().equalsAscii("A"));
2350 1 : pItem = aCache.GetItemDataById(0, 2);
2351 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2352 : pItem->GetType() == ScDPItemData::String &&
2353 1 : pItem->GetString().equalsAscii("F"));
2354 1 : pItem = aCache.GetItemDataById(0, 3);
2355 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2356 : pItem->GetType() == ScDPItemData::String &&
2357 1 : pItem->GetString().equalsAscii("R"));
2358 1 : pItem = aCache.GetItemDataById(0, 4);
2359 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2360 : pItem->GetType() == ScDPItemData::String &&
2361 1 : pItem->GetString().equalsAscii("Y"));
2362 1 : pItem = aCache.GetItemDataById(0, 5);
2363 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2364 : pItem->GetType() == ScDPItemData::String &&
2365 1 : pItem->GetString().equalsAscii("Z"));
2366 1 : pItem = aCache.GetItemDataById(0, 6);
2367 1 : CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
2368 :
2369 : // Dimension 1 - duplicate values in source.
2370 1 : nMemCount = aCache.GetDimMemberCount(1);
2371 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 3);
2372 1 : pItem = aCache.GetItemDataById(1, 0);
2373 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2374 : pItem->GetType() == ScDPItemData::String &&
2375 1 : pItem->GetString().equalsAscii("A"));
2376 1 : pItem = aCache.GetItemDataById(1, 1);
2377 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2378 : pItem->GetType() == ScDPItemData::String &&
2379 1 : pItem->GetString().equalsAscii("B"));
2380 1 : pItem = aCache.GetItemDataById(1, 2);
2381 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2382 : pItem->GetType() == ScDPItemData::String &&
2383 1 : pItem->GetString().equalsAscii("C"));
2384 1 : pItem = aCache.GetItemDataById(1, 3);
2385 1 : CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
2386 :
2387 : // Dimension 2 - values only.
2388 1 : nMemCount = aCache.GetDimMemberCount(2);
2389 1 : CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
2390 1 : pItem = aCache.GetItemDataById(2, 0);
2391 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2392 : pItem->GetType() == ScDPItemData::Value &&
2393 1 : pItem->GetValue() == 8);
2394 1 : pItem = aCache.GetItemDataById(2, 1);
2395 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2396 : pItem->GetType() == ScDPItemData::Value &&
2397 1 : pItem->GetValue() == 12);
2398 1 : pItem = aCache.GetItemDataById(2, 2);
2399 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2400 : pItem->GetType() == ScDPItemData::Value &&
2401 1 : pItem->GetValue() == 15);
2402 1 : pItem = aCache.GetItemDataById(2, 3);
2403 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2404 : pItem->GetType() == ScDPItemData::Value &&
2405 1 : pItem->GetValue() == 20);
2406 1 : pItem = aCache.GetItemDataById(2, 4);
2407 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2408 : pItem->GetType() == ScDPItemData::Value &&
2409 1 : pItem->GetValue() == 30);
2410 1 : pItem = aCache.GetItemDataById(2, 5);
2411 2 : CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
2412 : pItem->GetType() == ScDPItemData::Value &&
2413 1 : pItem->GetValue() == 45);
2414 1 : pItem = aCache.GetItemDataById(2, 6);
2415 1 : CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
2416 :
2417 : {
2418 : // Check the integrity of the source data.
2419 1 : ScDPItemData aTest;
2420 : long nDim;
2421 :
2422 : {
2423 : // Dimension 0: Z, R, A, F, Y, 12
2424 1 : nDim = 0;
2425 1 : const char* aChecks[] = { "Z", "R", "A", "F", "Y" };
2426 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
2427 : {
2428 5 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
2429 5 : aTest.SetString(rtl::OUString::createFromAscii(aChecks[i]));
2430 5 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
2431 : }
2432 :
2433 1 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, 5, false));
2434 1 : aTest.SetValue(12);
2435 1 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
2436 : }
2437 :
2438 : {
2439 : // Dimension 1: A, A, B, B, C, C
2440 1 : nDim = 1;
2441 1 : const char* aChecks[] = { "A", "A", "B", "B", "C", "C" };
2442 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
2443 : {
2444 6 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
2445 6 : aTest.SetString(rtl::OUString::createFromAscii(aChecks[i]));
2446 6 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
2447 : }
2448 : }
2449 :
2450 : {
2451 : // Dimension 2: 30, 20, 45, 12, 8, 15
2452 1 : nDim = 2;
2453 1 : double aChecks[] = { 30, 20, 45, 12, 8, 15 };
2454 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
2455 : {
2456 6 : pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
2457 6 : aTest.SetValue(aChecks[i]);
2458 6 : CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
2459 : }
2460 1 : }
2461 : }
2462 :
2463 : // Now, on to testing the filtered cache.
2464 :
2465 : {
2466 : // Non-filtered cache - everything should be visible.
2467 1 : ScDPFilteredCache aFilteredCache(aCache);
2468 1 : aFilteredCache.fillTable();
2469 :
2470 1 : sal_Int32 nRows = aFilteredCache.getRowSize();
2471 1 : CPPUNIT_ASSERT_MESSAGE("Wrong dimension.", nRows == 6 && aFilteredCache.getColSize() == 3);
2472 :
2473 7 : for (sal_Int32 i = 0; i < nRows; ++i)
2474 : {
2475 6 : if (!aFilteredCache.isRowActive(i))
2476 : {
2477 0 : std::ostringstream os;
2478 0 : os << "Row " << i << " should be visible but it isn't.";
2479 0 : CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
2480 : }
2481 1 : }
2482 : }
2483 :
2484 : // TODO : Add test for filtered caches.
2485 :
2486 1 : m_pDoc->DeleteTab(0);
2487 1 : }
2488 :
2489 1 : void Test::testPivotTableDuplicateDataFields()
2490 : {
2491 1 : m_pDoc->InsertTab(0, OUString("Data"));
2492 1 : m_pDoc->InsertTab(1, OUString("Table"));
2493 :
2494 : // Raw data
2495 : const char* aData[][2] = {
2496 : { "Name", "Value" },
2497 : { "A", "45" },
2498 : { "A", "5" },
2499 : { "A", "41" },
2500 : { "A", "49" },
2501 : { "A", "4" },
2502 : { "B", "33" },
2503 : { "B", "84" },
2504 : { "B", "74" },
2505 : { "B", "8" },
2506 : { "B", "68" }
2507 1 : };
2508 :
2509 : // Dimension definition
2510 : DPFieldDef aFields[] = {
2511 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
2512 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
2513 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_COUNT }
2514 1 : };
2515 :
2516 1 : ScAddress aPos(2,2,0);
2517 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
2518 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
2519 :
2520 : ScDPObject* pDPObj = createDPFromRange(
2521 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
2522 :
2523 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2524 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2525 :
2526 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
2527 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
2528 1 : pDPs->GetCount(), static_cast<size_t>(1));
2529 1 : pDPObj->SetName(pDPs->CreateNewName());
2530 :
2531 1 : ScRange aOutRange = refresh(pDPObj);
2532 : {
2533 : // Expected output table content. 0 = empty cell
2534 : const char* aOutputCheck[][3] = {
2535 : { "Name", "Data", 0 },
2536 : { "A", "Sum - Value", "144" },
2537 : { 0, "Count - Value", "5" },
2538 : { "B", "Sum - Value", "267" },
2539 : { 0, "Count - Value", "5" },
2540 : { "Total Sum - Value", 0, "411" },
2541 : { "Total Count - Value", 0, "10" },
2542 1 : };
2543 :
2544 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2545 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2546 : }
2547 :
2548 : // Move the data layout dimension from row to column.
2549 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
2550 1 : CPPUNIT_ASSERT_MESSAGE("No save data!?", pSaveData);
2551 1 : ScDPSaveDimension* pDataLayout = pSaveData->GetDataLayoutDimension();
2552 1 : CPPUNIT_ASSERT_MESSAGE("No data layout dimension.", pDataLayout);
2553 1 : pDataLayout->SetOrientation(sheet::DataPilotFieldOrientation_COLUMN);
2554 1 : pDPObj->SetSaveData(*pSaveData);
2555 :
2556 : // Refresh the table output.
2557 1 : aOutRange = refresh(pDPObj);
2558 : {
2559 : // Expected output table content. 0 = empty cell
2560 : const char* aOutputCheck[][3] = {
2561 : { 0, "Data", 0 },
2562 : { "Name", "Sum - Value", "Count - Value" },
2563 : { "A", "144", "5" },
2564 : { "B", "267", "5" },
2565 : { "Total Result", "411", "10" }
2566 1 : };
2567 :
2568 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
2569 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2570 : }
2571 :
2572 1 : ScPivotParam aParam;
2573 1 : pDPObj->FillLabelData(aParam);
2574 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 4 labels (2 original, 1 data layout, and 1 duplicate dimensions).",
2575 1 : aParam.maLabelArray.size(), static_cast<size_t>(4));
2576 :
2577 1 : pDPs->FreeTable(pDPObj);
2578 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
2579 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
2580 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
2581 :
2582 1 : m_pDoc->DeleteTab(1);
2583 1 : m_pDoc->DeleteTab(0);
2584 1 : }
2585 :
2586 1 : void Test::testPivotTableNormalGrouping()
2587 : {
2588 1 : m_pDoc->InsertTab(0, OUString("Data"));
2589 1 : m_pDoc->InsertTab(1, OUString("Table"));
2590 :
2591 : // Raw data
2592 : const char* aData[][2] = {
2593 : { "Name", "Value" },
2594 : { "A", "1" },
2595 : { "B", "2" },
2596 : { "C", "3" },
2597 : { "D", "4" },
2598 : { "E", "5" },
2599 : { "F", "6" },
2600 : { "G", "7" }
2601 1 : };
2602 :
2603 : // Dimension definition
2604 : DPFieldDef aFields[] = {
2605 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
2606 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
2607 1 : };
2608 :
2609 1 : ScAddress aPos(1,1,0);
2610 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
2611 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
2612 :
2613 : ScDPObject* pDPObj = createDPFromRange(
2614 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
2615 :
2616 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2617 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2618 :
2619 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
2620 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
2621 1 : pDPs->GetCount(), static_cast<size_t>(1));
2622 1 : pDPObj->SetName(pDPs->CreateNewName());
2623 :
2624 1 : ScRange aOutRange = refresh(pDPObj);
2625 : {
2626 : // Expected output table content. 0 = empty cell
2627 : const char* aOutputCheck[][2] = {
2628 : { "Name", 0 },
2629 : { "A", "1" },
2630 : { "B", "2" },
2631 : { "C", "3" },
2632 : { "D", "4" },
2633 : { "E", "5" },
2634 : { "F", "6" },
2635 : { "G", "7" },
2636 : { "Total Result", "28" }
2637 1 : };
2638 :
2639 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Initial output without grouping");
2640 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2641 : }
2642 :
2643 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
2644 1 : CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
2645 1 : ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
2646 1 : CPPUNIT_ASSERT_MESSAGE("Failed to create dimension data.", pDimData);
2647 :
2648 1 : rtl::OUString aGroupPrefix("Group");
2649 1 : rtl::OUString aBaseDimName("Name");
2650 : rtl::OUString aGroupDimName =
2651 1 : pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, NULL);
2652 :
2653 : {
2654 : // Group A, B and C together.
2655 1 : ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
2656 1 : rtl::OUString aGroupName = aGroupDim.CreateGroupName(aGroupPrefix);
2657 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group1"));
2658 :
2659 1 : ScDPSaveGroupItem aGroup(aGroupName);
2660 1 : aGroup.AddElement(rtl::OUString("A"));
2661 1 : aGroup.AddElement(rtl::OUString("B"));
2662 1 : aGroup.AddElement(rtl::OUString("C"));
2663 1 : aGroupDim.AddGroupItem(aGroup);
2664 1 : pDimData->AddGroupDimension(aGroupDim);
2665 :
2666 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
2667 1 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
2668 1 : pSaveData->SetPosition(pDim, 0); // Set it before the base dimension.
2669 : }
2670 :
2671 1 : pDPObj->SetSaveData(*pSaveData);
2672 1 : aOutRange = refreshGroups(pDPs, pDPObj);
2673 : {
2674 : // Expected output table content. 0 = empty cell
2675 : const char* aOutputCheck[][3] = {
2676 : { "Name2", "Name", 0 },
2677 : { "D", "D", "4" },
2678 : { "E", "E", "5" },
2679 : { "F", "F", "6" },
2680 : { "G", "G", "7" },
2681 : { "Group1", "A", "1" },
2682 : { 0, "B", "2" },
2683 : { 0, "C", "3" },
2684 : { "Total Result", 0, "28" }
2685 1 : };
2686 :
2687 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "A, B, C grouped by Group1.");
2688 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2689 : }
2690 :
2691 1 : pSaveData = pDPObj->GetSaveData();
2692 1 : pDimData = pSaveData->GetDimensionData();
2693 :
2694 : {
2695 : // Group D, E, F together.
2696 1 : ScDPSaveGroupDimension* pGroupDim = pDimData->GetGroupDimAccForBase(aBaseDimName);
2697 1 : CPPUNIT_ASSERT_MESSAGE("There should be an existing group dimension.", pGroupDim);
2698 1 : rtl::OUString aGroupName = pGroupDim->CreateGroupName(aGroupPrefix);
2699 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group2"));
2700 :
2701 1 : ScDPSaveGroupItem aGroup(aGroupName);
2702 1 : aGroup.AddElement(rtl::OUString("D"));
2703 1 : aGroup.AddElement(rtl::OUString("E"));
2704 1 : aGroup.AddElement(rtl::OUString("F"));
2705 1 : pGroupDim->AddGroupItem(aGroup);
2706 : }
2707 :
2708 1 : pDPObj->SetSaveData(*pSaveData);
2709 1 : aOutRange = refreshGroups(pDPs, pDPObj);
2710 : {
2711 : // Expected output table content. 0 = empty cell
2712 : const char* aOutputCheck[][3] = {
2713 : { "Name2", "Name", 0 },
2714 : { "G", "G", "7" },
2715 : { "Group1", "A", "1" },
2716 : { 0, "B", "2" },
2717 : { 0, "C", "3" },
2718 : { "Group2", "D", "4" },
2719 : { 0, "E", "5" },
2720 : { 0, "F", "6" },
2721 : { "Total Result", 0, "28" }
2722 1 : };
2723 :
2724 1 : bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "D, E, F grouped by Group2.");
2725 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2726 : }
2727 :
2728 1 : pDPs->FreeTable(pDPObj);
2729 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
2730 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
2731 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
2732 :
2733 1 : m_pDoc->DeleteTab(1);
2734 1 : m_pDoc->DeleteTab(0);
2735 1 : }
2736 :
2737 1 : void Test::testPivotTableNumberGrouping()
2738 : {
2739 1 : m_pDoc->InsertTab(0, OUString("Data"));
2740 1 : m_pDoc->InsertTab(1, OUString("Table"));
2741 :
2742 : // Raw data
2743 : const char* aData[][2] = {
2744 : { "Order", "Score" },
2745 : { "43", "171" },
2746 : { "18", "20" },
2747 : { "69", "159" },
2748 : { "95", "19" },
2749 : { "96", "163" },
2750 : { "46", "70" },
2751 : { "22", "36" },
2752 : { "81", "49" },
2753 : { "54", "61" },
2754 : { "39", "62" },
2755 : { "86", "17" },
2756 : { "34", "0" },
2757 : { "30", "25" },
2758 : { "24", "103" },
2759 : { "16", "59" },
2760 : { "24", "119" },
2761 : { "15", "86" },
2762 : { "69", "170" }
2763 1 : };
2764 :
2765 : // Dimension definition
2766 : DPFieldDef aFields[] = {
2767 : { "Order", sheet::DataPilotFieldOrientation_ROW, 0 },
2768 : { "Score", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
2769 1 : };
2770 :
2771 1 : ScAddress aPos(1,1,0);
2772 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
2773 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
2774 :
2775 : ScDPObject* pDPObj = createDPFromRange(
2776 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
2777 :
2778 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2779 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2780 :
2781 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
2782 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
2783 1 : pDPs->GetCount(), static_cast<size_t>(1));
2784 1 : pDPObj->SetName(pDPs->CreateNewName());
2785 :
2786 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
2787 1 : CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
2788 1 : ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
2789 1 : CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
2790 :
2791 : {
2792 1 : ScDPNumGroupInfo aInfo;
2793 1 : aInfo.mbEnable = true;
2794 1 : aInfo.mbAutoStart = false;
2795 1 : aInfo.mbAutoEnd = false;
2796 1 : aInfo.mbDateValues = false;
2797 1 : aInfo.mbIntegerOnly = true;
2798 1 : aInfo.mfStart = 30;
2799 1 : aInfo.mfEnd = 60;
2800 1 : aInfo.mfStep = 10;
2801 1 : ScDPSaveNumGroupDimension aGroup(rtl::OUString("Order"), aInfo);
2802 1 : pDimData->AddNumGroupDimension(aGroup);
2803 : }
2804 :
2805 1 : pDPObj->SetSaveData(*pSaveData);
2806 1 : ScRange aOutRange = refreshGroups(pDPs, pDPObj);
2807 : {
2808 : // Expected output table content. 0 = empty cell
2809 : const char* aOutputCheck[][2] = {
2810 : { "Order", 0 },
2811 : { "<30", "423" },
2812 : { "30-39", "87" },
2813 : { "40-49", "241" },
2814 : { "50-60", "61" },
2815 : { ">60", "577" },
2816 : { "Total Result", "1389" }
2817 1 : };
2818 :
2819 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Order grouped by numbers");
2820 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2821 : }
2822 :
2823 1 : pDPs->FreeTable(pDPObj);
2824 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
2825 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
2826 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
2827 :
2828 1 : m_pDoc->DeleteTab(1);
2829 1 : m_pDoc->DeleteTab(0);
2830 1 : }
2831 :
2832 1 : void Test::testPivotTableDateGrouping()
2833 : {
2834 1 : m_pDoc->InsertTab(0, OUString("Data"));
2835 1 : m_pDoc->InsertTab(1, OUString("Table"));
2836 :
2837 : // Raw data
2838 : const char* aData[][2] = {
2839 : { "Date", "Value" },
2840 : { "2011-01-01", "1" },
2841 : { "2011-03-02", "2" },
2842 : { "2012-01-04", "3" },
2843 : { "2012-02-23", "4" },
2844 : { "2012-02-24", "5" },
2845 : { "2012-03-15", "6" },
2846 : { "2011-09-03", "7" },
2847 : { "2012-12-25", "8" }
2848 1 : };
2849 :
2850 : // Dimension definition
2851 : DPFieldDef aFields[] = {
2852 : { "Date", sheet::DataPilotFieldOrientation_ROW, 0 },
2853 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
2854 1 : };
2855 :
2856 1 : ScAddress aPos(1,1,0);
2857 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
2858 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
2859 :
2860 : ScDPObject* pDPObj = createDPFromRange(
2861 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
2862 :
2863 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
2864 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
2865 :
2866 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
2867 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
2868 1 : pDPs->GetCount() == 1);
2869 1 : pDPObj->SetName(pDPs->CreateNewName());
2870 :
2871 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
2872 1 : CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
2873 1 : ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
2874 1 : CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
2875 :
2876 1 : rtl::OUString aBaseDimName("Date");
2877 :
2878 1 : ScDPNumGroupInfo aInfo;
2879 1 : aInfo.mbEnable = true;
2880 1 : aInfo.mbAutoStart = true;
2881 1 : aInfo.mbAutoEnd = true;
2882 : {
2883 : // Turn the Date dimension into months. The first of the date
2884 : // dimensions is always a number-group dimension which replaces the
2885 : // original dimension.
2886 1 : ScDPSaveNumGroupDimension aGroup(aBaseDimName, aInfo, sheet::DataPilotFieldGroupBy::MONTHS);
2887 1 : pDimData->AddNumGroupDimension(aGroup);
2888 : }
2889 :
2890 : {
2891 : // Add quarter dimension. This will be an additional dimension.
2892 : rtl::OUString aGroupDimName =
2893 : pDimData->CreateDateGroupDimName(
2894 1 : sheet::DataPilotFieldGroupBy::QUARTERS, *pDPObj, true, NULL);
2895 1 : ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
2896 1 : aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::QUARTERS);
2897 1 : pDimData->AddGroupDimension(aGroupDim);
2898 :
2899 : // Set orientation.
2900 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
2901 1 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
2902 1 : pSaveData->SetPosition(pDim, 0); // set it to the left end.
2903 : }
2904 :
2905 : {
2906 : // Add year dimension. This is a new dimension also.
2907 : rtl::OUString aGroupDimName =
2908 : pDimData->CreateDateGroupDimName(
2909 1 : sheet::DataPilotFieldGroupBy::YEARS, *pDPObj, true, NULL);
2910 1 : ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
2911 1 : aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::YEARS);
2912 1 : pDimData->AddGroupDimension(aGroupDim);
2913 :
2914 : // Set orientation.
2915 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
2916 1 : pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
2917 1 : pSaveData->SetPosition(pDim, 0); // set it to the left end.
2918 : }
2919 :
2920 1 : pDPObj->SetSaveData(*pSaveData);
2921 1 : ScRange aOutRange = refreshGroups(pDPs, pDPObj);
2922 : {
2923 : // Expected output table content. 0 = empty cell
2924 : const char* aOutputCheck[][4] = {
2925 : { "Years", "Quarters", "Date", 0 },
2926 : { "2011", "Q1", "Jan", "1" },
2927 : { 0, 0, "Mar", "2" },
2928 : { 0, "Q3", "Sep", "7" },
2929 : { "2012", "Q1", "Jan", "3" },
2930 : { 0, 0, "Feb", "9" },
2931 : { 0, 0, "Mar", "6" },
2932 : { 0, "Q4", "Dec", "8" },
2933 : { "Total Result", 0, 0, "36" },
2934 1 : };
2935 :
2936 1 : bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Years, quarters and months date groups.");
2937 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2938 : }
2939 :
2940 : {
2941 : // Let's hide year 2012.
2942 1 : pSaveData = pDPObj->GetSaveData();
2943 1 : ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(rtl::OUString("Years"));
2944 1 : CPPUNIT_ASSERT_MESSAGE("Years dimension should exist.", pDim);
2945 1 : ScDPSaveMember* pMem = pDim->GetMemberByName(rtl::OUString("2012"));
2946 1 : CPPUNIT_ASSERT_MESSAGE("Member should exist.", pMem);
2947 1 : pMem->SetIsVisible(false);
2948 : }
2949 1 : pDPObj->SetSaveData(*pSaveData);
2950 1 : pDPObj->ReloadGroupTableData();
2951 1 : pDPObj->InvalidateData();
2952 :
2953 1 : aOutRange = refresh(pDPObj);
2954 : {
2955 : // Expected output table content. 0 = empty cell
2956 : const char* aOutputCheck[][4] = {
2957 : { "Years", "Quarters", "Date", 0 },
2958 : { "2011", "Q1", "Jan", "1" },
2959 : { 0, 0, "Mar", "2" },
2960 : { 0, "Q3", "Sep", "7" },
2961 : { "Total Result", 0, 0, "10" },
2962 1 : };
2963 :
2964 1 : bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Year 2012 data now hidden");
2965 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2966 : }
2967 :
2968 : // Remove all date grouping. The source dimension "Date" has two
2969 : // external dimensions ("Years" and "Quarters") and one internal ("Date"
2970 : // the same name but different hierarchy). Remove all of them.
2971 1 : pSaveData = pDPObj->GetSaveData();
2972 1 : pSaveData->RemoveAllGroupDimensions(aBaseDimName);
2973 1 : pDPObj->SetSaveData(*pSaveData);
2974 1 : pDPObj->ReloadGroupTableData();
2975 1 : pDPObj->InvalidateData();
2976 :
2977 1 : aOutRange = refresh(pDPObj);
2978 : {
2979 : // Expected output table content. 0 = empty cell
2980 : const char* aOutputCheck[][2] = {
2981 : { "Date", 0 },
2982 : { "2011-01-01", "1" },
2983 : { "2011-03-02", "2" },
2984 : { "2011-09-03", "7" },
2985 : { "2012-01-04", "3" },
2986 : { "2012-02-23", "4" },
2987 : { "2012-02-24", "5" },
2988 : { "2012-03-15", "6" },
2989 : { "2012-12-25", "8" },
2990 : { "Total Result", "36" }
2991 1 : };
2992 :
2993 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Remove all date grouping.");
2994 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
2995 : }
2996 :
2997 1 : pDPs->FreeTable(pDPObj);
2998 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
2999 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3000 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3001 :
3002 1 : m_pDoc->DeleteTab(1);
3003 1 : m_pDoc->DeleteTab(0);
3004 1 : }
3005 :
3006 1 : void Test::testPivotTableEmptyRows()
3007 : {
3008 1 : m_pDoc->InsertTab(0, OUString("Data"));
3009 1 : m_pDoc->InsertTab(1, OUString("Table"));
3010 :
3011 : // Raw data
3012 : const char* aData[][2] = {
3013 : { "Name", "Value" },
3014 : { "A", "1" },
3015 : { "B", "2" },
3016 : { "C", "3" },
3017 : { "D", "4" },
3018 1 : };
3019 :
3020 : // Dimension definition
3021 : DPFieldDef aFields[] = {
3022 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3023 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3024 1 : };
3025 :
3026 1 : ScAddress aPos(1,1,0);
3027 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3028 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3029 :
3030 : // Extend the range downward to include some trailing empty rows.
3031 1 : aDataRange.aEnd.IncRow(2);
3032 :
3033 : ScDPObject* pDPObj = createDPFromRange(
3034 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3035 :
3036 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3037 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3038 :
3039 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3040 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3041 1 : pDPs->GetCount() == 1);
3042 1 : pDPObj->SetName(pDPs->CreateNewName());
3043 :
3044 1 : ScRange aOutRange = refresh(pDPObj);
3045 :
3046 : {
3047 : // Expected output table content. 0 = empty cell
3048 : const char* aOutputCheck[][2] = {
3049 : { "Name", 0 },
3050 : { "A", "1" },
3051 : { "B", "2" },
3052 : { "C", "3" },
3053 : { "D", "4" },
3054 : { "(empty)", 0 },
3055 : { "Total Result", "10" },
3056 1 : };
3057 :
3058 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Include empty rows");
3059 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3060 : }
3061 :
3062 : // This time, ignore empty rows.
3063 1 : ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3064 1 : CPPUNIT_ASSERT_MESSAGE("Save data doesn't exist.", pSaveData);
3065 1 : pSaveData->SetIgnoreEmptyRows(true);
3066 1 : pDPObj->ClearTableData();
3067 1 : aOutRange = refresh(pDPObj);
3068 :
3069 : {
3070 : // Expected output table content. 0 = empty cell
3071 : const char* aOutputCheck[][2] = {
3072 : { "Name", 0 },
3073 : { "A", "1" },
3074 : { "B", "2" },
3075 : { "C", "3" },
3076 : { "D", "4" },
3077 : { "Total Result", "10" },
3078 1 : };
3079 :
3080 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
3081 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3082 : }
3083 :
3084 : // Modify the source to remove member 'A', then refresh the table.
3085 1 : m_pDoc->SetString(1, 2, 0, "B");
3086 :
3087 1 : std::set<ScDPObject*> aRefs;
3088 1 : sal_uLong nErr = pDPs->ReloadCache(pDPObj, aRefs);
3089 1 : CPPUNIT_ASSERT_MESSAGE("Failed to reload cache.", !nErr);
3090 2 : CPPUNIT_ASSERT_MESSAGE("There should only be one pivot table linked to this cache.",
3091 1 : aRefs.size() == 1 && *aRefs.begin() == pDPObj);
3092 :
3093 1 : pDPObj->ClearTableData();
3094 1 : aOutRange = refresh(pDPObj);
3095 :
3096 : {
3097 : // Expected output table content. 0 = empty cell
3098 : const char* aOutputCheck[][2] = {
3099 : { "Name", 0 },
3100 : { "B", "3" },
3101 : { "C", "3" },
3102 : { "D", "4" },
3103 : { "Total Result", "10" },
3104 1 : };
3105 :
3106 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
3107 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3108 : }
3109 :
3110 1 : pDPs->FreeTable(pDPObj);
3111 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3112 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3113 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3114 :
3115 1 : m_pDoc->DeleteTab(1);
3116 1 : m_pDoc->DeleteTab(0);
3117 1 : }
3118 :
3119 1 : void Test::testPivotTableTextNumber()
3120 : {
3121 1 : m_pDoc->InsertTab(0, OUString("Data"));
3122 1 : m_pDoc->InsertTab(1, OUString("Table"));
3123 :
3124 : // Raw data
3125 : const char* aData[][2] = {
3126 : { "Name", "Value" },
3127 : { "0001", "1" },
3128 : { "0002", "2" },
3129 : { "0003", "3" },
3130 : { "0004", "4" },
3131 1 : };
3132 :
3133 : // Dimension definition
3134 : DPFieldDef aFields[] = {
3135 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3136 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3137 1 : };
3138 :
3139 : // Insert raw data such that the first column values are entered as text.
3140 1 : size_t nRowCount = SAL_N_ELEMENTS(aData);
3141 6 : for (size_t nRow = 0; nRow < nRowCount; ++nRow)
3142 : {
3143 5 : ScSetStringParam aParam;
3144 5 : aParam.mbDetectNumberFormat = false;
3145 5 : aParam.meSetTextNumFormat = ScSetStringParam::Always;
3146 5 : m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aData[nRow][0]), &aParam);
3147 5 : aParam.meSetTextNumFormat = ScSetStringParam::Never;
3148 5 : m_pDoc->SetString(1, nRow, 0, OUString::createFromAscii(aData[nRow][1]), &aParam);
3149 :
3150 5 : if (nRow == 0)
3151 : // Don't check the header row.
3152 1 : continue;
3153 :
3154 : // Check the data rows.
3155 4 : CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be text.", m_pDoc->HasStringData(0, nRow, 0));
3156 4 : CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be numeric.", m_pDoc->HasValueData(1, nRow, 0));
3157 : }
3158 :
3159 1 : ScRange aDataRange(0, 0, 0, 1, 4, 0);
3160 :
3161 : ScDPObject* pDPObj = createDPFromRange(
3162 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3163 :
3164 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3165 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3166 :
3167 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3168 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3169 1 : pDPs->GetCount() == 1);
3170 1 : pDPObj->SetName(pDPs->CreateNewName());
3171 :
3172 1 : ScRange aOutRange = refresh(pDPObj);
3173 :
3174 : {
3175 : // Expected output table content. 0 = empty cell
3176 : const char* aOutputCheck[][2] = {
3177 : { "Name", 0 },
3178 : { "0001", "1" },
3179 : { "0002", "2" },
3180 : { "0003", "3" },
3181 : { "0004", "4" },
3182 : { "Total Result", "10" },
3183 1 : };
3184 :
3185 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Text number field members");
3186 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3187 : }
3188 :
3189 1 : pDPs->FreeTable(pDPObj);
3190 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3191 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3192 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3193 :
3194 1 : m_pDoc->DeleteTab(1);
3195 1 : m_pDoc->DeleteTab(0);
3196 1 : }
3197 :
3198 1 : void Test::testPivotTableCaseInsensitiveStrings()
3199 : {
3200 1 : m_pDoc->InsertTab(0, OUString("Data"));
3201 1 : m_pDoc->InsertTab(1, OUString("Table"));
3202 :
3203 : // Raw data
3204 : const char* aData[][2] = {
3205 : { "Name", "Value" },
3206 : { "A", "1" },
3207 : { "a", "2" },
3208 1 : };
3209 :
3210 : // Dimension definition
3211 : DPFieldDef aFields[] = {
3212 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3213 : { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3214 1 : };
3215 :
3216 1 : ScAddress aPos(1,1,0);
3217 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
3218 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
3219 :
3220 : ScDPObject* pDPObj = createDPFromRange(
3221 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3222 :
3223 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3224 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3225 :
3226 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3227 2 : CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
3228 1 : pDPs->GetCount() == 1);
3229 1 : pDPObj->SetName(pDPs->CreateNewName());
3230 :
3231 1 : ScRange aOutRange = refresh(pDPObj);
3232 :
3233 : {
3234 : // Expected output table content. 0 = empty cell
3235 : const char* aOutputCheck[][2] = {
3236 : { "Name", 0 },
3237 : { "A", "3" },
3238 : { "Total Result", "3" },
3239 1 : };
3240 :
3241 1 : bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Case insensitive strings");
3242 1 : CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
3243 : }
3244 :
3245 1 : pDPs->FreeTable(pDPObj);
3246 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3247 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3248 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3249 :
3250 1 : m_pDoc->DeleteTab(1);
3251 1 : m_pDoc->DeleteTab(0);
3252 1 : }
3253 :
3254 1 : void Test::testPivotTableNumStability()
3255 : {
3256 1 : FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
3257 :
3258 : // Raw Data
3259 : const char* aData[][4] = {
3260 : { "Name", "Time Start", "Time End", "Total" },
3261 : { "Sam", "07:48 AM", "09:00 AM", "=RC[-1]-RC[-2]" },
3262 : { "Sam", "09:00 AM", "10:30 AM", "=RC[-1]-RC[-2]" },
3263 : { "Sam", "10:30 AM", "12:30 PM", "=RC[-1]-RC[-2]" },
3264 : { "Sam", "12:30 PM", "01:00 PM", "=RC[-1]-RC[-2]" },
3265 : { "Sam", "01:00 PM", "01:30 PM", "=RC[-1]-RC[-2]" },
3266 : { "Sam", "01:30 PM", "02:00 PM", "=RC[-1]-RC[-2]" },
3267 : { "Sam", "02:00 PM", "07:15 PM", "=RC[-1]-RC[-2]" },
3268 : { "Sam", "07:47 AM", "09:00 AM", "=RC[-1]-RC[-2]" },
3269 : { "Sam", "09:00 AM", "10:00 AM", "=RC[-1]-RC[-2]" },
3270 : { "Sam", "10:00 AM", "11:00 AM", "=RC[-1]-RC[-2]" },
3271 : { "Sam", "11:00 AM", "11:30 AM", "=RC[-1]-RC[-2]" },
3272 : { "Sam", "11:30 AM", "12:45 PM", "=RC[-1]-RC[-2]" },
3273 : { "Sam", "12:45 PM", "01:15 PM", "=RC[-1]-RC[-2]" },
3274 : { "Sam", "01:15 PM", "02:30 PM", "=RC[-1]-RC[-2]" },
3275 : { "Sam", "02:30 PM", "02:45 PM", "=RC[-1]-RC[-2]" },
3276 : { "Sam", "02:45 PM", "04:30 PM", "=RC[-1]-RC[-2]" },
3277 : { "Sam", "04:30 PM", "06:00 PM", "=RC[-1]-RC[-2]" },
3278 : { "Sam", "06:00 PM", "07:15 PM", "=RC[-1]-RC[-2]" },
3279 : { "Mike", "06:15 AM", "08:30 AM", "=RC[-1]-RC[-2]" },
3280 : { "Mike", "08:30 AM", "10:03 AM", "=RC[-1]-RC[-2]" },
3281 : { "Mike", "10:03 AM", "12:00 PM", "=RC[-1]-RC[-2]" },
3282 : { "Dennis", "11:00 AM", "01:00 PM", "=RC[-1]-RC[-2]" },
3283 : { "Dennis", "01:00 PM", "02:00 PM", "=RC[-1]-RC[-2]" }
3284 1 : };
3285 :
3286 : // Dimension definition
3287 : DPFieldDef aFields[] = {
3288 : { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
3289 : { "Total", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
3290 1 : };
3291 :
3292 1 : m_pDoc->InsertTab(0, OUString("Data"));
3293 1 : m_pDoc->InsertTab(1, OUString("Table"));
3294 :
3295 1 : size_t nRowCount = SAL_N_ELEMENTS(aData);
3296 1 : ScAddress aPos(1,1,0);
3297 1 : ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, nRowCount);
3298 :
3299 : // Insert formulas to manually calculate sums for each name.
3300 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])");
3301 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])");
3302 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])");
3303 :
3304 1 : m_pDoc->CalcAll();
3305 :
3306 : // Get correct sum values.
3307 1 : double fDennisTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+1, aDataRange.aStart.Tab());
3308 1 : double fMikeTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+2, aDataRange.aStart.Tab());
3309 1 : double fSamTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+3, aDataRange.aStart.Tab());
3310 :
3311 : ScDPObject* pDPObj = createDPFromRange(
3312 1 : m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
3313 :
3314 1 : ScDPCollection* pDPs = m_pDoc->GetDPCollection();
3315 1 : bool bSuccess = pDPs->InsertNewTable(pDPObj);
3316 :
3317 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
3318 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
3319 1 : pDPs->GetCount(), static_cast<size_t>(1));
3320 1 : pDPObj->SetName(pDPs->CreateNewName());
3321 :
3322 1 : ScRange aOutRange = refresh(pDPObj);
3323 :
3324 : // Manually check the total value for each name.
3325 : //
3326 : // +--------------+----------------+
3327 : // | Name | |
3328 : // +--------------+----------------+
3329 : // | Dennis | <Dennis total> |
3330 : // +--------------+----------------+
3331 : // | Mike | <Miks total> |
3332 : // +--------------+----------------+
3333 : // | Sam | <Sam total> |
3334 : // +--------------+----------------+
3335 : // | Total Result | ... |
3336 : // +--------------+----------------+
3337 :
3338 1 : aPos = aOutRange.aStart;
3339 1 : aPos.IncCol();
3340 1 : aPos.IncRow();
3341 1 : double fTest = m_pDoc->GetValue(aPos);
3342 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect value for Dennis.", rtl::math::approxEqual(fTest, fDennisTotal));
3343 1 : aPos.IncRow();
3344 1 : fTest = m_pDoc->GetValue(aPos);
3345 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect value for Mike.", rtl::math::approxEqual(fTest, fMikeTotal));
3346 1 : aPos.IncRow();
3347 1 : fTest = m_pDoc->GetValue(aPos);
3348 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect value for Sam.", rtl::math::approxEqual(fTest, fSamTotal));
3349 :
3350 1 : pDPs->FreeTable(pDPObj);
3351 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
3352 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
3353 1 : pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
3354 :
3355 1 : m_pDoc->DeleteTab(1);
3356 1 : m_pDoc->DeleteTab(0);
3357 1 : }
3358 :
3359 1 : void Test::testSheetCopy()
3360 : {
3361 1 : OUString aTabName("TestTab");
3362 1 : m_pDoc->InsertTab(0, aTabName);
3363 1 : CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
3364 : SCROW nRow1, nRow2;
3365 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3366 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
3367 :
3368 : // Copy and test the result.
3369 1 : m_pDoc->CopyTab(0, 1);
3370 1 : CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
3371 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
3372 1 : CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
3373 1 : m_pDoc->DeleteTab(1);
3374 :
3375 1 : m_pDoc->SetRowHidden(5, 10, 0, true);
3376 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3377 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
3378 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3379 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
3380 1 : bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
3381 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
3382 :
3383 : // Copy the sheet once again.
3384 1 : m_pDoc->CopyTab(0, 1);
3385 1 : CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
3386 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
3387 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
3388 1 : bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
3389 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
3390 1 : bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
3391 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
3392 1 : m_pDoc->DeleteTab(1);
3393 1 : m_pDoc->DeleteTab(0);
3394 1 : }
3395 :
3396 1 : void Test::testSheetMove()
3397 : {
3398 1 : OUString aTabName("TestTab1");
3399 1 : m_pDoc->InsertTab(0, aTabName);
3400 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount(), static_cast<SCTAB>(1));
3401 : SCROW nRow1, nRow2;
3402 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3403 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
3404 :
3405 : //test if inserting before another sheet works
3406 1 : m_pDoc->InsertTab(0, OUString("TestTab2"));
3407 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
3408 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3409 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
3410 :
3411 : // Move and test the result.
3412 1 : m_pDoc->MoveTab(0, 1);
3413 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
3414 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
3415 1 : CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
3416 1 : rtl::OUString aName;
3417 1 : m_pDoc->GetName(0, aName);
3418 1 : CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab1" );
3419 :
3420 1 : m_pDoc->SetRowHidden(5, 10, 0, true);
3421 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
3422 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
3423 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
3424 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
3425 1 : bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
3426 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
3427 :
3428 : // Move the sheet once again.
3429 1 : m_pDoc->MoveTab(1, 0);
3430 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
3431 1 : bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
3432 1 : CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
3433 1 : bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
3434 1 : CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
3435 1 : bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
3436 1 : CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
3437 1 : m_pDoc->GetName(0, aName);
3438 1 : CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab2" );
3439 1 : m_pDoc->DeleteTab(1);
3440 1 : m_pDoc->DeleteTab(0);
3441 1 : }
3442 :
3443 5 : ScDocShell* findLoadedDocShellByName(const OUString& rName)
3444 : {
3445 5 : TypeId aType(TYPE(ScDocShell));
3446 5 : ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
3447 175 : while (pShell)
3448 : {
3449 168 : SfxMedium* pMedium = pShell->GetMedium();
3450 168 : if (pMedium)
3451 : {
3452 4 : OUString aName = pMedium->GetName();
3453 4 : if (aName.equals(rName))
3454 3 : return pShell;
3455 : }
3456 165 : pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
3457 : }
3458 2 : return NULL;
3459 : }
3460 :
3461 2 : ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
3462 : {
3463 2 : ScRange aRange;
3464 :
3465 2 : vector<SCROW> aRows;
3466 2 : pCacheTab->getAllRows(aRows);
3467 2 : vector<SCROW>::const_iterator itrRow = aRows.begin(), itrRowEnd = aRows.end();
3468 2 : bool bFirst = true;
3469 11 : for (; itrRow != itrRowEnd; ++itrRow)
3470 : {
3471 9 : SCROW nRow = *itrRow;
3472 9 : vector<SCCOL> aCols;
3473 9 : pCacheTab->getAllCols(nRow, aCols);
3474 9 : vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end();
3475 27 : for (; itrCol != itrColEnd; ++itrCol)
3476 : {
3477 18 : SCCOL nCol = *itrCol;
3478 18 : if (bFirst)
3479 : {
3480 2 : aRange.aStart = ScAddress(nCol, nRow, 0);
3481 2 : aRange.aEnd = aRange.aStart;
3482 2 : bFirst = false;
3483 : }
3484 : else
3485 : {
3486 16 : if (nCol < aRange.aStart.Col())
3487 0 : aRange.aStart.SetCol(nCol);
3488 16 : else if (aRange.aEnd.Col() < nCol)
3489 2 : aRange.aEnd.SetCol(nCol);
3490 :
3491 16 : if (nRow < aRange.aStart.Row())
3492 0 : aRange.aStart.SetRow(nRow);
3493 16 : else if (aRange.aEnd.Row() < nRow)
3494 7 : aRange.aEnd.SetRow(nRow);
3495 : }
3496 : }
3497 9 : }
3498 2 : return aRange;
3499 : }
3500 :
3501 1 : void Test::testExternalRef()
3502 : {
3503 1 : ScDocShellRef xExtDocSh = new ScDocShell;
3504 1 : OUString aExtDocName("file:///extdata.fake");
3505 1 : OUString aExtSh1Name("Data1");
3506 1 : OUString aExtSh2Name("Data2");
3507 1 : OUString aExtSh3Name("Data3");
3508 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
3509 1 : xExtDocSh->DoInitNew(pMed);
3510 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
3511 1 : findLoadedDocShellByName(aExtDocName) != NULL);
3512 :
3513 : // Populate the external source document.
3514 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
3515 1 : pExtDoc->InsertTab(0, aExtSh1Name);
3516 1 : pExtDoc->InsertTab(1, aExtSh2Name);
3517 1 : pExtDoc->InsertTab(2, aExtSh3Name);
3518 :
3519 1 : OUString name("Name");
3520 1 : OUString value("Value");
3521 1 : OUString andy("Andy");
3522 1 : OUString bruce("Bruce");
3523 1 : OUString charlie("Charlie");
3524 1 : OUString david("David");
3525 1 : OUString edward("Edward");
3526 1 : OUString frank("Frank");
3527 1 : OUString george("George");
3528 1 : OUString henry("Henry");
3529 :
3530 : // Sheet 1
3531 1 : pExtDoc->SetString(0, 0, 0, name);
3532 1 : pExtDoc->SetString(0, 1, 0, andy);
3533 1 : pExtDoc->SetString(0, 2, 0, bruce);
3534 1 : pExtDoc->SetString(0, 3, 0, charlie);
3535 1 : pExtDoc->SetString(0, 4, 0, david);
3536 1 : pExtDoc->SetString(1, 0, 0, value);
3537 1 : double val = 10;
3538 1 : pExtDoc->SetValue(1, 1, 0, val);
3539 1 : val = 11;
3540 1 : pExtDoc->SetValue(1, 2, 0, val);
3541 1 : val = 12;
3542 1 : pExtDoc->SetValue(1, 3, 0, val);
3543 1 : val = 13;
3544 1 : pExtDoc->SetValue(1, 4, 0, val);
3545 :
3546 : // Sheet 2 remains empty.
3547 :
3548 : // Sheet 3
3549 1 : pExtDoc->SetString(0, 0, 2, name);
3550 1 : pExtDoc->SetString(0, 1, 2, edward);
3551 1 : pExtDoc->SetString(0, 2, 2, frank);
3552 1 : pExtDoc->SetString(0, 3, 2, george);
3553 1 : pExtDoc->SetString(0, 4, 2, henry);
3554 1 : pExtDoc->SetString(1, 0, 2, value);
3555 1 : val = 99;
3556 1 : pExtDoc->SetValue(1, 1, 2, val);
3557 1 : val = 98;
3558 1 : pExtDoc->SetValue(1, 2, 2, val);
3559 1 : val = 97;
3560 1 : pExtDoc->SetValue(1, 3, 2, val);
3561 1 : val = 96;
3562 1 : pExtDoc->SetValue(1, 4, 2, val);
3563 :
3564 : // Test external refernces on the main document while the external
3565 : // document is still in memory.
3566 1 : OUString test;
3567 1 : m_pDoc->InsertTab(0, OUString("Test Sheet"));
3568 1 : m_pDoc->SetString(0, 0, 0, OUString("='file:///extdata.fake'#Data1.A1"));
3569 1 : m_pDoc->GetString(0, 0, 0, test);
3570 1 : CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(name));
3571 :
3572 : // After the initial access to the external document, the external ref
3573 : // manager should create sheet cache entries for *all* sheets from that
3574 : // document. Note that the doc may have more than 3 sheets but ensure
3575 : // that the first 3 are what we expect.
3576 1 : ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
3577 1 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
3578 1 : vector<OUString> aTabNames;
3579 1 : pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
3580 1 : CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
3581 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[0].equals(aExtSh1Name));
3582 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[1].equals(aExtSh2Name));
3583 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[2].equals(aExtSh3Name));
3584 :
3585 1 : m_pDoc->SetString(1, 0, 0, OUString("='file:///extdata.fake'#Data1.B1"));
3586 1 : m_pDoc->GetString(1, 0, 0, test);
3587 1 : CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(value));
3588 :
3589 1 : m_pDoc->SetString(0, 1, 0, OUString("='file:///extdata.fake'#Data1.A2"));
3590 1 : m_pDoc->SetString(0, 2, 0, OUString("='file:///extdata.fake'#Data1.A3"));
3591 1 : m_pDoc->SetString(0, 3, 0, OUString("='file:///extdata.fake'#Data1.A4"));
3592 1 : m_pDoc->SetString(0, 4, 0, OUString("='file:///extdata.fake'#Data1.A5"));
3593 1 : m_pDoc->SetString(0, 5, 0, OUString("='file:///extdata.fake'#Data1.A6"));
3594 :
3595 : {
3596 : // Referencing an empty cell should display '0'.
3597 1 : const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
3598 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3599 : {
3600 5 : m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0, test);
3601 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
3602 : }
3603 : }
3604 1 : m_pDoc->SetString(1, 1, 0, OUString("='file:///extdata.fake'#Data1.B2"));
3605 1 : m_pDoc->SetString(1, 2, 0, OUString("='file:///extdata.fake'#Data1.B3"));
3606 1 : m_pDoc->SetString(1, 3, 0, OUString("='file:///extdata.fake'#Data1.B4"));
3607 1 : m_pDoc->SetString(1, 4, 0, OUString("='file:///extdata.fake'#Data1.B5"));
3608 1 : m_pDoc->SetString(1, 5, 0, OUString("='file:///extdata.fake'#Data1.B6"));
3609 : {
3610 1 : double pChecks[] = { 10, 11, 12, 13, 0 };
3611 6 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3612 : {
3613 5 : m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
3614 5 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", val == pChecks[i]);
3615 : }
3616 : }
3617 :
3618 1 : m_pDoc->SetString(2, 0, 0, OUString("='file:///extdata.fake'#Data3.A1"));
3619 1 : m_pDoc->SetString(2, 1, 0, OUString("='file:///extdata.fake'#Data3.A2"));
3620 1 : m_pDoc->SetString(2, 2, 0, OUString("='file:///extdata.fake'#Data3.A3"));
3621 1 : m_pDoc->SetString(2, 3, 0, OUString("='file:///extdata.fake'#Data3.A4"));
3622 : {
3623 1 : const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
3624 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3625 : {
3626 4 : m_pDoc->GetString(2, static_cast<SCROW>(i), 0, test);
3627 4 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
3628 : }
3629 : }
3630 :
3631 1 : m_pDoc->SetString(3, 0, 0, OUString("='file:///extdata.fake'#Data3.B1"));
3632 1 : m_pDoc->SetString(3, 1, 0, OUString("='file:///extdata.fake'#Data3.B2"));
3633 1 : m_pDoc->SetString(3, 2, 0, OUString("='file:///extdata.fake'#Data3.B3"));
3634 1 : m_pDoc->SetString(3, 3, 0, OUString("='file:///extdata.fake'#Data3.B4"));
3635 : {
3636 1 : const char* pChecks[] = { "Value", "99", "98", "97" };
3637 5 : for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
3638 : {
3639 4 : m_pDoc->GetString(3, static_cast<SCROW>(i), 0, test);
3640 4 : CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
3641 : }
3642 : }
3643 :
3644 : // At this point, all accessed cell data from the external document should
3645 : // have been cached.
3646 : ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
3647 1 : nFileId, aExtSh1Name, false);
3648 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != NULL);
3649 1 : ScRange aCachedRange = getCachedRange(pCacheTab);
3650 2 : CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
3651 : aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
3652 1 : aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
3653 :
3654 : // Sheet2 is not referenced at all; the cache table shouldn't even exist.
3655 1 : pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
3656 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", pCacheTab.get() == NULL);
3657 :
3658 : // Sheet3's row 5 is not referenced; it should not be cached.
3659 1 : pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
3660 1 : CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != NULL);
3661 1 : aCachedRange = getCachedRange(pCacheTab);
3662 2 : CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
3663 : aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
3664 1 : aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
3665 :
3666 : // Unload the external document shell.
3667 1 : xExtDocSh->DoClose();
3668 2 : CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
3669 1 : findLoadedDocShellByName(aExtDocName) == NULL);
3670 :
3671 1 : m_pDoc->DeleteTab(0);
3672 1 : }
3673 :
3674 1 : void testExtRefFuncT(ScDocument* pDoc, ScDocument* pExtDoc)
3675 : {
3676 1 : clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
3677 1 : clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
3678 :
3679 1 : pExtDoc->SetString(0, 0, 0, rtl::OUString("'1.2"));
3680 1 : pExtDoc->SetString(0, 1, 0, rtl::OUString("Foo"));
3681 1 : pExtDoc->SetValue(0, 2, 0, 12.3);
3682 1 : pDoc->SetString(0, 0, 0, rtl::OUString("=T('file:///extdata.fake'#Data.A1)"));
3683 1 : pDoc->SetString(0, 1, 0, rtl::OUString("=T('file:///extdata.fake'#Data.A2)"));
3684 1 : pDoc->SetString(0, 2, 0, rtl::OUString("=T('file:///extdata.fake'#Data.A3)"));
3685 1 : pDoc->CalcAll();
3686 :
3687 1 : rtl::OUString aRes = pDoc->GetString(0, 0, 0);
3688 1 : CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "1.2" );
3689 1 : aRes = pDoc->GetString(0, 1, 0);
3690 1 : CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "Foo" );
3691 1 : aRes = pDoc->GetString(0, 2, 0);
3692 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
3693 1 : }
3694 :
3695 1 : void Test::testExternalRefFunctions()
3696 : {
3697 1 : ScDocShellRef xExtDocSh = new ScDocShell;
3698 1 : OUString aExtDocName("file:///extdata.fake");
3699 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
3700 1 : xExtDocSh->DoInitNew(pMed);
3701 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
3702 1 : findLoadedDocShellByName(aExtDocName) != NULL);
3703 :
3704 1 : ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
3705 1 : CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
3706 1 : sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
3707 1 : const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
3708 2 : CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
3709 1 : pFileName && pFileName->equals(aExtDocName));
3710 :
3711 : // Populate the external source document.
3712 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
3713 1 : pExtDoc->InsertTab(0, rtl::OUString("Data"));
3714 1 : double val = 1;
3715 1 : pExtDoc->SetValue(0, 0, 0, val);
3716 : // leave cell B1 empty.
3717 1 : val = 2;
3718 1 : pExtDoc->SetValue(0, 1, 0, val);
3719 1 : pExtDoc->SetValue(1, 1, 0, val);
3720 1 : val = 3;
3721 1 : pExtDoc->SetValue(0, 2, 0, val);
3722 1 : pExtDoc->SetValue(1, 2, 0, val);
3723 1 : val = 4;
3724 1 : pExtDoc->SetValue(0, 3, 0, val);
3725 1 : pExtDoc->SetValue(1, 3, 0, val);
3726 :
3727 1 : m_pDoc->InsertTab(0, rtl::OUString("Test"));
3728 :
3729 : struct {
3730 : const char* pFormula; double fResult;
3731 : } aChecks[] = {
3732 : { "=SUM('file:///extdata.fake'#Data.A1:A4)", 10 },
3733 : { "=SUM('file:///extdata.fake'#Data.B1:B4)", 9 },
3734 : { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
3735 : { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
3736 : { "=COUNT('file:///extdata.fake'#Data.A1:A4)", 4 },
3737 : { "=COUNT('file:///extdata.fake'#Data.B1:B4)", 3 }
3738 1 : };
3739 :
3740 7 : for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
3741 : {
3742 6 : m_pDoc->SetString(0, 0, 0, rtl::OUString::createFromAscii(aChecks[i].pFormula));
3743 6 : m_pDoc->CalcAll();
3744 6 : m_pDoc->GetValue(0, 0, 0, val);
3745 6 : CPPUNIT_ASSERT_MESSAGE("unexpected result involving external ranges.", val == aChecks[i].fResult);
3746 : }
3747 :
3748 1 : pRefMgr->clearCache(nFileId);
3749 1 : testExtRefFuncT(m_pDoc, pExtDoc);
3750 :
3751 : // Unload the external document shell.
3752 1 : xExtDocSh->DoClose();
3753 2 : CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
3754 1 : findLoadedDocShellByName(aExtDocName) == NULL);
3755 :
3756 1 : m_pDoc->DeleteTab(0);
3757 1 : }
3758 :
3759 1 : void Test::testDataArea()
3760 : {
3761 1 : m_pDoc->InsertTab(0, OUString("Data"));
3762 :
3763 : // Totally empty sheet should be rightfully considered empty in all accounts.
3764 1 : CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
3765 1 : CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
3766 :
3767 : // Now, set borders in some cells....
3768 1 : ::editeng::SvxBorderLine aLine(NULL, 50, table::BorderLineStyle::SOLID);
3769 1 : SvxBoxItem aBorderItem(ATTR_BORDER);
3770 1 : aBorderItem.SetLine(&aLine, BOX_LINE_LEFT);
3771 1 : aBorderItem.SetLine(&aLine, BOX_LINE_RIGHT);
3772 101 : for (SCROW i = 0; i < 100; ++i)
3773 : // Set borders from row 1 to 100.
3774 100 : m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
3775 :
3776 : // Now the sheet is considered non-empty for printing purposes, but still
3777 : // be empty in all the other cases.
3778 2 : CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
3779 1 : !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
3780 2 : CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
3781 1 : m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
3782 :
3783 : // Adding a real cell content should turn the block non-empty.
3784 1 : m_pDoc->SetString(0, 0, 0, OUString("Some text"));
3785 2 : CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
3786 1 : !m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
3787 :
3788 : // TODO: Add more tests for normal data area calculation.
3789 :
3790 1 : m_pDoc->DeleteTab(0);
3791 1 : }
3792 :
3793 1 : void Test::testStreamValid()
3794 : {
3795 1 : m_pDoc->InsertTab(0, OUString("Sheet1"));
3796 1 : m_pDoc->InsertTab(1, OUString("Sheet2"));
3797 1 : m_pDoc->InsertTab(2, OUString("Sheet3"));
3798 1 : m_pDoc->InsertTab(3, OUString("Sheet4"));
3799 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", m_pDoc->GetTableCount(), static_cast<SCTAB>(4));
3800 :
3801 1 : OUString a1("A1");
3802 1 : OUString a2("A2");
3803 1 : OUString test;
3804 :
3805 : // Put values into Sheet1.
3806 1 : m_pDoc->SetString(0, 0, 0, a1);
3807 1 : m_pDoc->SetString(0, 1, 0, a2);
3808 1 : m_pDoc->GetString(0, 0, 0, test);
3809 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A1", test.equals(a1));
3810 1 : m_pDoc->GetString(0, 1, 0, test);
3811 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A2", test.equals(a2));
3812 :
3813 : // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
3814 1 : m_pDoc->SetString(0, 0, 1, OUString("=Sheet1.A1"));
3815 1 : m_pDoc->SetString(0, 1, 1, OUString("=Sheet1.A2"));
3816 1 : m_pDoc->SetString(0, 0, 2, OUString("=Sheet1.A1"));
3817 1 : m_pDoc->SetString(0, 0, 3, OUString("=Sheet1.A2"));
3818 :
3819 1 : m_pDoc->GetString(0, 0, 1, test);
3820 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A1", test.equals(a1));
3821 1 : m_pDoc->GetString(0, 1, 1, test);
3822 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A2", test.equals(a2));
3823 1 : m_pDoc->GetString(0, 0, 2, test);
3824 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a1));
3825 1 : m_pDoc->GetString(0, 0, 3, test);
3826 1 : CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a2));
3827 :
3828 : // Set all sheet streams valid after all the initial cell values are in
3829 : // place. In reality we need to have real XML streams stored in order to
3830 : // claim they are valid, but we are just testing the flag values here.
3831 1 : m_pDoc->SetStreamValid(0, true);
3832 1 : m_pDoc->SetStreamValid(1, true);
3833 1 : m_pDoc->SetStreamValid(2, true);
3834 1 : m_pDoc->SetStreamValid(3, true);
3835 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
3836 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
3837 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
3838 1 : CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
3839 :
3840 : // Now, insert a new row at row 2 position on Sheet1. This will move cell
3841 : // A2 downward but cell A1 remains unmoved.
3842 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 2);
3843 1 : m_pDoc->GetString(0, 0, 0, test);
3844 1 : CPPUNIT_ASSERT_MESSAGE("Cell A1 should not have moved.", test.equals(a1));
3845 1 : m_pDoc->GetString(0, 3, 0, test);
3846 1 : CPPUNIT_ASSERT_MESSAGE("the old cell A2 should now be at A4.", test.equals(a2));
3847 1 : const ScBaseCell* pCell = m_pDoc->GetCell(ScAddress(0, 1, 0));
3848 1 : CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", pCell == NULL);
3849 1 : pCell = m_pDoc->GetCell(ScAddress(0, 2, 0));
3850 1 : CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", pCell == NULL);
3851 :
3852 : // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
3853 : // invalidated, whereas Sheet3's stream should still be valid.
3854 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
3855 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
3856 1 : CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
3857 1 : CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
3858 :
3859 1 : m_pDoc->DeleteTab(3);
3860 1 : m_pDoc->DeleteTab(2);
3861 1 : m_pDoc->DeleteTab(1);
3862 1 : m_pDoc->DeleteTab(0);
3863 1 : }
3864 :
3865 1 : void Test::testFunctionLists()
3866 : {
3867 : const char* aDataBase[] = {
3868 : "DAVERAGE",
3869 : "DCOUNT",
3870 : "DCOUNTA",
3871 : "DGET",
3872 : "DMAX",
3873 : "DMIN",
3874 : "DPRODUCT",
3875 : "DSTDEV",
3876 : "DSTDEVP",
3877 : "DSUM",
3878 : "DVAR",
3879 : "DVARP",
3880 : 0
3881 1 : };
3882 :
3883 : const char* aDateTime[] = {
3884 : "DATE",
3885 : "DATEDIF",
3886 : "DATEVALUE",
3887 : "DAY",
3888 : "DAYS",
3889 : "DAYS360",
3890 : "EASTERSUNDAY",
3891 : "HOUR",
3892 : "MINUTE",
3893 : "MONTH",
3894 : "NOW",
3895 : "SECOND",
3896 : "TIME",
3897 : "TIMEVALUE",
3898 : "TODAY",
3899 : "WEEKDAY",
3900 : "WEEKNUM",
3901 : "YEAR",
3902 : 0
3903 1 : };
3904 :
3905 : const char* aFinancial[] = {
3906 : "CUMIPMT",
3907 : "CUMPRINC",
3908 : "DB",
3909 : "DDB",
3910 : "DURATION",
3911 : "EFFECTIVE",
3912 : "FV",
3913 : "IPMT",
3914 : "IRR",
3915 : "ISPMT",
3916 : "MIRR",
3917 : "NOMINAL",
3918 : "NPER",
3919 : "NPV",
3920 : "PMT",
3921 : "PPMT",
3922 : "PV",
3923 : "RATE",
3924 : "RRI",
3925 : "SLN",
3926 : "SYD",
3927 : "VDB",
3928 : 0
3929 1 : };
3930 :
3931 : const char* aInformation[] = {
3932 : "CELL",
3933 : "CURRENT",
3934 : "FORMULA",
3935 : "INFO",
3936 : "ISBLANK",
3937 : "ISERR",
3938 : "ISERROR",
3939 : "ISFORMULA",
3940 : "ISLOGICAL",
3941 : "ISNA",
3942 : "ISNONTEXT",
3943 : "ISNUMBER",
3944 : "ISREF",
3945 : "ISTEXT",
3946 : "N",
3947 : "NA",
3948 : "TYPE",
3949 : 0
3950 1 : };
3951 :
3952 : const char* aLogical[] = {
3953 : "AND",
3954 : "FALSE",
3955 : "IF",
3956 : "NOT",
3957 : "OR",
3958 : "TRUE",
3959 : "XOR",
3960 : 0
3961 1 : };
3962 :
3963 : const char* aMathematical[] = {
3964 : "ABS",
3965 : "ACOS",
3966 : "ACOSH",
3967 : "ACOT",
3968 : "ACOTH",
3969 : "ASIN",
3970 : "ASINH",
3971 : "ATAN",
3972 : "ATAN2",
3973 : "ATANH",
3974 : "AVERAGEIF",
3975 : "AVERAGEIFS",
3976 : "BITAND",
3977 : "BITLSHIFT",
3978 : "BITOR",
3979 : "BITRSHIFT",
3980 : "BITXOR",
3981 : "CEILING",
3982 : "COMBIN",
3983 : "COMBINA",
3984 : "CONVERT",
3985 : "COS",
3986 : "COSH",
3987 : "COT",
3988 : "COTH",
3989 : "COUNTBLANK",
3990 : "COUNTIF",
3991 : "COUNTIFS",
3992 : "CSC",
3993 : "CSCH",
3994 : "DEGREES",
3995 : "EUROCONVERT",
3996 : "EVEN",
3997 : "EXP",
3998 : "FACT",
3999 : "FLOOR",
4000 : "GCD",
4001 : "INT",
4002 : "ISEVEN",
4003 : "ISODD",
4004 : "LCM",
4005 : "LN",
4006 : "LOG",
4007 : "LOG10",
4008 : "MOD",
4009 : "ODD",
4010 : "PI",
4011 : "POWER",
4012 : "PRODUCT",
4013 : "RADIANS",
4014 : "RAND",
4015 : "ROUND",
4016 : "ROUNDDOWN",
4017 : "ROUNDUP",
4018 : "SEC",
4019 : "SECH",
4020 : "SIGN",
4021 : "SIN",
4022 : "SINH",
4023 : "SQRT",
4024 : "SUBTOTAL",
4025 : "SUM",
4026 : "SUMIF",
4027 : "SUMIFS",
4028 : "SUMSQ",
4029 : "TAN",
4030 : "TANH",
4031 : "TRUNC",
4032 : 0
4033 1 : };
4034 :
4035 : const char* aArray[] = {
4036 : "FREQUENCY",
4037 : "GROWTH",
4038 : "LINEST",
4039 : "LOGEST",
4040 : "MDETERM",
4041 : "MINVERSE",
4042 : "MMULT",
4043 : "MUNIT",
4044 : "SUMPRODUCT",
4045 : "SUMX2MY2",
4046 : "SUMX2PY2",
4047 : "SUMXMY2",
4048 : "TRANSPOSE",
4049 : "TREND",
4050 : 0
4051 1 : };
4052 :
4053 : const char* aStatistical[] = {
4054 : "AVEDEV",
4055 : "AVERAGE",
4056 : "AVERAGEA",
4057 : "B",
4058 : "BETADIST",
4059 : "BETAINV",
4060 : "BINOMDIST",
4061 : "CHIDIST",
4062 : "CHIINV",
4063 : "CHISQDIST",
4064 : "CHISQINV",
4065 : "CHITEST",
4066 : "CONFIDENCE",
4067 : "CORREL",
4068 : "COUNT",
4069 : "COUNTA",
4070 : "COVAR",
4071 : "CRITBINOM",
4072 : "DEVSQ",
4073 : "EXPONDIST",
4074 : "FDIST",
4075 : "FINV",
4076 : "FISHER",
4077 : "FISHERINV",
4078 : "FORECAST",
4079 : "FTEST",
4080 : "GAMMA",
4081 : "GAMMADIST",
4082 : "GAMMAINV",
4083 : "GAMMALN",
4084 : "GAUSS",
4085 : "GEOMEAN",
4086 : "HARMEAN",
4087 : "HYPGEOMDIST",
4088 : "INTERCEPT",
4089 : "KURT",
4090 : "LARGE",
4091 : "LOGINV",
4092 : "LOGNORMDIST",
4093 : "MAX",
4094 : "MAXA",
4095 : "MEDIAN",
4096 : "MIN",
4097 : "MINA",
4098 : "MODE",
4099 : "NEGBINOMDIST",
4100 : "NORMDIST",
4101 : "NORMINV",
4102 : "NORMSDIST",
4103 : "NORMSINV",
4104 : "PEARSON",
4105 : "PERCENTILE",
4106 : "PERCENTRANK",
4107 : "PERMUT",
4108 : "PERMUTATIONA",
4109 : "PHI",
4110 : "POISSON",
4111 : "PROB",
4112 : "QUARTILE",
4113 : "RANK",
4114 : "RSQ",
4115 : "SKEW",
4116 : "SLOPE",
4117 : "SMALL",
4118 : "STANDARDIZE",
4119 : "STDEV",
4120 : "STDEVA",
4121 : "STDEVP",
4122 : "STDEVPA",
4123 : "STEYX",
4124 : "TDIST",
4125 : "TINV",
4126 : "TRIMMEAN",
4127 : "TTEST",
4128 : "VAR",
4129 : "VARA",
4130 : "VARP",
4131 : "VARPA",
4132 : "WEIBULL",
4133 : "ZTEST",
4134 : 0
4135 1 : };
4136 :
4137 : const char* aSpreadsheet[] = {
4138 : "ADDRESS",
4139 : "AREAS",
4140 : "CHOOSE",
4141 : "COLUMN",
4142 : "COLUMNS",
4143 : "DDE",
4144 : "ERRORTYPE",
4145 : "GETPIVOTDATA",
4146 : "HLOOKUP",
4147 : "HYPERLINK",
4148 : "INDEX",
4149 : "INDIRECT",
4150 : "LOOKUP",
4151 : "MATCH",
4152 : "OFFSET",
4153 : "ROW",
4154 : "ROWS",
4155 : "SHEET",
4156 : "SHEETS",
4157 : "STYLE",
4158 : "VLOOKUP",
4159 : 0
4160 1 : };
4161 :
4162 : const char* aText[] = {
4163 : "ARABIC",
4164 : "ASC",
4165 : "BAHTTEXT",
4166 : "BASE",
4167 : "CHAR",
4168 : "CLEAN",
4169 : "CODE",
4170 : "CONCATENATE",
4171 : "DECIMAL",
4172 : "DOLLAR",
4173 : "EXACT",
4174 : "FIND",
4175 : "FIXED",
4176 : "JIS",
4177 : "LEFT",
4178 : "LEN",
4179 : "LOWER",
4180 : "MID",
4181 : "PROPER",
4182 : "REPLACE",
4183 : "REPT",
4184 : "RIGHT",
4185 : "ROMAN",
4186 : "SEARCH",
4187 : "SUBSTITUTE",
4188 : "T",
4189 : "TEXT",
4190 : "TRIM",
4191 : "UNICHAR",
4192 : "UNICODE",
4193 : "UPPER",
4194 : "VALUE",
4195 : 0
4196 1 : };
4197 :
4198 : struct {
4199 : const char* Category; const char** Functions;
4200 : } aTests[] = {
4201 : { "Database", aDataBase },
4202 : { "Date&Time", aDateTime },
4203 : { "Financial", aFinancial },
4204 : { "Information", aInformation },
4205 : { "Logical", aLogical },
4206 : { "Mathematical", aMathematical },
4207 : { "Array", aArray },
4208 : { "Statistical", aStatistical },
4209 : { "Spreadsheet", aSpreadsheet },
4210 : { "Text", aText },
4211 : { "Add-in", 0 },
4212 : { 0, 0 }
4213 1 : };
4214 :
4215 1 : ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
4216 1 : sal_uInt32 n = pFuncMgr->getCount();
4217 12 : for (sal_uInt32 i = 0; i < n; ++i)
4218 : {
4219 11 : const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
4220 11 : CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
4221 11 : sal_uInt32 nFuncCount = pCat->getCount();
4222 302 : for (sal_uInt32 j = 0; j < nFuncCount; ++j)
4223 : {
4224 291 : const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
4225 291 : CPPUNIT_ASSERT_MESSAGE("Unexpected function name", pFunc->getFunctionName().equalsAscii(aTests[i].Functions[j]));
4226 : }
4227 : }
4228 1 : }
4229 :
4230 1 : void Test::testGraphicsInGroup()
4231 : {
4232 1 : OUString aTabName("TestTab");
4233 1 : m_pDoc->InsertTab(0, aTabName);
4234 1 : CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
4235 : SCROW nRow1, nRow2;
4236 1 : bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4237 1 : CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4238 :
4239 1 : m_pDoc->InitDrawLayer();
4240 1 : ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
4241 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
4242 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
4243 1 : CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
4244 :
4245 : {
4246 : //Add a square
4247 1 : Rectangle aOrigRect(2,2,100,100);
4248 1 : SdrRectObj *pObj = new SdrRectObj(aOrigRect);
4249 1 : pPage->InsertObject(pObj);
4250 1 : const Rectangle &rNewRect = pObj->GetLogicRect();
4251 1 : CPPUNIT_ASSERT_MESSAGE("must have equal position and size", aOrigRect == rNewRect);
4252 :
4253 1 : ScDrawLayer::SetPageAnchored(*pObj);
4254 :
4255 : //Use a range of rows guaranteed to include all of the square
4256 1 : m_pDoc->ShowRows(0, 100, 0, false);
4257 1 : m_pDoc->SetDrawPageSize(0);
4258 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
4259 1 : m_pDoc->ShowRows(0, 100, 0, true);
4260 1 : m_pDoc->SetDrawPageSize(0);
4261 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
4262 :
4263 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
4264 1 : CPPUNIT_ASSERT_MESSAGE("That shouldn't change size or positioning", aOrigRect == rNewRect);
4265 :
4266 1 : m_pDoc->ShowRows(0, 100, 0, false);
4267 1 : m_pDoc->SetDrawPageSize(0);
4268 2 : CPPUNIT_ASSERT_MESSAGE("Left and Right should be unchanged",
4269 1 : aOrigRect.Left() == rNewRect.Left() && aOrigRect.Right() == rNewRect.Right());
4270 2 : CPPUNIT_ASSERT_MESSAGE("Height should be minimum allowed height",
4271 1 : (rNewRect.Bottom() - rNewRect.Top()) <= 1);
4272 1 : m_pDoc->ShowRows(0, 100, 0, true);
4273 1 : m_pDoc->SetDrawPageSize(0);
4274 1 : CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
4275 : }
4276 :
4277 : {
4278 : // Add a circle.
4279 1 : Rectangle aOrigRect = Rectangle(10,10,210,210); // 200 x 200
4280 1 : SdrCircObj* pObj = new SdrCircObj(OBJ_CIRC, aOrigRect);
4281 1 : pPage->InsertObject(pObj);
4282 1 : const Rectangle& rNewRect = pObj->GetLogicRect();
4283 2 : CPPUNIT_ASSERT_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
4284 1 : aOrigRect == rNewRect);
4285 :
4286 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
4287 2 : CPPUNIT_ASSERT_MESSAGE("Size changed when cell anchored. Not good.",
4288 1 : aOrigRect == rNewRect);
4289 :
4290 : // Insert 2 rows at the top. This should push the circle object down.
4291 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
4292 1 : m_pDoc->SetDrawPageSize(0);
4293 :
4294 : // Make sure the size of the circle is still identical.
4295 2 : CPPUNIT_ASSERT_MESSAGE("Size of the circle has changed, but shouldn't!",
4296 1 : aOrigRect.GetSize() == rNewRect.GetSize());
4297 :
4298 : // Delete 2 rows at the top. This should bring the circle object to its original position.
4299 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
4300 1 : m_pDoc->SetDrawPageSize(0);
4301 1 : CPPUNIT_ASSERT_MESSAGE("Failed to move back to its original position.", aOrigRect == rNewRect);
4302 : }
4303 :
4304 : {
4305 : // Add a line.
4306 1 : basegfx::B2DPolygon aTempPoly;
4307 1 : Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
4308 1 : Rectangle aOrigRect(10,200,110,300); // 100 x 100
4309 1 : aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
4310 1 : aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
4311 1 : SdrPathObj* pObj = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
4312 1 : pObj->NbcSetLogicRect(aOrigRect);
4313 1 : pPage->InsertObject(pObj);
4314 1 : const Rectangle& rNewRect = pObj->GetLogicRect();
4315 1 : CPPUNIT_ASSERT_MESSAGE("Size differ.", aOrigRect == rNewRect);
4316 :
4317 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
4318 2 : CPPUNIT_ASSERT_MESSAGE("Size changed when cell-anchored. Not good.",
4319 1 : aOrigRect == rNewRect);
4320 :
4321 : // Insert 2 rows at the top and delete them immediately.
4322 1 : m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
4323 1 : m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
4324 1 : m_pDoc->SetDrawPageSize(0);
4325 2 : CPPUNIT_ASSERT_MESSAGE("Size of a line object changed after row insertion and removal.",
4326 1 : aOrigRect == rNewRect);
4327 :
4328 1 : sal_Int32 n = pObj->GetPointCount();
4329 1 : CPPUNIT_ASSERT_MESSAGE("There should be exactly 2 points in a line object.", n == 2);
4330 2 : CPPUNIT_ASSERT_MESSAGE("Line shape has changed.",
4331 2 : aStartPos == pObj->GetPoint(0) && aEndPos == pObj->GetPoint(1));
4332 : }
4333 :
4334 1 : m_pDoc->DeleteTab(0);
4335 1 : }
4336 :
4337 1 : void Test::testGraphicsOnSheetMove()
4338 : {
4339 1 : m_pDoc->InsertTab(0, rtl::OUString("Tab1"));
4340 1 : m_pDoc->InsertTab(1, rtl::OUString("Tab2"));
4341 1 : CPPUNIT_ASSERT_MESSAGE("There should be only 2 sheets to begin with", m_pDoc->GetTableCount() == 2);
4342 :
4343 1 : m_pDoc->InitDrawLayer();
4344 1 : ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
4345 1 : CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
4346 1 : SdrPage* pPage = pDrawLayer->GetPage(0);
4347 1 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
4348 :
4349 : // Insert an object.
4350 1 : Rectangle aObjRect(2,2,100,100);
4351 1 : SdrObject* pObj = new SdrRectObj(aObjRect);
4352 1 : pPage->InsertObject(pObj);
4353 1 : ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
4354 :
4355 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(1));
4356 :
4357 1 : const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
4358 1 : CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
4359 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
4360 :
4361 1 : pPage = pDrawLayer->GetPage(1);
4362 1 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
4363 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(0));
4364 :
4365 : // Insert a new sheet at left-end, and make sure the object has moved to
4366 : // the 2nd page.
4367 1 : m_pDoc->InsertTab(0, rtl::OUString("NewTab"));
4368 1 : CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(3));
4369 1 : pPage = pDrawLayer->GetPage(0);
4370 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
4371 1 : pPage = pDrawLayer->GetPage(1);
4372 1 : CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
4373 1 : pPage = pDrawLayer->GetPage(2);
4374 1 : CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage && pPage->GetObjCount() == 0);
4375 :
4376 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
4377 :
4378 : // Now, delete the sheet that just got inserted. The object should be back
4379 : // on the 1st sheet.
4380 1 : m_pDoc->DeleteTab(0);
4381 1 : pPage = pDrawLayer->GetPage(0);
4382 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage && pPage->GetObjCount() == 1);
4383 2 : CPPUNIT_ASSERT_MESSAGE("Size and position of the object shouldn't change.",
4384 1 : pObj->GetLogicRect() == aObjRect);
4385 :
4386 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
4387 :
4388 : // Move the 1st sheet to the last position.
4389 1 : m_pDoc->MoveTab(0, 1);
4390 1 : pPage = pDrawLayer->GetPage(0);
4391 1 : CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
4392 1 : pPage = pDrawLayer->GetPage(1);
4393 1 : CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
4394 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
4395 :
4396 : // Copy the 2nd sheet, which has one drawing object to the last position.
4397 1 : m_pDoc->CopyTab(1, 2);
4398 1 : pPage = pDrawLayer->GetPage(2);
4399 1 : CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage && pPage->GetObjCount() == 1);
4400 1 : pObj = pPage->GetObj(0);
4401 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
4402 1 : pData = ScDrawLayer::GetObjData(pObj);
4403 1 : CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
4404 1 : CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 2 && pData->maEnd.Tab() == 2);
4405 :
4406 1 : m_pDoc->DeleteTab(2);
4407 1 : m_pDoc->DeleteTab(1);
4408 1 : m_pDoc->DeleteTab(0);
4409 1 : }
4410 :
4411 1 : void Test::testPostIts()
4412 : {
4413 1 : rtl::OUString aHello("Hello world");
4414 1 : rtl::OUString aJimBob("Jim Bob");
4415 1 : rtl::OUString aTabName("PostIts");
4416 1 : rtl::OUString aTabName2("Table2");
4417 1 : m_pDoc->InsertTab(0, aTabName);
4418 :
4419 1 : ScAddress rAddr(2, 2, 0); // cell C3
4420 1 : ScPostIt *pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
4421 1 : pNote->SetText(rAddr, aHello);
4422 1 : pNote->SetAuthor(aJimBob);
4423 :
4424 1 : ScPostIt *pGetNote = m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr);
4425 1 : CPPUNIT_ASSERT_MESSAGE("note should be itself", pGetNote == pNote );
4426 :
4427 : // Insert one row at row 1.
4428 1 : bool bInsertRow = m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 1);
4429 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
4430 :
4431 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
4432 1 : rAddr.IncRow(); // cell C4
4433 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4434 :
4435 : // Insert column at column A.
4436 1 : bool bInsertCol = m_pDoc->InsertCol(0, 0, MAXROW, 0, 1, 1);
4437 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
4438 :
4439 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
4440 1 : rAddr.IncCol(); // cell D4
4441 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4442 :
4443 : // Insert a new sheet to shift the current sheet to the right.
4444 1 : m_pDoc->InsertTab(0, aTabName2);
4445 1 : CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
4446 1 : rAddr.IncTab(); // Move to the next sheet.
4447 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4448 :
4449 1 : m_pDoc->DeleteTab(0);
4450 1 : rAddr.IncTab(-1);
4451 1 : CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4452 :
4453 : // Insert cell at C4. This should NOT shift the note position.
4454 1 : bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
4455 1 : CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
4456 1 : CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4457 :
4458 : // Delete cell at C4. Again, this should NOT shift the note position.
4459 1 : m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
4460 1 : CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4461 :
4462 : // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
4463 1 : m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
4464 1 : rAddr.IncRow(-1); // cell D3
4465 1 : CPPUNIT_ASSERT_MESSAGE("Note at D4 should have shifted up to D3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4466 :
4467 : // Delete column C. This should shift the note one cell left.
4468 1 : m_pDoc->DeleteCol(0, 0, MAXROW, 0, 2, 1);
4469 1 : rAddr.IncCol(-1); // cell C3
4470 1 : CPPUNIT_ASSERT_MESSAGE("Note at D3 should have shifted left to C3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
4471 :
4472 1 : m_pDoc->DeleteTab(0);
4473 1 : }
4474 :
4475 1 : void Test::testToggleRefFlag()
4476 : {
4477 : // In this test, there is no need to insert formula string into a cell in
4478 : // the document, as ScRefFinder does not depend on the content of the
4479 : // document except for the sheet names.
4480 :
4481 1 : OUString aTabName("Test");
4482 1 : m_pDoc->InsertTab(0, aTabName);
4483 :
4484 : {
4485 : // Calc A1: basic 2D reference
4486 :
4487 1 : OUString aFormula("=B100");
4488 1 : ScAddress aPos(1, 5, 0);
4489 1 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
4490 :
4491 : // Original
4492 1 : CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
4493 :
4494 : // column relative / row relative -> column absolute / row absolute
4495 1 : aFinder.ToggleRel(0, aFormula.getLength());
4496 1 : aFormula = aFinder.GetText();
4497 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B$100" );
4498 :
4499 : // column absolute / row absolute -> column relative / row absolute
4500 1 : aFinder.ToggleRel(0, aFormula.getLength());
4501 1 : aFormula = aFinder.GetText();
4502 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B$100" );
4503 :
4504 : // column relative / row absolute -> column absolute / row relative
4505 1 : aFinder.ToggleRel(0, aFormula.getLength());
4506 1 : aFormula = aFinder.GetText();
4507 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B100" );
4508 :
4509 : // column absolute / row relative -> column relative / row relative
4510 1 : aFinder.ToggleRel(0, aFormula.getLength());
4511 1 : aFormula = aFinder.GetText();
4512 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B100" );
4513 : }
4514 :
4515 : {
4516 : // Excel R1C1: basic 2D reference
4517 :
4518 1 : OUString aFormula("=R2C1");
4519 1 : ScAddress aPos(3, 5, 0);
4520 1 : ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
4521 :
4522 : // Original
4523 1 : CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
4524 :
4525 : // column absolute / row absolute -> column relative / row absolute
4526 1 : aFinder.ToggleRel(0, aFormula.getLength());
4527 1 : aFormula = aFinder.GetText();
4528 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=R2C[-3]" );
4529 :
4530 : // column relative / row absolute - > column absolute / row relative
4531 1 : aFinder.ToggleRel(0, aFormula.getLength());
4532 1 : aFormula = aFinder.GetText();
4533 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=R[-4]C1" );
4534 :
4535 : // column absolute / row relative -> column relative / row relative
4536 1 : aFinder.ToggleRel(0, aFormula.getLength());
4537 1 : aFormula = aFinder.GetText();
4538 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=R[-4]C[-3]" );
4539 :
4540 : // column relative / row relative -> column absolute / row absolute
4541 1 : aFinder.ToggleRel(0, aFormula.getLength());
4542 1 : aFormula = aFinder.GetText();
4543 1 : CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=R2C1" );
4544 : }
4545 :
4546 : // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
4547 : // partial selection within formula string.
4548 :
4549 1 : m_pDoc->DeleteTab(0);
4550 1 : }
4551 :
4552 1 : void Test::testAutofilter()
4553 : {
4554 1 : OUString aTabName("Test");
4555 1 : OUString aDBName("NONAME");
4556 :
4557 1 : m_pDoc->InsertTab( 0, aTabName );
4558 :
4559 : // cell contents (0 = empty cell)
4560 : const char* aData[][3] = {
4561 : { "C1", "C2", "C3" },
4562 : { "0", "1", "A" },
4563 : { "1", "2", 0 },
4564 : { "1", "2", "B" },
4565 : { "0", "2", "B" }
4566 1 : };
4567 :
4568 1 : SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
4569 1 : SCROW nRows = SAL_N_ELEMENTS(aData);
4570 :
4571 : // Populate cells.
4572 6 : for (SCROW i = 0; i < nRows; ++i)
4573 20 : for (SCCOL j = 0; j < nCols; ++j)
4574 15 : if (aData[i][j])
4575 14 : m_pDoc->SetString(j, i, 0, rtl::OUString::createFromAscii(aData[i][j]));
4576 :
4577 1 : ScDBData* pDBData = new ScDBData(aDBName, 0, 0, 0, nCols-1, nRows-1);
4578 1 : m_pDoc->SetAnonymousDBData(0,pDBData);
4579 :
4580 1 : pDBData->SetAutoFilter(true);
4581 1 : ScRange aRange;
4582 1 : pDBData->GetArea(aRange);
4583 1 : m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
4584 1 : aRange.aEnd.Col(), aRange.aStart.Row(),
4585 3 : aRange.aStart.Tab(), SC_MF_AUTO);
4586 :
4587 : //create the query param
4588 1 : ScQueryParam aParam;
4589 1 : pDBData->GetQueryParam(aParam);
4590 1 : ScQueryEntry& rEntry = aParam.GetEntry(0);
4591 1 : rEntry.bDoQuery = true;
4592 1 : rEntry.nField = 0;
4593 1 : rEntry.eOp = SC_EQUAL;
4594 1 : rEntry.GetQueryItem().mfVal = 0;
4595 : // add queryParam to database range.
4596 1 : pDBData->SetQueryParam(aParam);
4597 :
4598 : // perform the query.
4599 1 : m_pDoc->Query(0, aParam, true);
4600 :
4601 : //control output
4602 : SCROW nRow1, nRow2;
4603 1 : bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
4604 1 : CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden && nRow1 == 2 && nRow2 == 3);
4605 :
4606 : // Remove filtering.
4607 1 : rEntry.Clear();
4608 1 : m_pDoc->Query(0, aParam, true);
4609 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4610 1 : CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
4611 :
4612 : // Filter for non-empty cells by column C.
4613 1 : rEntry.bDoQuery = true;
4614 1 : rEntry.nField = 2;
4615 1 : rEntry.SetQueryByNonEmpty();
4616 1 : m_pDoc->Query(0, aParam, true);
4617 :
4618 : // only row 3 should be hidden. The rest should be visible.
4619 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4620 1 : CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 1);
4621 1 : bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
4622 1 : CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden && nRow1 == 2 && nRow2 == 2);
4623 1 : bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
4624 1 : CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden && nRow1 == 3 && nRow2 == MAXROW);
4625 :
4626 : // Now, filter for empty cells by column C.
4627 1 : rEntry.SetQueryByEmpty();
4628 1 : m_pDoc->Query(0, aParam, true);
4629 :
4630 : // Now, only row 1 and 3, and 6 and down should be visible.
4631 1 : bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
4632 1 : CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 0);
4633 1 : bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
4634 1 : CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden && nRow1 == 1 && nRow2 == 1);
4635 1 : bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
4636 1 : CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden && nRow1 == 2 && nRow2 == 2);
4637 1 : bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
4638 1 : CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden && nRow1 == 3 && nRow2 == 4);
4639 1 : bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
4640 1 : CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden && nRow1 == 5 && nRow2 == MAXROW);
4641 :
4642 1 : m_pDoc->DeleteTab(0);
4643 1 : }
4644 :
4645 1 : void Test::testCopyPaste()
4646 : {
4647 1 : m_pDoc->InsertTab(0, OUString("Sheet1"));
4648 1 : m_pDoc->InsertTab(1, OUString("Sheet2"));
4649 : //test copy&paste + ScUndoPaste
4650 : //copy local and global range names in formulas
4651 : //string cells and value cells
4652 1 : m_pDoc->SetValue(0, 0, 0, 1);
4653 1 : m_pDoc->SetValue(3, 0, 0, 0);
4654 1 : m_pDoc->SetValue(3, 1, 0, 1);
4655 1 : m_pDoc->SetValue(3, 2, 0, 2);
4656 1 : m_pDoc->SetValue(3, 3, 0, 3);
4657 1 : m_pDoc->SetString(2, 0, 0, OUString("test"));
4658 1 : ScAddress aAdr (0, 0, 0);
4659 :
4660 : //create some range names, local and global
4661 1 : ScRangeData* pLocal1 = new ScRangeData(m_pDoc, rtl::OUString("local1"), aAdr);
4662 1 : ScRangeData* pLocal2 = new ScRangeData(m_pDoc, OUString("local2"), aAdr);
4663 1 : ScRangeData* pGlobal = new ScRangeData(m_pDoc, OUString("global"), aAdr);
4664 1 : ScRangeName* pGlobalRangeName = new ScRangeName();
4665 1 : pGlobalRangeName->insert(pGlobal);
4666 1 : ScRangeName* pLocalRangeName1 = new ScRangeName();
4667 1 : pLocalRangeName1->insert(pLocal1);
4668 1 : pLocalRangeName1->insert(pLocal2);
4669 1 : m_pDoc->SetRangeName(pGlobalRangeName);
4670 1 : m_pDoc->SetRangeName(0, pLocalRangeName1);
4671 :
4672 : //add formula
4673 1 : rtl::OUString aFormulaString("=local1+global+SUM($C$1:$D$4)");
4674 1 : m_pDoc->SetString(1, 0, 0, aFormulaString);
4675 :
4676 1 : double aValue = 0;
4677 1 : m_pDoc->GetValue(1, 0, 0, aValue);
4678 1 : std::cout << "Value: " << aValue << std::endl;
4679 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 8", aValue, 8);
4680 :
4681 : //copy Sheet1.A1:C1 to Sheet2.A2:C2
4682 1 : ScRange aRange(0,0,0,2,0,0);
4683 1 : ScClipParam aClipParam(aRange, false);
4684 1 : ScMarkData aMark;
4685 1 : aMark.SetMarkArea(aRange);
4686 1 : ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
4687 1 : m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
4688 :
4689 1 : sal_uInt16 nFlags = IDF_ALL;
4690 1 : aRange = ScRange(0,1,1,2,1,1);//target: Sheet2.A2:C2
4691 1 : ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
4692 1 : pUndoDoc->InitUndo(m_pDoc, 1, 1, true, true);
4693 1 : ScMarkData aMarkData2;
4694 1 : aMarkData2.SetMarkArea(aRange);
4695 1 : ScRefUndoData* pRefUndoData= new ScRefUndoData(m_pDoc);
4696 : SfxUndoAction* pUndo = new ScUndoPaste(
4697 1 : &m_xDocShRef, ScRange(0, 1, 1, 2, 1, 1), aMarkData2, pUndoDoc, NULL, IDF_ALL, pRefUndoData, false);
4698 1 : m_pDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
4699 :
4700 : //check values after copying
4701 1 : rtl::OUString aString;
4702 1 : m_pDoc->GetValue(1,1,1, aValue);
4703 1 : m_pDoc->GetFormula(1,1,1, aString);
4704 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("copied formula should return 2", aValue, 2);
4705 1 : CPPUNIT_ASSERT_MESSAGE("formula string was not copied correctly", aString == aFormulaString);
4706 1 : m_pDoc->GetValue(0,1,1, aValue);
4707 1 : CPPUNIT_ASSERT_MESSAGE("copied value should be 1", aValue == 1);
4708 :
4709 : //chack local range name after copying
4710 1 : pLocal1 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL1"));
4711 1 : CPPUNIT_ASSERT_MESSAGE("local range name 1 should be copied", pLocal1);
4712 1 : ScRange aRangeLocal1;
4713 1 : pLocal1->IsValidReference(aRangeLocal1);
4714 1 : CPPUNIT_ASSERT_MESSAGE("local range 1 should still point to Sheet1.A1",aRangeLocal1 == ScRange(0,0,0,0,0,0));
4715 1 : pLocal2 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
4716 1 : CPPUNIT_ASSERT_MESSAGE("local2 should not be copied", pLocal2 == NULL);
4717 :
4718 :
4719 : //check undo and redo
4720 1 : pUndo->Undo();
4721 1 : m_pDoc->GetValue(1,1,1, aValue);
4722 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", aValue, 0);
4723 1 : m_pDoc->GetString(2,1,1, aString);
4724 1 : CPPUNIT_ASSERT_MESSAGE("after undo string should be removed", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("")));
4725 :
4726 1 : pUndo->Redo();
4727 1 : m_pDoc->GetValue(1,1,1, aValue);
4728 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 2 after redo", aValue, 2);
4729 1 : m_pDoc->GetString(2,1,1, aString);
4730 1 : CPPUNIT_ASSERT_MESSAGE("Cell Sheet2.C2 should contain: test", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("test")));
4731 1 : m_pDoc->GetFormula(1,1,1, aString);
4732 1 : CPPUNIT_ASSERT_MESSAGE("Formula should be correct again", aString == aFormulaString);
4733 :
4734 : //clear all variables
4735 1 : delete pClipDoc;
4736 1 : delete pUndoDoc;
4737 1 : m_pDoc->DeleteTab(1);
4738 1 : m_pDoc->DeleteTab(0);
4739 1 : }
4740 :
4741 1 : void Test::testMergedCells()
4742 : {
4743 : //test merge and unmerge
4744 : //TODO: an undo/redo test for this would be a good idea
4745 1 : m_pDoc->InsertTab(0, rtl::OUString("Sheet1"));
4746 1 : m_pDoc->DoMerge(0, 1, 1, 3, 3, false);
4747 1 : SCCOL nEndCol = 1;
4748 1 : SCROW nEndRow = 1;
4749 1 : m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0, false);
4750 1 : CPPUNIT_ASSERT_MESSAGE("did not merge cells", nEndCol == 3 && nEndRow == 3);
4751 1 : ScRange aRange(0,2,0,MAXCOL,2,0);
4752 1 : ScMarkData aMark;
4753 1 : aMark.SetMarkArea(aRange);
4754 1 : m_pDoc->SetInTest();
4755 1 : m_xDocShRef->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS, true, true);
4756 1 : m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0, false);
4757 1 : cout << nEndRow << nEndCol;
4758 1 : CPPUNIT_ASSERT_MESSAGE("did not increase merge area", nEndCol == 3 && nEndRow == 4);
4759 1 : m_pDoc->DeleteTab(0);
4760 1 : }
4761 :
4762 :
4763 1 : void Test::testRenameTable()
4764 : {
4765 : //test set rename table
4766 : //TODO: set name1 and name2 and do an undo to check if name 1 is set now
4767 : //TODO: also check if new name for table is same as another table
4768 :
4769 1 : m_pDoc->InsertTab(0, "Sheet1");
4770 1 : m_pDoc->InsertTab(1, "Sheet2");
4771 :
4772 : //test case 1 , rename table2 to sheet 1, it should return error
4773 1 : rtl::OUString nameToSet = "Sheet1";
4774 1 : ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
4775 1 : CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
4776 :
4777 : //test case 2 , simple rename to check name
4778 1 : nameToSet = "test1";
4779 1 : m_xDocShRef->GetDocFunc().RenameTable(0,nameToSet,false,true);
4780 1 : rtl::OUString nameJustSet;
4781 1 : m_pDoc->GetName(0,nameJustSet);
4782 1 : CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet != nameJustSet);
4783 :
4784 : //test case 3 , rename again
4785 1 : rtl::OUString anOldName;
4786 1 : m_pDoc->GetName(0,anOldName);
4787 :
4788 1 : nameToSet = "test2";
4789 1 : rDocFunc.RenameTable(0,nameToSet,false,true);
4790 1 : m_pDoc->GetName(0,nameJustSet);
4791 1 : CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet != nameJustSet);
4792 :
4793 : //test case 4 , check if undo works
4794 1 : SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShRef,0,anOldName,nameToSet);
4795 1 : pUndo->Undo();
4796 1 : m_pDoc->GetName(0,nameJustSet);
4797 1 : CPPUNIT_ASSERT_MESSAGE("the correct name is not set after undo", nameJustSet == anOldName);
4798 :
4799 1 : pUndo->Redo();
4800 1 : m_pDoc->GetName(0,nameJustSet);
4801 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after redo", nameJustSet == nameToSet);
4802 :
4803 1 : m_pDoc->DeleteTab(0);
4804 1 : m_pDoc->DeleteTab(1);
4805 1 : }
4806 :
4807 :
4808 :
4809 1 : void Test::testSetBackgroundColor()
4810 : {
4811 : //test set background color
4812 : //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
4813 :
4814 1 : m_pDoc->InsertTab(0, rtl::OUString("Sheet1"));
4815 1 : Color aColor;
4816 :
4817 : //test yellow
4818 1 : aColor=Color(COL_YELLOW);
4819 1 : m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4820 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set", m_pDoc->GetTabBgColor(0)!= aColor);
4821 :
4822 :
4823 1 : Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
4824 1 : aColor.SetColor(COL_BLUE);//set BLUE
4825 1 : m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
4826 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set the second time", m_pDoc->GetTabBgColor(0)!= aColor);
4827 :
4828 : //now check for undo
4829 1 : SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShRef,0, aOldTabBgColor, aColor);
4830 1 : pUndo->Undo();
4831 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aOldTabBgColor);
4832 1 : pUndo->Redo();
4833 1 : CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aColor);
4834 1 : m_pDoc->DeleteTab(0);
4835 1 : }
4836 :
4837 :
4838 :
4839 1 : void Test::testUpdateReference()
4840 : {
4841 : //test that formulas are correctly updated during sheet delete
4842 : //TODO: add tests for relative references, updating of named ranges, ...
4843 1 : rtl::OUString aSheet1("Sheet1");
4844 1 : rtl::OUString aSheet2("Sheet2");
4845 1 : rtl::OUString aSheet3("Sheet3");
4846 1 : rtl::OUString aSheet4("Sheet4");
4847 1 : m_pDoc->InsertTab(0, aSheet1);
4848 1 : m_pDoc->InsertTab(1, aSheet2);
4849 1 : m_pDoc->InsertTab(2, aSheet3);
4850 1 : m_pDoc->InsertTab(3, aSheet4);
4851 :
4852 1 : m_pDoc->SetValue(0,0,2, 1);
4853 1 : m_pDoc->SetValue(1,0,2, 2);
4854 1 : m_pDoc->SetValue(1,1,3, 4);
4855 1 : m_pDoc->SetString(2,0,2, rtl::OUString("=A1+B1"));
4856 1 : m_pDoc->SetString(2,1,2, rtl::OUString("=Sheet4.B2+A1"));
4857 :
4858 : double aValue;
4859 1 : m_pDoc->GetValue(2,0,2, aValue);
4860 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 3);
4861 1 : m_pDoc->GetValue(2,1,2, aValue);
4862 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 5);
4863 :
4864 : //test deleting both sheets: one is not directly before the sheet, the other one is
4865 1 : m_pDoc->DeleteTab(0);
4866 1 : m_pDoc->GetValue(2,0,1, aValue);
4867 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 3);
4868 1 : m_pDoc->GetValue(2,1,1, aValue);
4869 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 5);
4870 :
4871 1 : m_pDoc->DeleteTab(0);
4872 1 : m_pDoc->GetValue(2,0,0, aValue);
4873 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 3);
4874 1 : m_pDoc->GetValue(2,1,0, aValue);
4875 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 5);
4876 :
4877 : //test adding two sheets
4878 1 : m_pDoc->InsertTab(0, aSheet2);
4879 1 : m_pDoc->GetValue(2,0,1, aValue);
4880 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 3);
4881 1 : m_pDoc->GetValue(2,1,1, aValue);
4882 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 5);
4883 :
4884 1 : m_pDoc->InsertTab(0, aSheet1);
4885 1 : m_pDoc->GetValue(2,0,2, aValue);
4886 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 3);
4887 1 : m_pDoc->GetValue(2,1,2, aValue);
4888 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 5);
4889 :
4890 : //test new DeleteTabs/InsertTabs methods
4891 1 : m_pDoc->DeleteTabs(0, 2);
4892 1 : m_pDoc->GetValue(2, 0, 0, aValue);
4893 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 3);
4894 1 : m_pDoc->GetValue(2, 1, 0, aValue);
4895 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 5);
4896 :
4897 1 : std::vector<rtl::OUString> aSheets;
4898 1 : aSheets.push_back(aSheet1);
4899 1 : aSheets.push_back(aSheet2);
4900 1 : m_pDoc->InsertTabs(0, aSheets, false, true);
4901 1 : m_pDoc->GetValue(2, 0, 2, aValue);
4902 1 : rtl::OUString aFormula;
4903 1 : m_pDoc->GetFormula(2,0,2, aFormula);
4904 1 : std::cout << "formel: " << rtl::OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
4905 1 : std::cout << std::endl << aValue << std::endl;
4906 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 3);
4907 1 : m_pDoc->GetValue(2, 1, 2, aValue);
4908 1 : ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 5);
4909 :
4910 1 : m_pDoc->DeleteTab(3);
4911 1 : m_pDoc->DeleteTab(2);
4912 1 : m_pDoc->DeleteTab(1);
4913 1 : m_pDoc->DeleteTab(0);
4914 1 : }
4915 :
4916 : namespace {
4917 :
4918 4 : bool hasRange(const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange)
4919 : {
4920 4 : std::vector<ScTokenRef>::const_iterator it = rRefTokens.begin(), itEnd = rRefTokens.end();
4921 5 : for (; it != itEnd; ++it)
4922 : {
4923 5 : const ScTokenRef& p = *it;
4924 5 : if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
4925 0 : continue;
4926 :
4927 5 : switch (p->GetType())
4928 : {
4929 : case formula::svSingleRef:
4930 : {
4931 2 : ScSingleRefData aData = p->GetSingleRef();
4932 2 : if (rRange.aStart != rRange.aEnd)
4933 : break;
4934 :
4935 2 : ScAddress aThis(aData.nCol, aData.nRow, aData.nTab);
4936 2 : if (aThis == rRange.aStart)
4937 2 : return true;
4938 : }
4939 0 : break;
4940 : case formula::svDoubleRef:
4941 : {
4942 3 : ScComplexRefData aData = p->GetDoubleRef();
4943 3 : ScRange aThis(aData.Ref1.nCol, aData.Ref1.nRow, aData.Ref1.nTab, aData.Ref2.nCol, aData.Ref2.nRow, aData.Ref2.nTab);
4944 3 : if (aThis == rRange)
4945 2 : return true;
4946 : }
4947 1 : break;
4948 : default:
4949 : ;
4950 : }
4951 : }
4952 0 : return false;
4953 : }
4954 :
4955 : }
4956 :
4957 1 : void Test::testJumpToPrecedentsDependents()
4958 : {
4959 : // Precedent is another cell that the cell references, while dependent is
4960 : // another cell that references it.
4961 1 : m_pDoc->InsertTab(0, rtl::OUString("Test"));
4962 :
4963 1 : m_pDoc->SetString(2, 0, 0, rtl::OUString("=A1+A2+B3")); // C1
4964 1 : m_pDoc->SetString(2, 1, 0, rtl::OUString("=A1")); // C2
4965 1 : m_pDoc->CalcAll();
4966 :
4967 1 : std::vector<ScTokenRef> aRefTokens;
4968 1 : ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
4969 :
4970 : {
4971 : // C1's precedent should be A1:A2,B3.
4972 1 : ScRangeList aRange(ScRange(2, 0, 0));
4973 1 : rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4974 2 : CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
4975 1 : hasRange(aRefTokens, ScRange(0, 0, 0, 0, 1, 0)));
4976 2 : CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
4977 2 : hasRange(aRefTokens, ScRange(1, 2, 0)));
4978 : }
4979 :
4980 : {
4981 : // C2's precedent should be A1 only.
4982 1 : ScRangeList aRange(ScRange(2, 1, 0));
4983 1 : rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
4984 2 : CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
4985 1 : aRefTokens.size(), static_cast<size_t>(1));
4986 2 : CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
4987 2 : hasRange(aRefTokens, ScRange(0, 0, 0)));
4988 : }
4989 :
4990 : {
4991 : // A1's dependent should be C1:C2.
4992 1 : ScRangeList aRange(ScRange(0, 0, 0));
4993 1 : rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
4994 2 : CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
4995 2 : aRefTokens.size() == 1 && hasRange(aRefTokens, ScRange(2, 0, 0, 2, 1, 0)));
4996 : }
4997 :
4998 1 : m_pDoc->DeleteTab(0);
4999 1 : }
5000 :
5001 1 : void Test::testAutoFill()
5002 : {
5003 1 : m_pDoc->InsertTab(0, "test");
5004 :
5005 1 : m_pDoc->SetValue(0,0,0,1);
5006 :
5007 1 : ScMarkData aMarkData;
5008 1 : aMarkData.SelectTable(0, true);
5009 :
5010 1 : m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 5);
5011 7 : for (SCROW i = 0; i< 6; ++i)
5012 6 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
5013 :
5014 : // check that hidden rows are not affected by autofill
5015 : // set values for hidden rows
5016 1 : m_pDoc->SetValue(0,1,0,10);
5017 1 : m_pDoc->SetValue(0,2,0,10);
5018 :
5019 1 : m_pDoc->SetRowHidden(1, 2, 0, true);
5020 1 : m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 8);
5021 :
5022 1 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
5023 1 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
5024 6 : for (SCROW i = 3; i< 8; ++i)
5025 5 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
5026 :
5027 1 : m_pDoc->Fill( 0, 0, 0, 8, NULL, aMarkData, 5, FILL_TO_RIGHT );
5028 6 : for (SCCOL i = 0; i < 5; ++i)
5029 : {
5030 45 : for(SCROW j = 0; j < 8; ++j)
5031 : {
5032 40 : if (j > 2)
5033 : {
5034 25 : ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
5035 : }
5036 15 : else if (j == 0)
5037 : {
5038 5 : ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
5039 : }
5040 10 : else if (j == 1 || j== 2)
5041 : {
5042 10 : if(i == 0)
5043 2 : ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
5044 : else
5045 8 : ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
5046 : }
5047 : }
5048 : }
5049 :
5050 : // test auto fill user data lists
5051 1 : m_pDoc->SetString( 0, 100, 0, "January" );
5052 1 : m_pDoc->Fill( 0, 100, 0, 100, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
5053 1 : rtl::OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
5054 1 : CPPUNIT_ASSERT_EQUAL( aTestValue, rtl::OUString("February") );
5055 1 : aTestValue = m_pDoc->GetString( 0, 102, 0 );
5056 1 : CPPUNIT_ASSERT_EQUAL( aTestValue, rtl::OUString("March") );
5057 :
5058 : // test that two same user data list entries will not result in incremental fill
5059 1 : m_pDoc->SetString( 0, 101, 0, "January" );
5060 1 : m_pDoc->Fill( 0, 100, 0, 101, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
5061 3 : for ( SCROW i = 102; i <= 103; ++i )
5062 : {
5063 2 : aTestValue = m_pDoc->GetString( 0, i, 0 );
5064 2 : CPPUNIT_ASSERT_EQUAL( aTestValue, rtl::OUString("January") );
5065 : }
5066 1 : m_pDoc->DeleteTab(0);
5067 1 : }
5068 :
5069 1 : void Test::testCopyPasteFormulas()
5070 : {
5071 1 : m_pDoc->InsertTab(0, "Sheet1");
5072 1 : m_pDoc->InsertTab(1, "Sheet2");
5073 :
5074 1 : m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
5075 1 : m_pDoc->SetString(0,1,0, "=$A$1+B2" );
5076 1 : m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
5077 1 : m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
5078 1 : m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
5079 :
5080 : // to prevent ScEditableTester in ScDocFunc::MoveBlock
5081 1 : m_pDoc->SetInTest();
5082 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,0,0), 1.0);
5083 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,1,0), 1.0);
5084 1 : ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
5085 1 : bool bMoveDone = rDocFunc.MoveBlock(ScRange(0,0,0,0,4,0), ScAddress( 10, 10, 0), false, false, false, true);
5086 :
5087 : // check that moving was succesful, mainly for editable tester
5088 1 : CPPUNIT_ASSERT(bMoveDone);
5089 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,10,0), 1.0);
5090 1 : ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,11,0), 1.0);
5091 1 : rtl::OUString aFormula;
5092 1 : m_pDoc->GetFormula(10,10,0, aFormula);
5093 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=COLUMN($A$1)"));
5094 1 : m_pDoc->GetFormula(10,11,0, aFormula);
5095 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=$A$1+L12"));
5096 1 : m_pDoc->GetFormula(10,12,0, aFormula);
5097 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=$Sheet2.K11"));
5098 1 : m_pDoc->GetFormula(10,13,0, aFormula);
5099 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=$Sheet2.$A$1"));
5100 1 : m_pDoc->GetFormula(10,14,0, aFormula);
5101 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=$Sheet2.K$1"));
5102 1 : }
5103 :
5104 1 : void Test::testCopyPasteFormulasExternalDoc()
5105 : {
5106 1 : rtl::OUString aDocName("file:///source.fake");
5107 1 : SfxMedium* pMedium = new SfxMedium(aDocName, STREAM_STD_READWRITE);
5108 1 : m_xDocShRef->DoInitNew(pMedium);
5109 1 : m_pDoc = m_xDocShRef->GetDocument();
5110 :
5111 1 : ScDocShellRef xExtDocSh = new ScDocShell;
5112 1 : OUString aExtDocName("file:///extdata.fake");
5113 1 : OUString aExtSh1Name("ExtSheet1");
5114 1 : OUString aExtSh2Name("ExtSheet2");
5115 1 : SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
5116 1 : xExtDocSh->DoInitNew(pMed);
5117 2 : CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
5118 1 : findLoadedDocShellByName(aExtDocName) != NULL);
5119 :
5120 1 : ScDocument* pExtDoc = xExtDocSh->GetDocument();
5121 1 : pExtDoc->InsertTab(0, aExtSh1Name);
5122 1 : pExtDoc->InsertTab(1, aExtSh2Name);
5123 :
5124 1 : m_pDoc->InsertTab(0, "Sheet1");
5125 1 : m_pDoc->InsertTab(1, "Sheet2");
5126 :
5127 1 : m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
5128 1 : m_pDoc->SetString(0,1,0, "=$A$1+B2" );
5129 1 : m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
5130 1 : m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
5131 1 : m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
5132 1 : m_pDoc->SetString(0,5,0, "=$Sheet1.$A$1");
5133 :
5134 1 : ScRange aRange(0,0,0,0,5,0);
5135 1 : ScClipParam aClipParam(aRange, false);
5136 1 : ScMarkData aMark;
5137 1 : aMark.SetMarkArea(aRange);
5138 1 : ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
5139 1 : m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
5140 :
5141 1 : sal_uInt16 nFlags = IDF_ALL;
5142 1 : aRange = ScRange(1,1,1,1,6,1);
5143 1 : ScMarkData aMarkData2;
5144 1 : aMarkData2.SetMarkArea(aRange);
5145 1 : pExtDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
5146 :
5147 1 : rtl::OUString aFormula;
5148 1 : pExtDoc->GetFormula(1,1,1, aFormula);
5149 : //adjust absolute refs pointing to the copy area
5150 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=COLUMN($B$2)"));
5151 1 : pExtDoc->GetFormula(1,2,1, aFormula);
5152 : //adjust absolute refs and keep relative refs
5153 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=$B$2+C3"));
5154 1 : pExtDoc->GetFormula(1,3,1, aFormula);
5155 : // make absolute sheet refs external refs
5156 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("='file:///source.fake'#$Sheet2.B2"));
5157 1 : pExtDoc->GetFormula(1,4,1, aFormula);
5158 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("='file:///source.fake'#$Sheet2.$A$1"));
5159 1 : pExtDoc->GetFormula(1,5,1, aFormula);
5160 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("='file:///source.fake'#$Sheet2.B$1"));
5161 1 : pExtDoc->GetFormula(1,6,1, aFormula);
5162 1 : CPPUNIT_ASSERT_EQUAL(aFormula, rtl::OUString("=$ExtSheet2.$B$2"));
5163 1 : }
5164 :
5165 1 : void Test::testFindAreaPosRowDown()
5166 : {
5167 : const char* aData[][2] = {
5168 : { "", "1" },
5169 : { "1", "" },
5170 : { "1", "1" },
5171 : { "", "1" },
5172 : { "1", "1" },
5173 : { "1", "" },
5174 1 : { "1", "1" }, };
5175 :
5176 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
5177 1 : rtl::OUString aTabName1("test1");
5178 1 : pDoc->InsertTab(0, aTabName1);
5179 1 : clearRange( pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
5180 1 : ScAddress aPos(0,0,0);
5181 1 : ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
5182 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
5183 :
5184 1 : pDoc->SetRowHidden(4,4,0,true);
5185 1 : bool bHidden = pDoc->RowHidden(4,0);
5186 1 : CPPUNIT_ASSERT(bHidden);
5187 :
5188 1 : SCCOL nCol = 0;
5189 1 : SCROW nRow = 0;
5190 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5191 :
5192 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
5193 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
5194 :
5195 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5196 :
5197 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
5198 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
5199 :
5200 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5201 :
5202 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
5203 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
5204 :
5205 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5206 :
5207 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
5208 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
5209 :
5210 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5211 :
5212 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(MAXROW), nRow);
5213 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
5214 :
5215 1 : nCol = 1;
5216 1 : nRow = 2;
5217 :
5218 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5219 :
5220 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
5221 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
5222 :
5223 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
5224 :
5225 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
5226 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
5227 :
5228 1 : pDoc->DeleteTab(0);
5229 1 : }
5230 :
5231 1 : void Test::testFindAreaPosColRight()
5232 : {
5233 : const char* aData[][7] = {
5234 : { "", "1", "1", "", "1", "1", "1" },
5235 1 : { "", "", "1", "1", "1", "", "1" }, };
5236 :
5237 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
5238 1 : rtl::OUString aTabName1("test1");
5239 1 : pDoc->InsertTab(0, aTabName1);
5240 1 : clearRange( pDoc, ScRange(0, 0, 0, 7, SAL_N_ELEMENTS(aData), 0));
5241 1 : ScAddress aPos(0,0,0);
5242 1 : ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
5243 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
5244 :
5245 1 : pDoc->SetColHidden(4,4,0,true);
5246 1 : bool bHidden = pDoc->ColHidden(4,0);
5247 1 : CPPUNIT_ASSERT(bHidden);
5248 :
5249 1 : SCCOL nCol = 0;
5250 1 : SCROW nRow = 0;
5251 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5252 :
5253 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
5254 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
5255 :
5256 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5257 :
5258 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
5259 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
5260 :
5261 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5262 :
5263 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
5264 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
5265 :
5266 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5267 :
5268 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
5269 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
5270 :
5271 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5272 :
5273 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
5274 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(MAXCOL), nCol);
5275 :
5276 1 : nCol = 2;
5277 1 : nRow = 1;
5278 :
5279 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5280 :
5281 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
5282 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
5283 :
5284 1 : pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
5285 :
5286 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
5287 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
5288 :
5289 1 : pDoc->DeleteTab(0);
5290 1 : }
5291 :
5292 : // regression test fo fdo#53814, sorting doens't work as expected
5293 : // if cells in the sort are referenced by formulas
5294 1 : void Test::testSortWithFormulaRefs()
5295 : {
5296 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
5297 1 : rtl::OUString aTabName1("List1");
5298 1 : rtl::OUString aTabName2("List2");
5299 1 : pDoc->InsertTab(0, aTabName1);
5300 1 : pDoc->InsertTab(1, aTabName2);
5301 :
5302 : const char* aFormulaData[6] = {
5303 : "=IF($List1.A2<>\"\",$List1.A2,\"\")",
5304 : "=IF($List1.A3<>\"\",$List1.A3,\"\")",
5305 : "=IF($List1.A4<>\"\",$List1.A4,\"\")",
5306 : "=IF($List1.A5<>\"\",$List1.A5,\"\")",
5307 : "=IF($List1.A6<>\"\",$List1.A6,\"\")",
5308 : "=IF($List1.A7<>\"\",$List1.A7,\"\")",
5309 1 : };
5310 :
5311 : const char* aTextData[4] = {
5312 : "bob",
5313 : "tim",
5314 : "brian",
5315 : "larry",
5316 1 : };
5317 :
5318 : const char* aResults[ 6 ] = {
5319 : "bob",
5320 : "brian",
5321 : "larry",
5322 : "tim",
5323 : "",
5324 : "",
5325 1 : };
5326 : // insert data to sort
5327 1 : SCROW nStart = 1, nEnd = 4;
5328 5 : for ( SCROW i = nStart; i <= nEnd; ++i )
5329 4 : pDoc->SetString( 0, i, 0, rtl::OUString::createFromAscii(aTextData[i-1]) );
5330 : // insert forumulas
5331 1 : nStart = 0;
5332 1 : nEnd = SAL_N_ELEMENTS(aFormulaData);
5333 7 : for ( SCROW i = nStart; i < nEnd; ++i )
5334 6 : pDoc->SetString( 0, i, 1, rtl::OUString::createFromAscii(aFormulaData[i]) );
5335 :
5336 1 : ScSortParam aSortData;
5337 1 : aSortData.nCol1 = 0;
5338 1 : aSortData.nCol2 = 0;
5339 1 : aSortData.nRow1 = 1;
5340 1 : aSortData.nRow2 = 7;
5341 1 : aSortData.maKeyState[0].bDoSort = true;
5342 1 : aSortData.maKeyState[0].nField = 0;
5343 :
5344 1 : pDoc->Sort(0, aSortData, false, NULL);
5345 :
5346 1 : nEnd = SAL_N_ELEMENTS( aResults );
5347 7 : for ( SCROW i = nStart; i < nEnd; ++i )
5348 : {
5349 6 : rtl::OUString sResult;
5350 6 : pDoc->GetString( 0, i + 1, 0, sResult );
5351 6 : CPPUNIT_ASSERT_EQUAL( rtl::OUString::createFromAscii( aResults[ i ] ), sResult );
5352 6 : }
5353 1 : pDoc->DeleteTab(0);
5354 1 : pDoc->DeleteTab(1);
5355 1 : }
5356 :
5357 1 : void Test::testSort()
5358 : {
5359 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
5360 1 : rtl::OUString aTabName1("test1");
5361 1 : pDoc->InsertTab(0, aTabName1);
5362 :
5363 : const char* aData[][2] = {
5364 : { "2", "4" },
5365 : { "4", "1" },
5366 : { "1", "2" }
5367 1 : };
5368 :
5369 1 : clearRange( pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
5370 1 : ScAddress aPos(0,0,0);
5371 1 : ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
5372 1 : CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
5373 :
5374 1 : rtl::OUString aHello("Hello");
5375 1 : rtl::OUString aJimBob("Jim Bob");
5376 1 : ScAddress rAddr(1, 1, 0);
5377 1 : ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
5378 1 : pNote->SetText(rAddr, aHello);
5379 1 : pNote->SetAuthor(aJimBob);
5380 :
5381 1 : ScSortParam aSortData;
5382 1 : aSortData.nCol1 = 1;
5383 1 : aSortData.nCol2 = 1;
5384 1 : aSortData.nRow1 = 0;
5385 1 : aSortData.nRow2 = 2;
5386 1 : aSortData.maKeyState[0].bDoSort = true;
5387 1 : aSortData.maKeyState[0].nField = 1;
5388 :
5389 1 : pDoc->Sort(0, aSortData, false, NULL);
5390 1 : double nVal = pDoc->GetValue(1,0,0);
5391 1 : ASSERT_DOUBLES_EQUAL(nVal, 1.0);
5392 :
5393 : // check that note is also moved
5394 1 : pNote = m_pDoc->GetNotes(0)->findByAddress( 1, 0 );
5395 1 : CPPUNIT_ASSERT(pNote);
5396 :
5397 1 : pDoc->DeleteTab(0);
5398 1 : }
5399 :
5400 1 : void Test::testShiftCells()
5401 : {
5402 1 : m_pDoc->InsertTab(0, "foo");
5403 :
5404 1 : OUString aTestVal("Some Text");
5405 :
5406 : // Text into cell E5.
5407 1 : m_pDoc->SetString(4, 3, 0, aTestVal);
5408 :
5409 : // Insert cell at D5. This should shift the string cell to right.
5410 1 : m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
5411 1 : OUString aStr = m_pDoc->GetString(5, 3, 0);
5412 1 : CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
5413 1 : CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 3, 4, 3, 4));
5414 :
5415 : // Delete cell D5, to shift the text cell back into D5.
5416 1 : m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
5417 1 : aStr = m_pDoc->GetString(4, 3, 0);
5418 1 : CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
5419 1 : CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 4, 4, 4, 4));
5420 :
5421 1 : m_pDoc->DeleteTab(0);
5422 1 : }
5423 :
5424 1 : void Test::testDeleteRow()
5425 : {
5426 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
5427 1 : rtl::OUString aSheet1("Sheet1");
5428 1 : pDoc->InsertTab(0, aSheet1);
5429 :
5430 1 : rtl::OUString aHello("Hello");
5431 1 : rtl::OUString aJimBob("Jim Bob");
5432 1 : ScAddress rAddr(1, 1, 0);
5433 1 : ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
5434 1 : pNote->SetText(rAddr, aHello);
5435 1 : pNote->SetAuthor(aJimBob);
5436 :
5437 1 : pDoc->DeleteRow(0, 0, MAXCOL, 0, 1, 1);
5438 :
5439 1 : CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
5440 1 : pDoc->DeleteTab(0);
5441 1 : }
5442 :
5443 1 : void Test::testDeleteCol()
5444 : {
5445 1 : ScDocument* pDoc = m_xDocShRef->GetDocument();
5446 1 : rtl::OUString aSheet1("Sheet1");
5447 1 : pDoc->InsertTab(0, aSheet1);
5448 :
5449 1 : rtl::OUString aHello("Hello");
5450 1 : rtl::OUString aJimBob("Jim Bob");
5451 1 : ScAddress rAddr(1, 1, 0);
5452 1 : ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
5453 1 : pNote->SetText(rAddr, aHello);
5454 1 : pNote->SetAuthor(aJimBob);
5455 :
5456 1 : pDoc->DeleteCol(0, 0, MAXROW, 0, 1, 1);
5457 :
5458 1 : CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
5459 1 : pDoc->DeleteTab(0);
5460 1 : }
5461 :
5462 1 : CPPUNIT_TEST_SUITE_REGISTRATION(Test);
5463 :
5464 : }
5465 :
5466 4 : CPPUNIT_PLUGIN_IMPLEMENT();
5467 :
5468 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|