1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "helper/sorthelper.hxx"
11 #include "helper/debughelper.hxx"
12 #include "helper/qahelper.hxx"
15 #include <sortparam.hxx>
17 #include <formulacell.hxx>
18 #include <scopetools.hxx>
19 #include <globalnames.hxx>
20 #include <dbdocfun.hxx>
21 #include <docfunc.hxx>
22 #include <scitems.hxx>
23 #include <editutil.hxx>
24 #include <drwlayer.hxx>
25 #include <queryiter.hxx>
26 #include <undomanager.hxx>
28 #include <editeng/wghtitem.hxx>
29 #include <editeng/postitem.hxx>
30 #include <svx/svdocirc.hxx>
31 #include <svx/svdpage.hxx>
32 #include <rtl/math.hxx>
34 class TestSort
: public ScUcalcTestBase
38 void testSortHorizontal();
39 void testSortHorizontalWholeColumn();
40 void testSortSingleRow();
41 void testSortWithFormulaRefs();
42 void testSortWithStrings();
43 void testSortInFormulaGroup();
44 void testSortWithCellFormats();
45 void testSortRefUpdate();
46 void testSortRefUpdate2();
47 void testSortRefUpdate3();
48 void testSortRefUpdate4();
49 void testSortRefUpdate4_Impl();
50 void testSortRefUpdate5();
51 void testSortRefUpdate6();
52 void testSortBroadcaster();
53 void testSortBroadcastBroadcaster();
54 void testSortOutOfPlaceResult();
55 void testSortPartialFormulaGroup();
56 void testSortImages();
57 void testQueryBinarySearch();
59 CPPUNIT_TEST_SUITE(TestSort
);
61 CPPUNIT_TEST(testSort
);
62 CPPUNIT_TEST(testSortHorizontal
);
63 CPPUNIT_TEST(testSortHorizontalWholeColumn
);
64 CPPUNIT_TEST(testSortSingleRow
);
65 CPPUNIT_TEST(testSortWithFormulaRefs
);
66 CPPUNIT_TEST(testSortWithStrings
);
67 CPPUNIT_TEST(testSortInFormulaGroup
);
68 CPPUNIT_TEST(testSortWithCellFormats
);
69 CPPUNIT_TEST(testSortRefUpdate
);
70 CPPUNIT_TEST(testSortRefUpdate2
);
71 CPPUNIT_TEST(testSortRefUpdate3
);
72 CPPUNIT_TEST(testSortRefUpdate4
);
73 CPPUNIT_TEST(testSortRefUpdate5
);
74 CPPUNIT_TEST(testSortRefUpdate6
);
75 CPPUNIT_TEST(testSortBroadcaster
);
76 CPPUNIT_TEST(testSortBroadcastBroadcaster
);
77 CPPUNIT_TEST(testSortOutOfPlaceResult
);
78 CPPUNIT_TEST(testSortPartialFormulaGroup
);
79 CPPUNIT_TEST(testSortImages
);
80 CPPUNIT_TEST(testQueryBinarySearch
);
82 CPPUNIT_TEST_SUITE_END();
85 void TestSort::testSort()
87 m_pDoc
->InsertTab(0, "test1");
89 // We need a drawing layer in order to create caption objects.
90 m_pDoc
->InitDrawLayer(m_xDocShell
.get());
92 ScAddress
aPos(0,0,0);
94 const std::vector
<std::vector
<const char*>> aData
= {
101 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, aData
.size(), 0));
102 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
103 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
106 // Insert note in cell B2.
107 ScAddress
rAddr(1, 1, 0);
108 ScPostIt
* pNote
= m_pDoc
->GetOrCreateNote(rAddr
);
109 pNote
->SetText(rAddr
, "Hello");
110 pNote
->SetAuthor("Jim Bob");
112 ScSortParam aSortData
;
117 aSortData
.maKeyState
[0].bDoSort
= true;
118 aSortData
.maKeyState
[0].nField
= 1;
119 aSortData
.maKeyState
[0].bAscending
= true;
121 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
123 double nVal
= m_pDoc
->GetValue(1,0,0);
124 ASSERT_DOUBLES_EQUAL(nVal
, 1.0);
126 // check that note is also moved after sorting
127 pNote
= m_pDoc
->GetNote(1, 0, 0);
128 CPPUNIT_ASSERT(pNote
);
131 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
134 const std::vector
<std::vector
<const char*>> aData
= {
145 aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
146 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
149 aSortData
.nCol1
= aDataRange
.aStart
.Col();
150 aSortData
.nCol2
= aDataRange
.aEnd
.Col();
151 aSortData
.nRow1
= aDataRange
.aStart
.Row();
152 aSortData
.nRow2
= aDataRange
.aEnd
.Row();
153 aSortData
.bHasHeader
= true;
154 aSortData
.maKeyState
[0].nField
= 0;
155 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
157 // Title should stay at the top, numbers should be sorted numerically,
158 // numbers always come before strings, and empty cells always occur at the
160 CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc
->GetString(aPos
));
162 CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc
->GetString(aPos
));
164 CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc
->GetString(aPos
));
166 CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc
->GetString(aPos
));
168 CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc
->GetString(aPos
));
170 CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc
->GetString(aPos
));
172 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE
, m_pDoc
->GetCellType(aPos
));
174 m_pDoc
->DeleteTab(0);
177 void TestSort::testSortHorizontal()
179 SortRefUpdateSetter aUpdateSet
;
181 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true);
182 m_pDoc
->InsertTab(0, "Sort");
184 // Test case from fdo#78079.
187 const std::vector
<std::vector
<const char*>> aData
= {
188 { "table", "has UNIQUE", "Publish to EC2", "flag" },
189 { "w2gi.mobilehit", "Yes", "No", "=CONCATENATE(B2;\"-\";C2)" },
190 { "w2gi.visitors", "No", "No", "=CONCATENATE(B3;\"-\";C3)" },
191 { "w2gi.pagedimension", "Yes", "Yes", "=CONCATENATE(B4;\"-\";C4)" },
194 // Insert raw data into A1:D4.
195 ScRange aDataRange
= insertRangeData(m_pDoc
, ScAddress(0,0,0), aData
);
196 CPPUNIT_ASSERT_EQUAL(OUString("A1:D4"), aDataRange
.Format(*m_pDoc
, ScRefFlags::VALID
));
198 // Check the formula values.
199 CPPUNIT_ASSERT_EQUAL(OUString("Yes-No"), m_pDoc
->GetString(ScAddress(3,1,0)));
200 CPPUNIT_ASSERT_EQUAL(OUString("No-No"), m_pDoc
->GetString(ScAddress(3,2,0)));
201 CPPUNIT_ASSERT_EQUAL(OUString("Yes-Yes"), m_pDoc
->GetString(ScAddress(3,3,0)));
203 // Define A1:D4 as sheet-local anonymous database range.
204 m_pDoc
->SetAnonymousDBData(
205 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 3, 3)));
207 // Sort A1:D4 horizontally, ascending by row 1.
208 ScDBDocFunc
aFunc(*m_xDocShell
);
210 ScSortParam aSortData
;
215 aSortData
.bHasHeader
= true;
216 aSortData
.bByRow
= false; // Sort by column (in horizontal direction).
217 aSortData
.aDataAreaExtras
.mbCellFormats
= true;
218 aSortData
.maKeyState
[0].bDoSort
= true;
219 aSortData
.maKeyState
[0].nField
= 0;
220 aSortData
.maKeyState
[0].bAscending
= true;
221 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
222 CPPUNIT_ASSERT(bSorted
);
225 // Expected output table content. 0 = empty cell
226 std::vector
<std::vector
<const char*>> aOutputCheck
= {
227 { "table", "flag", "has UNIQUE", "Publish to EC2" },
228 { "w2gi.mobilehit", "Yes-No", "Yes", "No" },
229 { "w2gi.visitors", "No-No", "No", "No" },
230 { "w2gi.pagedimension", "Yes-Yes", "Yes", "Yes" },
233 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted by column with formula");
234 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
237 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=CONCATENATE(C2;\"-\";D2)"), m_pDoc
->GetFormula(1,1,0));
238 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=CONCATENATE(C3;\"-\";D3)"), m_pDoc
->GetFormula(1,2,0));
239 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=CONCATENATE(C4;\"-\";D4)"), m_pDoc
->GetFormula(1,3,0));
241 m_pDoc
->DeleteTab(0);
244 void TestSort::testSortHorizontalWholeColumn()
246 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true);
247 m_pDoc
->InsertTab(0, "Sort");
250 const std::vector
<std::vector
<const char*>> aData
= {
251 { "4", "2", "47", "a", "9" }
254 // Insert row data to C1:G1.
255 ScRange aSortRange
= insertRangeData(m_pDoc
, ScAddress(2,0,0), aData
);
256 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
257 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
258 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
259 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(5,0,0)));
260 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
262 // Extend the sort range to whole column.
263 aSortRange
.aEnd
.SetRow(m_pDoc
->MaxRow());
265 SCCOL nCol1
= aSortRange
.aStart
.Col();
266 SCCOL nCol2
= aSortRange
.aEnd
.Col();
267 SCROW nRow1
= aSortRange
.aStart
.Row();
268 SCROW nRow2
= aSortRange
.aEnd
.Row();
270 // Define C:G as sheet-local anonymous database range.
271 m_pDoc
->SetAnonymousDBData(
272 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, nCol1
, nRow1
, nCol2
, nRow2
, false, false)));
274 // Sort C:G horizontally ascending by row 1.
275 ScDBDocFunc
aFunc(*m_xDocShell
);
277 ScSortParam aSortData
;
278 aSortData
.nCol1
= nCol1
;
279 aSortData
.nCol2
= nCol2
;
280 aSortData
.nRow1
= nRow1
;
281 aSortData
.nRow2
= nRow2
;
282 aSortData
.bHasHeader
= false;
283 aSortData
.bByRow
= false; // Sort by column (in horizontal direction).
284 aSortData
.aDataAreaExtras
.mbCellFormats
= true;
285 aSortData
.maKeyState
[0].bDoSort
= true;
286 aSortData
.maKeyState
[0].nField
= 0;
287 aSortData
.maKeyState
[0].bAscending
= true;
288 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
289 CPPUNIT_ASSERT(bSorted
);
291 // Check the sort result.
292 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
293 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
294 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
295 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
296 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(6,0,0)));
300 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
301 CPPUNIT_ASSERT(pUndoMgr
);
304 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
305 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
306 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
307 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(5,0,0)));
308 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
312 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
313 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
314 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
315 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
316 CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc
->GetString(ScAddress(6,0,0)));
318 m_pDoc
->DeleteTab(0);
321 void TestSort::testSortSingleRow()
323 // This test case is from fdo#80462.
325 m_pDoc
->InsertTab(0, "Test");
327 // Sort range consists of only one row.
328 m_pDoc
->SetString(ScAddress(0,0,0), "X");
329 m_pDoc
->SetString(ScAddress(1,0,0), "Y");
331 // Define A1:B1 as sheet-local anonymous database range.
332 m_pDoc
->SetAnonymousDBData(
333 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 1, 0)));
335 // Sort A1:B1 horizontally, ascending by row 1.
336 ScDBDocFunc
aFunc(*m_xDocShell
);
338 ScSortParam aSortData
;
343 aSortData
.bHasHeader
= true;
344 aSortData
.bByRow
= true;
345 aSortData
.aDataAreaExtras
.mbCellFormats
= true;
346 aSortData
.maKeyState
[0].bDoSort
= true;
347 aSortData
.maKeyState
[0].nField
= 0;
348 aSortData
.maKeyState
[0].bAscending
= true;
350 // Do the sorting. This should not crash.
351 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
352 CPPUNIT_ASSERT(bSorted
);
354 // Another test case - single row horizontal sort with header column.
355 clearSheet(m_pDoc
, 0);
358 m_pDoc
->SetString(ScAddress(0,0,0), "Header");
359 m_pDoc
->SetValue(ScAddress(1,0,0), 1.0);
360 m_pDoc
->SetValue(ScAddress(2,0,0), 10.0);
361 m_pDoc
->SetValue(ScAddress(3,0,0), 3.0);
362 m_pDoc
->SetValue(ScAddress(4,0,0), 9.0);
363 m_pDoc
->SetValue(ScAddress(5,0,0), 12.0);
364 m_pDoc
->SetValue(ScAddress(6,0,0), 2.0);
366 // Define A1:G1 as sheet-local anonymous database range.
367 m_pDoc
->SetAnonymousDBData(
368 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 6, 0, false, true)));
370 // Update the sort data.
373 aSortData
.bByRow
= false;
374 bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
375 CPPUNIT_ASSERT(bSorted
);
378 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
379 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
380 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
381 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
382 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
383 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
384 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
387 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
388 CPPUNIT_ASSERT(pUndoMgr
);
391 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
392 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
393 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
394 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
395 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
396 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
397 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
401 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
402 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(1,0,0)));
403 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(2,0,0)));
404 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,0,0)));
405 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(4,0,0)));
406 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc
->GetValue(ScAddress(5,0,0)));
407 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(6,0,0)));
409 m_pDoc
->DeleteTab(0);
412 // regression test of fdo#53814, sorting doesn't work as expected
413 // if cells in the sort are referenced by formulas
414 void TestSort::testSortWithFormulaRefs()
416 SortRefUpdateSetter aUpdateSet
;
418 m_pDoc
->InsertTab(0, "List1");
419 m_pDoc
->InsertTab(1, "List2");
421 const char* aFormulaData
[6] = {
422 "=IF($List1.A2<>\"\";$List1.A2;\"\")",
423 "=IF($List1.A3<>\"\";$List1.A3;\"\")",
424 "=IF($List1.A4<>\"\";$List1.A4;\"\")",
425 "=IF($List1.A5<>\"\";$List1.A5;\"\")",
426 "=IF($List1.A6<>\"\";$List1.A6;\"\")",
427 "=IF($List1.A7<>\"\";$List1.A7;\"\")",
430 const char* const aTextData
[4] = {
437 const char* aResults
[6] = {
446 // Insert data to sort in A2:A5 on the 1st sheet.
447 for (SCROW i
= 1; i
<= 4; ++i
)
448 m_pDoc
->SetString( 0, i
, 0, OUString::createFromAscii(aTextData
[i
-1]) );
450 // Insert formulas in A1:A6 on the 2nd sheet.
451 for (size_t i
= 0; i
< std::size(aFormulaData
); ++i
)
452 m_pDoc
->SetString( 0, i
, 1, OUString::createFromAscii(aFormulaData
[i
]) );
454 // Sort data in A2:A8 on the 1st sheet. No column header.
455 ScSortParam aSortData
;
460 aSortData
.maKeyState
[0].bDoSort
= true;
461 aSortData
.maKeyState
[0].nField
= 0;
463 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
465 for (size_t i
= 0; i
< std::size(aResults
); ++i
)
467 OUString sResult
= m_pDoc
->GetString(0, i
+ 1, 0);
468 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults
[i
] ), sResult
);
470 m_pDoc
->DeleteTab(1);
471 m_pDoc
->DeleteTab(0);
474 void TestSort::testSortWithStrings()
476 m_pDoc
->InsertTab(0, "Test");
478 ScFieldEditEngine
& rEE
= m_pDoc
->GetEditEngine();
479 rEE
.SetTextCurrentDefaults("Val1");
480 m_pDoc
->SetString(ScAddress(1,1,0), "Header");
481 m_pDoc
->SetString(ScAddress(1,2,0), "Val2");
482 m_pDoc
->SetEditText(ScAddress(1,3,0), rEE
.CreateTextObject());
484 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(1,1,0)));
485 CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc
->GetString(ScAddress(1,2,0)));
486 CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc
->GetString(ScAddress(1,3,0)));
493 aParam
.bHasHeader
= true;
494 aParam
.maKeyState
[0].bDoSort
= true;
495 aParam
.maKeyState
[0].bAscending
= true;
496 aParam
.maKeyState
[0].nField
= 1;
498 m_pDoc
->Sort(0, aParam
, false, true, nullptr, nullptr);
500 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(1,1,0)));
501 CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc
->GetString(ScAddress(1,2,0)));
502 CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc
->GetString(ScAddress(1,3,0)));
504 aParam
.maKeyState
[0].bAscending
= false;
506 m_pDoc
->Sort(0, aParam
, false, true, nullptr, nullptr);
508 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(1,1,0)));
509 CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc
->GetString(ScAddress(1,2,0)));
510 CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc
->GetString(ScAddress(1,3,0)));
512 m_pDoc
->DeleteTab(0);
515 void TestSort::testSortInFormulaGroup()
517 SortRefUpdateSetter aUpdateSet
;
519 static const struct {
524 { 0, 0, "3" }, { 1, 0, "=A1" },
525 { 0, 1, "1" }, { 1, 1, "=A2" },
526 { 0, 2, "20" }, { 1, 2, "=A3" },
527 { 0, 3, "10" }, { 1, 3, "=A4+1" }, // swap across groups
528 { 0, 4, "2" }, { 1, 4, "=A5+1" },
529 { 0, 5, "101" }, { 1, 5, "=A6" }, // swap inside contiguous group
530 { 0, 6, "100" }, { 1, 6, "=A7" },
531 { 0, 7, "102" }, { 1, 7, "=A8" },
532 { 0, 8, "104" }, { 1, 8, "=A9" },
533 { 0, 9, "103" }, { 1, 9, "=A10" },
536 m_pDoc
->InsertTab(0, "sorttest");
538 for ( auto const & i
: aEntries
)
539 m_pDoc
->SetString( i
.nCol
, i
.nRow
, 0,
540 OUString::createFromAscii( i
.pData
) );
542 ScSortParam aSortData
;
547 aSortData
.maKeyState
[0].bDoSort
= true;
548 aSortData
.maKeyState
[0].nField
= 0;
549 aSortData
.maKeyState
[0].bAscending
= true;
551 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
553 static const double aResults
[] = {
566 for ( SCROW i
= 0; i
< SCROW(std::size( aEntries
)); ++i
)
568 double val
= m_pDoc
->GetValue( aEntries
[i
].nCol
, aEntries
[i
].nRow
, 0 );
569 CPPUNIT_ASSERT_MESSAGE("Mis-matching value after sort.",
570 rtl::math::approxEqual(val
, aResults
[i
]));
573 m_pDoc
->DeleteTab( 0 );
576 void TestSort::testSortWithCellFormats()
580 bool isBold( const ScPatternAttr
* pPat
) const
584 cerr
<< "Pattern is NULL!" << endl
;
588 const SfxPoolItem
* pItem
= nullptr;
589 if (!pPat
->GetItemSet().HasItem(ATTR_FONT_WEIGHT
, &pItem
))
591 cerr
<< "Pattern does not have a font weight item, but it should." << endl
;
595 CPPUNIT_ASSERT(pItem
);
597 if (static_cast<const SvxWeightItem
*>(pItem
)->GetEnumValue() != WEIGHT_BOLD
)
599 cerr
<< "Font weight should be bold." << endl
;
606 bool isItalic( const ScPatternAttr
* pPat
) const
610 cerr
<< "Pattern is NULL!" << endl
;
614 const SfxPoolItem
* pItem
= nullptr;
615 if (!pPat
->GetItemSet().HasItem(ATTR_FONT_POSTURE
, &pItem
))
617 cerr
<< "Pattern does not have a font posture item, but it should." << endl
;
621 CPPUNIT_ASSERT(pItem
);
623 if (static_cast<const SvxPostureItem
*>(pItem
)->GetEnumValue() != ITALIC_NORMAL
)
625 cerr
<< "Italic should be applied.." << endl
;
632 bool isNormal( const ScPatternAttr
* pPat
) const
636 cerr
<< "Pattern is NULL!" << endl
;
640 const SfxPoolItem
* pItem
= nullptr;
641 if (pPat
->GetItemSet().HasItem(ATTR_FONT_WEIGHT
, &pItem
))
643 // Check if the font weight is applied.
644 if (static_cast<const SvxWeightItem
*>(pItem
)->GetEnumValue() == WEIGHT_BOLD
)
646 cerr
<< "This cell is bold, but shouldn't." << endl
;
651 if (pPat
->GetItemSet().HasItem(ATTR_FONT_POSTURE
, &pItem
))
653 // Check if the italics is applied.
654 if (static_cast<const SvxPostureItem
*>(pItem
)->GetEnumValue() == ITALIC_NORMAL
)
656 cerr
<< "This cell is italic, but shouldn't." << endl
;
666 m_pDoc
->InsertTab(0, "Test");
668 // Insert some values into A1:A4.
669 m_pDoc
->SetString(ScAddress(0,0,0), "Header");
670 m_pDoc
->SetString(ScAddress(0,1,0), "Normal");
671 m_pDoc
->SetString(ScAddress(0,2,0), "Bold");
672 m_pDoc
->SetString(ScAddress(0,3,0), "Italic");
674 // Set A3 bold and A4 italic.
675 const ScPatternAttr
* pPat
= m_pDoc
->GetPattern(ScAddress(0,2,0));
676 CPPUNIT_ASSERT(pPat
);
678 ScPatternAttr
aNewPat(*pPat
);
679 SfxItemSet
& rSet
= aNewPat
.GetItemSet();
680 rSet
.Put(SvxWeightItem(WEIGHT_BOLD
, ATTR_FONT_WEIGHT
));
681 m_pDoc
->ApplyPattern(0, 2, 0, aNewPat
);
683 // Make sure it's really in.
684 bool bGood
= aCheck
.isBold(m_pDoc
->GetPattern(ScAddress(0,2,0)));
685 CPPUNIT_ASSERT_MESSAGE("A3 is not bold but it should.", bGood
);
688 pPat
= m_pDoc
->GetPattern(ScAddress(0,3,0));
689 CPPUNIT_ASSERT(pPat
);
691 ScPatternAttr
aNewPat(*pPat
);
692 SfxItemSet
& rSet
= aNewPat
.GetItemSet();
693 rSet
.Put(SvxPostureItem(ITALIC_NORMAL
, ATTR_FONT_POSTURE
));
694 m_pDoc
->ApplyPattern(0, 3, 0, aNewPat
);
696 bool bGood
= aCheck
.isItalic(m_pDoc
->GetPattern(ScAddress(0,3,0)));
697 CPPUNIT_ASSERT_MESSAGE("A4 is not italic but it should.", bGood
);
700 // Define A1:A4 as sheet-local anonymous database range, else sort wouldn't run.
701 m_pDoc
->SetAnonymousDBData(
702 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 3)));
704 // Sort A1:A4 ascending with cell formats.
705 ScDBDocFunc
aFunc(*m_xDocShell
);
707 ScSortParam aSortData
;
712 aSortData
.bHasHeader
= true;
713 aSortData
.aDataAreaExtras
.mbCellFormats
= true;
714 aSortData
.maKeyState
[0].bDoSort
= true;
715 aSortData
.maKeyState
[0].nField
= 0;
716 aSortData
.maKeyState
[0].bAscending
= true;
717 bool bSorted
= aFunc
.Sort(0, aSortData
, true, false, true);
718 CPPUNIT_ASSERT(bSorted
);
720 // Check the sort result.
721 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
722 CPPUNIT_ASSERT_EQUAL(OUString("Bold"), m_pDoc
->GetString(ScAddress(0,1,0)));
723 CPPUNIT_ASSERT_EQUAL(OUString("Italic"), m_pDoc
->GetString(ScAddress(0,2,0)));
724 CPPUNIT_ASSERT_EQUAL(OUString("Normal"), m_pDoc
->GetString(ScAddress(0,3,0)));
726 // A2 should be bold now.
727 bool bBold
= aCheck
.isBold(m_pDoc
->GetPattern(ScAddress(0,1,0)));
728 CPPUNIT_ASSERT_MESSAGE("A2 should be bold after the sort.", bBold
);
730 // and A3 should be italic.
731 bool bItalic
= aCheck
.isItalic(m_pDoc
->GetPattern(ScAddress(0,2,0)));
732 CPPUNIT_ASSERT_MESSAGE("A3 should be italic.", bItalic
);
734 // A4 should have neither bold nor italic.
735 bool bNormal
= aCheck
.isNormal(m_pDoc
->GetPattern(ScAddress(0,3,0)));
736 CPPUNIT_ASSERT_MESSAGE("A4 should be neither bold nor italic.", bNormal
);
738 m_pDoc
->DeleteTab(0);
741 void TestSort::testSortRefUpdate()
743 SortTypeSetter
aSortTypeSet(true);
745 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
746 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
748 m_pDoc
->InsertTab(0, "Sort");
750 // Set values to sort in column A.
751 m_pDoc
->SetString(ScAddress(0,0,0), "Header");
753 double aValues
[] = { 4.0, 36.0, 14.0, 29.0, 98.0, 78.0, 0.0, 99.0, 1.0 };
754 size_t nCount
= std::size(aValues
);
755 for (size_t i
= 0; i
< nCount
; ++i
)
756 m_pDoc
->SetValue(ScAddress(0,i
+1,0), aValues
[i
]);
758 // Set formulas to reference these values in column C.
759 m_pDoc
->SetString(ScAddress(2,0,0), "Formula");
760 for (size_t i
= 0; i
< nCount
; ++i
)
761 m_pDoc
->SetString(ScAddress(2,1+i
,0), "=RC[-2]");
763 // Check the values in column C.
764 for (size_t i
= 0; i
< nCount
; ++i
)
766 double fCheck
= aValues
[i
];
767 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
770 ScDBDocFunc
aFunc(*m_xDocShell
);
772 // Define A1:A10 as sheet-local anonymous database range, else sort wouldn't run.
773 m_pDoc
->SetAnonymousDBData(
774 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 9)));
776 // Sort A1:A10 (with a header row).
777 ScSortParam aSortData
;
782 aSortData
.bHasHeader
= true;
783 aSortData
.maKeyState
[0].bDoSort
= true;
784 aSortData
.maKeyState
[0].nField
= 0;
785 aSortData
.maKeyState
[0].bAscending
= true;
786 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
787 CPPUNIT_ASSERT(bSorted
);
789 double aSorted
[] = { 0.0, 1.0, 4.0, 14.0, 29.0, 36.0, 78.0, 98.0, 99.0 };
791 // Check the sort result.
792 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
793 for (size_t i
= 0; i
< nCount
; ++i
)
795 double fCheck
= aSorted
[i
];
796 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
799 // Sorting should not alter the values in column C.
800 m_pDoc
->CalcAll(); // just in case...
801 for (size_t i
= 0; i
< nCount
; ++i
)
803 double fCheck
= aValues
[i
];
804 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
807 // C2 should now point to A4.
808 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2!", OUString("=R[2]C[-2]"), m_pDoc
->GetFormula(2,1,0));
811 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
814 // Check the undo result.
815 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
816 for (size_t i
= 0; i
< nCount
; ++i
)
818 double fCheck
= aValues
[i
];
819 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
822 // Values in column C should still be unaltered.
823 m_pDoc
->CalcAll(); // just in case...
824 for (size_t i
= 0; i
< nCount
; ++i
)
826 double fCheck
= aValues
[i
];
827 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
830 // C2 should now point to A2.
831 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2!", OUString("=RC[-2]"), m_pDoc
->GetFormula(2,1,0));
836 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
837 for (size_t i
= 0; i
< nCount
; ++i
)
839 double fCheck
= aSorted
[i
];
840 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
843 // Sorting should not alter the values in column C.
844 m_pDoc
->CalcAll(); // just in case...
845 for (size_t i
= 0; i
< nCount
; ++i
)
847 double fCheck
= aValues
[i
];
848 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0)));
851 // C2 should now point to A4.
852 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2!", OUString("=R[2]C[-2]"), m_pDoc
->GetFormula(2,1,0));
857 // Formulas in column C should all be "RC[-2]" again.
858 for (size_t i
= 0; i
< nCount
; ++i
)
859 m_pDoc
->SetString(ScAddress(2,1+i
,0), "=RC[-2]");
861 // Turn off reference update on sort.
862 SortTypeSetter::changeTo(false);
864 bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
865 CPPUNIT_ASSERT(bSorted
);
867 // Check the sort result again.
868 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
869 for (size_t i
= 0; i
< nCount
; ++i
)
871 double fCheck
= aSorted
[i
];
872 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(0,i
+1,0)));
875 // Formulas in column C should all remain "RC[-2]".
876 for (size_t i
= 0; i
< nCount
; ++i
)
877 m_pDoc
->SetString(ScAddress(2,1+i
,0), "=RC[-2]");
879 // The values in column C should now be the same as sorted values in column A.
880 m_pDoc
->CalcAll(); // just in case...
881 for (size_t i
= 0; i
< nCount
; ++i
)
883 double fCheck
= aSorted
[i
];
884 CPPUNIT_ASSERT_EQUAL(fCheck
, m_pDoc
->GetValue(ScAddress(2,i
+1,0))); // column C
887 m_pDoc
->DeleteTab(0);
890 void TestSort::testSortRefUpdate2()
892 SortRefUpdateSetter aUpdateSet
;
894 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
895 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
897 m_pDoc
->InsertTab(0, "Sort");
900 const char* aData
[][2] = {
906 { nullptr, nullptr } // terminator
909 for (SCROW i
= 0; aData
[i
][0]; ++i
)
911 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
][0]));
912 m_pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aData
[i
][1]));
915 // Check the values in B2:B5.
916 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
917 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
918 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
919 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
921 ScDBDocFunc
aFunc(*m_xDocShell
);
923 // Define A1:B5 as sheet-local anonymous database range, else sort wouldn't run.
924 m_pDoc
->SetAnonymousDBData(
925 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 1, 4)));
927 // Sort A1:B5 by column A (with a row header).
928 ScSortParam aSortData
;
933 aSortData
.bHasHeader
= true;
934 aSortData
.maKeyState
[0].bDoSort
= true;
935 aSortData
.maKeyState
[0].nField
= 0;
936 aSortData
.maKeyState
[0].bAscending
= true;
937 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
938 CPPUNIT_ASSERT(bSorted
);
940 // Check the sort result in column A.
941 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
942 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
943 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
944 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
947 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
948 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
949 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
950 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
952 // Formulas in column B should still point to their respective left neighbor cell.
953 for (SCROW i
= 1; i
<= 4; ++i
)
955 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=RC[-1]"), m_pDoc
->GetFormula(1,i
,0));
958 // Undo and check the result in column B.
959 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
962 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
963 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
964 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
965 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
970 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
971 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
972 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
973 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
975 m_pDoc
->DeleteTab(0);
978 void TestSort::testSortRefUpdate3()
980 SortRefUpdateSetter aUpdateSet
;
982 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
983 m_pDoc
->InsertTab(0, "Sort");
985 const char* pData
[] = {
992 nullptr // terminator
995 for (SCROW i
= 0; pData
[i
]; ++i
)
996 m_pDoc
->SetString(ScAddress(0,i
,0), OUString::createFromAscii(pData
[i
]));
998 // Check the initial values.
999 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1000 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1001 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1002 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1003 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1004 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1006 ScDBDocFunc
aFunc(*m_xDocShell
);
1009 m_pDoc
->SetAnonymousDBData(
1010 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 5)));
1012 // Sort A1:A6 by column A (with a row header).
1013 ScSortParam aSortData
;
1014 aSortData
.nCol1
= 0;
1015 aSortData
.nCol2
= 0;
1016 aSortData
.nRow1
= 0;
1017 aSortData
.nRow2
= 5;
1018 aSortData
.bHasHeader
= true;
1019 aSortData
.maKeyState
[0].bDoSort
= true;
1020 aSortData
.maKeyState
[0].nField
= 0;
1021 aSortData
.maKeyState
[0].bAscending
= true;
1022 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1023 CPPUNIT_ASSERT(bSorted
);
1025 // Check the sorted values.
1027 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1028 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1029 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1030 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1031 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1032 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1034 // Make sure the formula cells have been adjusted correctly.
1035 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A4.", OUString("=A2+A3"), m_pDoc
->GetFormula(0,3,0));
1036 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", OUString("=A2+10"), m_pDoc
->GetFormula(0,4,0));
1037 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", OUString("=A3+10"), m_pDoc
->GetFormula(0,5,0));
1039 // Undo and check the result.
1040 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1043 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1044 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1045 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1046 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1047 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1048 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1050 // Redo and check the result.
1053 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1054 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1055 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1056 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1057 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1058 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1060 m_pDoc
->DeleteTab(0);
1063 // Derived from fdo#79441 https://bugs.freedesktop.org/attachment.cgi?id=100144
1064 // testRefInterne.ods
1065 void TestSort::testSortRefUpdate4()
1067 // This test has to work in both update reference modes.
1069 SortRefNoUpdateSetter aUpdateSet
;
1070 testSortRefUpdate4_Impl();
1073 SortRefUpdateSetter aUpdateSet
;
1074 testSortRefUpdate4_Impl();
1078 void TestSort::testSortRefUpdate4_Impl()
1080 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1081 m_pDoc
->InsertTab(0, "Sort");
1082 m_pDoc
->InsertTab(1, "Lesson1");
1083 m_pDoc
->InsertTab(2, "Lesson2");
1086 const std::vector
<std::vector
<const char*>> aData
= {
1088 { "Student1", "1" },
1089 { "Student2", "2" },
1090 { "Student3", "3" },
1091 { "Student4", "4" },
1092 { "Student5", "5" },
1096 ScAddress
aPos(0,0,nTab
);
1097 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 1, aData
.size(), nTab
));
1098 ScRange aLesson1Range
= insertRangeData(m_pDoc
, aPos
, aData
);
1099 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aLesson1Range
.aStart
);
1103 const std::vector
<std::vector
<const char*>> aData
= {
1105 { "=Lesson1.A2", "3" },
1106 { "=Lesson1.A3", "4" },
1107 { "=Lesson1.A4", "9" },
1108 { "=Lesson1.A5", "6" },
1109 { "=Lesson1.A6", "3" },
1113 ScAddress
aPos(0,0,nTab
);
1114 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 1, aData
.size(), nTab
));
1115 ScRange aLesson2Range
= insertRangeData(m_pDoc
, aPos
, aData
);
1116 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aLesson2Range
.aStart
);
1121 const std::vector
<std::vector
<const char*>> aData
= {
1122 { "Name", "Lesson1", "Lesson2", "Average" },
1123 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B2:C2)" },
1124 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B3:C3)" },
1125 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B4:C4)" },
1126 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B5:C5)" },
1127 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B6:C6)" },
1131 ScAddress
aPos(0,0,nTab
);
1132 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 1, aData
.size(), nTab
));
1133 aSortRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1134 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aSortRange
.aStart
);
1137 ScDBDocFunc
aFunc(*m_xDocShell
);
1139 // Sort A1:D6 by column D (Average, with a row header).
1141 ScSortParam aSortData
;
1142 aSortData
.nCol1
= aSortRange
.aStart
.Col();
1143 aSortData
.nCol2
= aSortRange
.aEnd
.Col();
1144 aSortData
.nRow1
= aSortRange
.aStart
.Row();
1145 aSortData
.nRow2
= aSortRange
.aEnd
.Row();
1146 aSortData
.bHasHeader
= true;
1147 aSortData
.maKeyState
[0].bDoSort
= true; // sort on
1148 aSortData
.maKeyState
[0].nField
= 3; // Average
1149 aSortData
.maKeyState
[0].bAscending
= false; // descending
1151 m_pDoc
->SetAnonymousDBData( 0, std::unique_ptr
<ScDBData
>(new ScDBData( STR_DB_LOCAL_NONAME
, aSortRange
.aStart
.Tab(),
1152 aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nCol2
, aSortData
.nRow2
)));
1154 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1155 CPPUNIT_ASSERT(bSorted
);
1157 // Check the sorted values.
1159 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1160 CPPUNIT_ASSERT_EQUAL(OUString("Student3"), m_pDoc
->GetString(ScAddress(0,1,0)));
1161 CPPUNIT_ASSERT_EQUAL(OUString("Student4"), m_pDoc
->GetString(ScAddress(0,2,0)));
1162 CPPUNIT_ASSERT_EQUAL(OUString("Student5"), m_pDoc
->GetString(ScAddress(0,3,0)));
1163 CPPUNIT_ASSERT_EQUAL(OUString("Student2"), m_pDoc
->GetString(ScAddress(0,4,0)));
1164 CPPUNIT_ASSERT_EQUAL(OUString("Student1"), m_pDoc
->GetString(ScAddress(0,5,0)));
1165 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1166 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1167 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1168 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1169 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1171 // Make sure the formula cells have been adjusted correctly.
1172 const char* aCheck
[][4] = {
1173 // Name Lesson1 Lesson2 Average
1174 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B2:C2)" },
1175 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B3:C3)" },
1176 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B4:C4)" },
1177 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B5:C5)" },
1178 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B6:C6)" },
1180 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aCheck
)); ++nRow
)
1182 for (SCCOL nCol
=0; nCol
< 4; ++nCol
)
1184 CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("Wrong formula in " + OStringChar(char('A'+nCol
)) + OString::number(nRow
+2) + ".").getStr(), OUString::createFromAscii(aCheck
[nRow
][nCol
]), m_pDoc
->GetFormula(nCol
,nRow
+1,0));
1188 // Undo and check the result.
1189 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1192 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1193 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1194 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1195 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1196 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1197 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1199 // Redo and check the result.
1202 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1203 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1204 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1205 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1206 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1207 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1210 // Sort A2:AMJ6 by column A (Name, without header).
1212 ScSortParam aSortData
;
1213 aSortData
.nCol1
= 0;
1214 aSortData
.nCol2
= m_pDoc
->MaxCol();
1215 aSortData
.nRow1
= aSortRange
.aStart
.Row()+1;
1216 aSortData
.nRow2
= aSortRange
.aEnd
.Row();
1217 aSortData
.bHasHeader
= false;
1218 aSortData
.maKeyState
[0].bDoSort
= true; // sort on
1219 aSortData
.maKeyState
[0].nField
= 0; // Name
1220 aSortData
.maKeyState
[0].bAscending
= false; // descending
1222 m_pDoc
->SetAnonymousDBData( 0, std::unique_ptr
<ScDBData
>(new ScDBData( STR_DB_LOCAL_NONAME
, aSortRange
.aStart
.Tab(),
1223 aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nCol2
, aSortData
.nRow2
)));
1225 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1226 CPPUNIT_ASSERT(bSorted
);
1228 // Check the sorted values.
1230 CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc
->GetString(ScAddress(0,0,0)));
1231 CPPUNIT_ASSERT_EQUAL(OUString("Student5"), m_pDoc
->GetString(ScAddress(0,1,0)));
1232 CPPUNIT_ASSERT_EQUAL(OUString("Student4"), m_pDoc
->GetString(ScAddress(0,2,0)));
1233 CPPUNIT_ASSERT_EQUAL(OUString("Student3"), m_pDoc
->GetString(ScAddress(0,3,0)));
1234 CPPUNIT_ASSERT_EQUAL(OUString("Student2"), m_pDoc
->GetString(ScAddress(0,4,0)));
1235 CPPUNIT_ASSERT_EQUAL(OUString("Student1"), m_pDoc
->GetString(ScAddress(0,5,0)));
1236 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc
->GetValue(ScAddress(3,1,0)));
1237 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc
->GetValue(ScAddress(3,2,0)));
1238 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc
->GetValue(ScAddress(3,3,0)));
1239 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc
->GetValue(ScAddress(3,4,0)));
1240 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(3,5,0)));
1242 // Make sure the formula cells have been adjusted correctly.
1243 const char* aCheck
[][4] = {
1244 // Name Lesson1 Lesson2 Average
1245 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B2:C2)" },
1246 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B3:C3)" },
1247 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B4:C4)" },
1248 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B5:C5)" },
1249 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B6:C6)" },
1251 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aCheck
)); ++nRow
)
1253 for (SCCOL nCol
=0; nCol
< 4; ++nCol
)
1255 CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("Wrong formula in " + OStringChar(char('A'+nCol
)) + OString::number(nRow
+2) + ".").getStr(), OUString::createFromAscii(aCheck
[nRow
][nCol
]), m_pDoc
->GetFormula(nCol
,nRow
+1,0));
1260 m_pDoc
->DeleteTab(2);
1261 m_pDoc
->DeleteTab(1);
1262 m_pDoc
->DeleteTab(0);
1265 // Make sure the refupdate works also with volatile cells, see fdo#83067
1266 /* FIXME: this test is not roll-over-midnight safe and will fail then! We may
1267 * want to have something different, but due to the nature of volatile
1268 * functions it's not that easy to come up with something reproducible staying
1269 * stable over sorts... ;-) Check for time and don't run test a few seconds
1270 * before midnight, ermm... */
1271 void TestSort::testSortRefUpdate5()
1273 SortRefUpdateSetter aUpdateSet
;
1275 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1276 m_pDoc
->InsertTab(0, "Sort");
1278 double aValCheck
[][3] = {
1279 // Result, Unsorted order, Sorted result.
1287 const std::vector
<std::vector
<const char*>> aData
= {
1288 { "Date", "Volatile", "Order" },
1289 { "1999-05-05", "=TODAY()-$A2", "4" },
1290 { "1994-10-18", "=TODAY()-$A3", "1" },
1291 { "1996-06-30", "=TODAY()-$A4", "3" },
1292 { "1995-11-21", "=TODAY()-$A5", "2" },
1296 ScAddress
aPos(0,0,nTab
);
1297 clearRange(m_pDoc
, ScRange(0, 0, nTab
, 2, aData
.size(), nTab
));
1298 aSortRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1299 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aSortRange
.aStart
);
1301 // Actual results and expected sorted results.
1302 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1304 double fVal
= m_pDoc
->GetValue(ScAddress(1,nRow
+1,0));
1305 aValCheck
[nRow
][0] = fVal
;
1306 aValCheck
[static_cast<size_t>(aValCheck
[nRow
][1])-1][2] = fVal
;
1310 ScDBDocFunc
aFunc(*m_xDocShell
);
1313 m_pDoc
->SetAnonymousDBData( 0, std::unique_ptr
<ScDBData
>(new ScDBData( STR_DB_LOCAL_NONAME
, aSortRange
.aStart
.Tab(),
1314 aSortRange
.aStart
.Col(), aSortRange
.aStart
.Row(), aSortRange
.aEnd
.Col(), aSortRange
.aEnd
.Row())));
1316 // Sort by column A.
1317 ScSortParam aSortData
;
1318 aSortData
.nCol1
= aSortRange
.aStart
.Col();
1319 aSortData
.nCol2
= aSortRange
.aEnd
.Col();
1320 aSortData
.nRow1
= aSortRange
.aStart
.Row();
1321 aSortData
.nRow2
= aSortRange
.aEnd
.Row();
1322 aSortData
.bHasHeader
= true;
1323 aSortData
.maKeyState
[0].bDoSort
= true; // sort on
1324 aSortData
.maKeyState
[0].nField
= 0; // Date
1325 aSortData
.maKeyState
[0].bAscending
= true; // ascending
1326 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1327 CPPUNIT_ASSERT(bSorted
);
1329 // Check the sorted values.
1331 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1333 size_t i
= static_cast<size_t>(m_pDoc
->GetValue(ScAddress(2,nRow
+1,0))); // order 1..4
1334 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(nRow
+1), i
);
1335 CPPUNIT_ASSERT_EQUAL( aValCheck
[i
-1][2], m_pDoc
->GetValue(ScAddress(1,nRow
+1,0)));
1338 // Make sure the formula cells have been adjusted correctly.
1339 const char* aFormulaCheck
[] = {
1346 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aFormulaCheck
)); ++nRow
)
1348 CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("Wrong formula in B" + OString::number(nRow
+2) + ".").getStr(), OUString::createFromAscii(aFormulaCheck
[nRow
]), m_pDoc
->GetFormula(1,nRow
+1,0));
1351 // Undo and check the result.
1352 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1355 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1357 CPPUNIT_ASSERT_EQUAL( aValCheck
[nRow
][0], m_pDoc
->GetValue(ScAddress(1,nRow
+1,0)));
1358 CPPUNIT_ASSERT_EQUAL( aValCheck
[nRow
][1], m_pDoc
->GetValue(ScAddress(2,nRow
+1,0)));
1361 // Redo and check the result.
1364 for (SCROW nRow
=0; nRow
< static_cast<SCROW
>(SAL_N_ELEMENTS(aValCheck
)); ++nRow
)
1366 size_t i
= static_cast<size_t>(m_pDoc
->GetValue(ScAddress(2,nRow
+1,0))); // order 1..4
1367 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(nRow
+1), i
);
1368 CPPUNIT_ASSERT_EQUAL( aValCheck
[i
-1][2], m_pDoc
->GetValue(ScAddress(1,nRow
+1,0)));
1371 m_pDoc
->DeleteTab(0);
1374 void TestSort::testSortRefUpdate6()
1376 SortRefNoUpdateSetter aUpdateSet
;
1378 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1379 m_pDoc
->InsertTab(0, "Sort");
1381 const std::vector
<std::vector
<const char*>> aData
= {
1382 { "Order", "Value", "1" },
1383 { "9", "1", "=C1+B2" },
1384 { "1", "2", "=C2+B3" },
1385 { "8", "3", "=C3+B4" },
1388 ScAddress
aPos(0,0,0);
1389 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1390 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1393 // Expected output table content. 0 = empty cell
1394 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1395 { "Order", "Value", "1" },
1401 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1402 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1405 ScDBDocFunc
aFunc(*m_xDocShell
);
1408 m_pDoc
->SetAnonymousDBData(
1409 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 2, 3)));
1411 // Sort A1:A6 by column A (with a row header).
1412 ScSortParam aSortData
;
1413 aSortData
.nCol1
= 0;
1414 aSortData
.nCol2
= 2;
1415 aSortData
.nRow1
= 0;
1416 aSortData
.nRow2
= 3;
1417 aSortData
.bHasHeader
= true;
1418 aSortData
.maKeyState
[0].bDoSort
= true;
1419 aSortData
.maKeyState
[0].nField
= 0;
1420 aSortData
.maKeyState
[0].bAscending
= true;
1421 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1422 CPPUNIT_ASSERT(bSorted
);
1425 // Expected output table content. 0 = empty cell
1426 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1427 { "Order", "Value", "1" },
1433 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1434 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1437 // Make sure that the formulas in C2:C4 are not adjusted.
1438 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=C1+B2"), m_pDoc
->GetFormula(2,1,0));
1439 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=C2+B3"), m_pDoc
->GetFormula(2,2,0));
1440 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=C3+B4"), m_pDoc
->GetFormula(2,3,0));
1443 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1444 CPPUNIT_ASSERT(pUndoMgr
);
1449 // Expected output table content. 0 = empty cell
1450 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1451 { "Order", "Value", "1" },
1457 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After undo");
1458 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1464 // Expected output table content. 0 = empty cell
1465 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1466 { "Order", "Value", "1" },
1472 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After redo");
1473 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1476 // Change the value of C1 and make sure the formula broadcasting chain still works.
1477 ScDocFunc
& rFunc
= m_xDocShell
->GetDocFunc();
1478 rFunc
.SetValueCell(ScAddress(2,0,0), 11.0, false);
1480 // Expected output table content. 0 = empty cell
1481 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1482 { "Order", "Value", "11" },
1488 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Change the header value");
1489 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1495 // Expected output table content. 0 = empty cell
1496 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1497 { "Order", "Value", "1" },
1503 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After undo of header value change");
1504 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1507 m_pDoc
->DeleteTab(0);
1510 // fdo#86762 check that broadcasters are sorted correctly and empty cell is
1512 void TestSort::testSortBroadcaster()
1514 SortRefNoUpdateSetter aUpdateSet
;
1516 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1517 m_pDoc
->InsertTab(0, "Sort");
1520 const std::vector
<std::vector
<const char*>> aData
= {
1521 { "1", nullptr, nullptr, "=B1", "=$B$1", "=SUM(A1:B1)", "=SUM($A$1:$B$1)" },
1522 { "2", "8", nullptr, "=B2", "=$B$2", "=SUM(A2:B2)", "=SUM($A$2:$B$2)" },
1525 ScAddress
aPos(0,0,0);
1526 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1527 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1530 // Expected output table content. 0 = empty cell
1531 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1532 { "1", nullptr, nullptr, "0", "0", "1", "1" },
1533 { "2", "8", nullptr, "8", "8", "10", "10" },
1536 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1537 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1541 m_pDoc
->SetAnonymousDBData(
1542 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 1, 1)));
1544 ScDBDocFunc
aFunc(*m_xDocShell
);
1546 // Sort A1:B2 by column A descending.
1547 ScSortParam aSortData
;
1548 aSortData
.nCol1
= 0;
1549 aSortData
.nCol2
= 1;
1550 aSortData
.nRow1
= 0;
1551 aSortData
.nRow2
= 1;
1552 aSortData
.bHasHeader
= false;
1553 aSortData
.bByRow
= true;
1554 aSortData
.maKeyState
[0].bDoSort
= true;
1555 aSortData
.maKeyState
[0].nField
= 0;
1556 aSortData
.maKeyState
[0].bAscending
= false;
1557 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1558 CPPUNIT_ASSERT(bSorted
);
1561 // Expected output table content. 0 = empty cell
1562 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1563 { "2", "8", nullptr, "8", "8", "10", "10" },
1564 { "1", nullptr, nullptr, "0", "0", "1", "1" },
1567 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1568 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1571 // Make sure that the formulas in D1:G2 are not adjusted.
1572 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=B1"), m_pDoc
->GetFormula(3,0,0));
1573 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=B2"), m_pDoc
->GetFormula(3,1,0));
1574 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=$B$1"), m_pDoc
->GetFormula(4,0,0));
1575 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=$B$2"), m_pDoc
->GetFormula(4,1,0));
1576 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A1:B1)"), m_pDoc
->GetFormula(5,0,0));
1577 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A2:B2)"), m_pDoc
->GetFormula(5,1,0));
1578 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM($A$1:$B$1)"), m_pDoc
->GetFormula(6,0,0));
1579 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM($A$2:$B$2)"), m_pDoc
->GetFormula(6,1,0));
1581 // Enter new value and check that it is broadcasted. First in empty cell.
1582 m_pDoc
->SetString(1,1,0, "16");
1583 double nVal
= m_pDoc
->GetValue(3,1,0);
1584 ASSERT_DOUBLES_EQUAL( 16.0, nVal
);
1585 nVal
= m_pDoc
->GetValue(4,1,0);
1586 ASSERT_DOUBLES_EQUAL( 16.0, nVal
);
1587 nVal
= m_pDoc
->GetValue(5,1,0);
1588 ASSERT_DOUBLES_EQUAL( 17.0, nVal
);
1589 nVal
= m_pDoc
->GetValue(6,1,0);
1590 ASSERT_DOUBLES_EQUAL( 17.0, nVal
);
1592 // Enter new value and check that it is broadcasted. Now overwriting data.
1593 m_pDoc
->SetString(1,0,0, "32");
1594 nVal
= m_pDoc
->GetValue(3,0,0);
1595 ASSERT_DOUBLES_EQUAL( 32.0, nVal
);
1596 nVal
= m_pDoc
->GetValue(4,0,0);
1597 ASSERT_DOUBLES_EQUAL( 32.0, nVal
);
1598 nVal
= m_pDoc
->GetValue(5,0,0);
1599 ASSERT_DOUBLES_EQUAL( 34.0, nVal
);
1600 nVal
= m_pDoc
->GetValue(6,0,0);
1601 ASSERT_DOUBLES_EQUAL( 34.0, nVal
);
1604 // The same for sort by column. Start data at A5.
1607 const std::vector
<std::vector
<const char*>> aData
= {
1610 { nullptr, nullptr },
1612 { "=$A$6", "=$B$6" },
1613 { "=SUM(A5:A6)", "=SUM(B5:B6)" },
1614 { "=SUM($A$5:$A$6)", "=SUM($B$5:$B$6)" },
1617 ScAddress
aPos(0,4,0);
1618 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1619 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1622 // Expected output table content. 0 = empty cell
1623 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1626 { nullptr, nullptr },
1633 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1634 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1638 m_pDoc
->SetAnonymousDBData(
1639 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 4, 1, 5)));
1641 ScDBDocFunc
aFunc(*m_xDocShell
);
1643 // Sort A5:B6 by row 5 descending.
1644 ScSortParam aSortData
;
1645 aSortData
.nCol1
= 0;
1646 aSortData
.nCol2
= 1;
1647 aSortData
.nRow1
= 4;
1648 aSortData
.nRow2
= 5;
1649 aSortData
.bHasHeader
= false;
1650 aSortData
.bByRow
= false;
1651 aSortData
.maKeyState
[0].bDoSort
= true;
1652 aSortData
.maKeyState
[0].nField
= 0;
1653 aSortData
.maKeyState
[0].bAscending
= false;
1654 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1655 CPPUNIT_ASSERT(bSorted
);
1658 // Expected output table content. 0 = empty cell
1659 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1662 { nullptr, nullptr },
1669 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1670 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1673 // Make sure that the formulas in A8:B11 are not adjusted.
1674 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=A6"), m_pDoc
->GetFormula(0,7,0));
1675 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=B6"), m_pDoc
->GetFormula(1,7,0));
1676 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=$A$6"), m_pDoc
->GetFormula(0,8,0));
1677 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=$B$6"), m_pDoc
->GetFormula(1,8,0));
1678 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(A5:A6)"), m_pDoc
->GetFormula(0,9,0));
1679 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM(B5:B6)"), m_pDoc
->GetFormula(1,9,0));
1680 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM($A$5:$A$6)"), m_pDoc
->GetFormula(0,10,0));
1681 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=SUM($B$5:$B$6)"), m_pDoc
->GetFormula(1,10,0));
1683 // Enter new value and check that it is broadcasted. First in empty cell.
1684 m_pDoc
->SetString(1,5,0, "16");
1685 double nVal
= m_pDoc
->GetValue(1,7,0);
1686 ASSERT_DOUBLES_EQUAL(nVal
, 16.0);
1687 nVal
= m_pDoc
->GetValue(1,8,0);
1688 ASSERT_DOUBLES_EQUAL(nVal
, 16.0);
1689 nVal
= m_pDoc
->GetValue(1,9,0);
1690 ASSERT_DOUBLES_EQUAL(nVal
, 17.0);
1691 nVal
= m_pDoc
->GetValue(1,10,0);
1692 ASSERT_DOUBLES_EQUAL(nVal
, 17.0);
1694 // Enter new value and check that it is broadcasted. Now overwriting data.
1695 m_pDoc
->SetString(0,5,0, "32");
1696 nVal
= m_pDoc
->GetValue(0,7,0);
1697 ASSERT_DOUBLES_EQUAL(nVal
, 32.0);
1698 nVal
= m_pDoc
->GetValue(0,8,0);
1699 ASSERT_DOUBLES_EQUAL(nVal
, 32.0);
1700 nVal
= m_pDoc
->GetValue(0,9,0);
1701 ASSERT_DOUBLES_EQUAL(nVal
, 34.0);
1702 nVal
= m_pDoc
->GetValue(0,10,0);
1703 ASSERT_DOUBLES_EQUAL(nVal
, 34.0);
1706 m_pDoc
->DeleteTab(0);
1709 // tdf#99417 check that formulas are tracked that *only* indirectly depend on
1710 // sorted data and no other broadcasting than BroadcastBroadcasters is
1711 // involved (for which this test can not be included in testSortBroadcaster()).
1712 void TestSort::testSortBroadcastBroadcaster()
1714 SortRefNoUpdateSetter aUpdateSet
;
1716 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1717 m_pDoc
->InsertTab(0, "Sort");
1720 const std::vector
<std::vector
<const char*>> aData
= {
1721 { "1", "=A1", "=B1" },
1722 { "2", "=A2", "=B2" },
1725 ScAddress
aPos(0,0,0);
1726 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1727 CPPUNIT_ASSERT_EQUAL(aPos
, aDataRange
.aStart
);
1730 // Expected output table content. 0 = empty cell
1731 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1736 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1737 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1741 m_pDoc
->SetAnonymousDBData(
1742 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 1)));
1744 ScDBDocFunc
aFunc(*m_xDocShell
);
1746 // Sort A1:A2 by column A descending.
1747 ScSortParam aSortData
;
1748 aSortData
.nCol1
= 0;
1749 aSortData
.nCol2
= 0;
1750 aSortData
.nRow1
= 0;
1751 aSortData
.nRow2
= 1;
1752 aSortData
.bHasHeader
= false;
1753 aSortData
.bByRow
= true;
1754 aSortData
.maKeyState
[0].bDoSort
= true;
1755 aSortData
.maKeyState
[0].nField
= 0;
1756 aSortData
.maKeyState
[0].bAscending
= false;
1757 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1758 CPPUNIT_ASSERT(bSorted
);
1761 // Expected output table content. 0 = empty cell
1762 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1767 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Sorted without reference update");
1768 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1771 // Make sure that the formulas in B1:C2 are not adjusted.
1772 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=A1"), m_pDoc
->GetFormula(1,0,0));
1773 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=A2"), m_pDoc
->GetFormula(1,1,0));
1774 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=B1"), m_pDoc
->GetFormula(2,0,0));
1775 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", OUString("=B2"), m_pDoc
->GetFormula(2,1,0));
1778 m_pDoc
->DeleteTab(0);
1781 void TestSort::testSortOutOfPlaceResult()
1783 m_pDoc
->InsertTab(0, "Sort");
1784 m_pDoc
->InsertTab(1, "Result");
1786 const char* pData
[] = {
1793 nullptr // terminator
1796 // source data in A1:A6.
1797 for (SCROW i
= 0; pData
[i
]; ++i
)
1798 m_pDoc
->SetString(ScAddress(0,i
,0), OUString::createFromAscii(pData
[i
]));
1800 // Check the initial values.
1801 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1802 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1803 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1804 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1805 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1806 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1808 ScDBDocFunc
aFunc(*m_xDocShell
);
1810 // Sort A1:A6, and set the result to C2:C7
1811 m_pDoc
->SetAnonymousDBData(
1812 0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 0, 5)));
1814 ScSortParam aSortData
;
1815 aSortData
.nCol1
= 0;
1816 aSortData
.nCol2
= 0;
1817 aSortData
.nRow1
= 0;
1818 aSortData
.nRow2
= 5;
1819 aSortData
.bHasHeader
= true;
1820 aSortData
.bInplace
= false;
1821 aSortData
.nDestTab
= 1;
1822 aSortData
.nDestCol
= 2;
1823 aSortData
.nDestRow
= 1;
1824 aSortData
.maKeyState
[0].bDoSort
= true;
1825 aSortData
.maKeyState
[0].nField
= 0;
1826 aSortData
.maKeyState
[0].bAscending
= true;
1827 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1828 CPPUNIT_ASSERT(bSorted
);
1830 // Source data still intact.
1831 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(0,0,0)));
1832 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1833 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1834 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1835 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1836 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1838 // Sort result in C2:C7 on sheet "Result".
1839 CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc
->GetString(ScAddress(2,1,1)));
1840 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc
->GetValue(ScAddress(2,2,1)));
1841 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc
->GetValue(ScAddress(2,3,1)));
1842 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue(ScAddress(2,4,1)));
1843 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue(ScAddress(2,5,1)));
1844 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc
->GetValue(ScAddress(2,6,1)));
1846 m_pDoc
->DeleteTab(1);
1847 m_pDoc
->DeleteTab(0);
1850 void TestSort::testSortPartialFormulaGroup()
1852 SortRefUpdateSetter aUpdateSet
;
1854 sc::AutoCalcSwitch
aACSwitch(*m_pDoc
, true); // turn auto calc on.
1855 FormulaGrammarSwitch
aFGSwitch(m_pDoc
, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
1857 m_pDoc
->InsertTab(0, "Sort");
1859 // Set up the sheet.
1860 const char* aData
[][2] = {
1862 { "43", "=RC[-1]" },
1863 { "50", "=RC[-1]" },
1865 { "47", "=RC[-1]" },
1866 { "28", "=RC[-1]" },
1867 { nullptr, nullptr } // terminator
1871 for (SCROW i
= 0; aData
[i
][0]; ++i
)
1873 m_pDoc
->SetString(0, i
, 0, OUString::createFromAscii(aData
[i
][0]));
1874 m_pDoc
->SetString(1, i
, 0, OUString::createFromAscii(aData
[i
][1]));
1877 // Check the initial condition.
1878 for (SCROW i
= 1; i
<= 5; ++i
)
1879 // A2:A6 should equal B2:B6.
1880 CPPUNIT_ASSERT_EQUAL(m_pDoc
->GetValue(ScAddress(0,i
,0)), m_pDoc
->GetValue(ScAddress(1,i
,0)));
1882 const ScFormulaCell
* pFC
= m_pDoc
->GetFormulaCell(ScAddress(1,1,0));
1883 CPPUNIT_ASSERT(pFC
);
1884 CPPUNIT_ASSERT_MESSAGE("This formula cell should be the first in a group.", pFC
->IsSharedTop());
1885 CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect formula group length.", static_cast<SCROW
>(5), pFC
->GetSharedLength());
1887 ScDBDocFunc
aFunc(*m_xDocShell
);
1889 // Sort only B2:B4. This caused crash at one point (c.f. fdo#81617).
1891 m_pDoc
->SetAnonymousDBData(0, std::unique_ptr
<ScDBData
>(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 1, 1, 1, 3)));
1893 ScSortParam aSortData
;
1894 aSortData
.nCol1
= 1;
1895 aSortData
.nCol2
= 1;
1896 aSortData
.nRow1
= 1;
1897 aSortData
.nRow2
= 3;
1898 aSortData
.bHasHeader
= false;
1899 aSortData
.bInplace
= true;
1900 aSortData
.maKeyState
[0].bDoSort
= true;
1901 aSortData
.maKeyState
[0].nField
= 0;
1902 aSortData
.maKeyState
[0].bAscending
= true;
1903 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
1904 CPPUNIT_ASSERT(bSorted
);
1906 m_pDoc
->CalcAll(); // just in case...
1908 // Check the cell values after the partial sort.
1911 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc
->GetValue(ScAddress(0,1,0)));
1912 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc
->GetValue(ScAddress(0,2,0)));
1913 CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc
->GetValue(ScAddress(0,3,0)));
1914 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(0,4,0)));
1915 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(0,5,0)));
1918 CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc
->GetValue(ScAddress(1,1,0)));
1919 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc
->GetValue(ScAddress(1,2,0)));
1920 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc
->GetValue(ScAddress(1,3,0)));
1921 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc
->GetValue(ScAddress(1,4,0)));
1922 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc
->GetValue(ScAddress(1,5,0)));
1924 m_pDoc
->DeleteTab(0);
1927 void TestSort::testSortImages()
1929 m_pDoc
->InsertTab(0, "testSortImages");
1931 // We need a drawing layer in order to create caption objects.
1932 m_pDoc
->InitDrawLayer(m_xDocShell
.get());
1933 ScDrawLayer
* pDrawLayer
= m_pDoc
->GetDrawLayer();
1934 CPPUNIT_ASSERT(pDrawLayer
);
1936 ScAddress
aPos(0,0,0);
1938 const std::vector
<std::vector
<const char*>> aData
= {
1943 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, aData
.size(), 0));
1944 ScRange aDataRange
= insertRangeData(m_pDoc
, aPos
, aData
);
1945 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos
, aDataRange
.aStart
);
1948 // Insert graphic in cell B2.
1949 const tools::Rectangle
aOrigRect(1000, 1000, 1200, 1200);
1950 rtl::Reference
<SdrCircObj
> pObj
= new SdrCircObj(*pDrawLayer
, SdrCircKind::Full
, aOrigRect
);
1951 SdrPage
* pPage
= pDrawLayer
->GetPage(0);
1952 CPPUNIT_ASSERT(pPage
);
1953 pPage
->InsertObject(pObj
.get());
1955 ScDrawLayer::SetCellAnchoredFromPosition(*pObj
, *m_pDoc
, 0, false);
1957 ScAddress
aCellPos(1, 1, 0);
1958 pDrawLayer
->MoveObject(pObj
.get(), aCellPos
);
1960 std::map
<SCROW
, std::vector
<SdrObject
*>> pRowObjects
1961 = pDrawLayer
->GetObjectsAnchoredToRange(aCellPos
.Tab(), aCellPos
.Col(), aCellPos
.Row(), aCellPos
.Row());
1962 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pRowObjects
[aCellPos
.Row()].size());
1964 ScSortParam aSortData
;
1965 aSortData
.nCol1
= 0;
1966 aSortData
.nCol2
= 1;
1967 aSortData
.nRow1
= 0;
1968 aSortData
.nRow2
= 1;
1969 aSortData
.maKeyState
[0].bDoSort
= true;
1970 aSortData
.maKeyState
[0].nField
= 0;
1971 aSortData
.maKeyState
[0].bAscending
= true;
1973 m_pDoc
->Sort(0, aSortData
, false, true, nullptr, nullptr);
1975 double nVal
= m_pDoc
->GetValue(0,0,0);
1976 ASSERT_DOUBLES_EQUAL(nVal
, 1.0);
1978 // check that note is also moved after sorting
1979 aCellPos
= ScAddress(1, 0, 0);
1981 = pDrawLayer
->GetObjectsAnchoredToRange(aCellPos
.Tab(), aCellPos
.Col(), aCellPos
.Row(), aCellPos
.Row());
1982 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pRowObjects
[aCellPos
.Row()].size());
1984 m_pDoc
->DeleteTab(0);
1990 class TestQueryIterator
1991 : public ScQueryCellIteratorBase
< ScQueryCellIteratorAccess::Direct
, ScQueryCellIteratorType::Generic
>
1993 typedef ScQueryCellIteratorBase
< ScQueryCellIteratorAccess::Direct
, ScQueryCellIteratorType::Generic
> Base
;
1995 TestQueryIterator( ScDocument
& rDocument
, ScInterpreterContext
& rContext
, SCTAB nTable
,
1996 const ScQueryParam
& aParam
, bool bMod
)
1997 : Base( rDocument
, rContext
, nTable
, aParam
, bMod
)
2000 using Base::BinarySearch
; // make public
2001 SCROW
GetRow() const { return nRow
; }
2004 ScQueryParam
makeSearchParam( const ScRange
& range
, SCCOL col
, ScQueryOp op
, double value
)
2007 param
.nCol1
= param
.nCol2
= col
;
2008 param
.nRow1
= range
.aStart
.Row();
2009 param
.nRow2
= range
.aEnd
.Row();
2011 ScQueryEntry
& entry
= param
.GetEntry(0);
2012 ScQueryEntry::Item
& item
= entry
.GetQueryItem();
2013 entry
.bDoQuery
= true;
2016 item
.meType
= ScQueryEntry::ByValue
;
2022 void TestSort::testQueryBinarySearch()
2024 m_pDoc
->InsertTab(0, "testQueryBinarySearch");
2026 const ScAddress
formulaAddress( 10, 0, 0 );
2029 SCCOL descendingCol
;
2030 OUString ascendingRangeName
;
2031 OUString descendingRangeName
;
2033 const std::vector
<std::vector
<const char*>> data
= {
2048 ascendingRangeName
= u
"$A$1:$A$" + OUString::number(data
.size());
2049 descendingRangeName
= u
"$B$1:$B$" + OUString::number(data
.size());
2051 ScAddress
pos(0,0,0);
2052 range
= insertRangeData(m_pDoc
, pos
, data
);
2053 CPPUNIT_ASSERT_EQUAL( ScRange( 0, 0, 0, data
[ 0 ].size() - 1, data
.size() - 1, 0 ), range
);
2057 // This should return the last 5.
2058 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(5;" + ascendingRangeName
+ ";1)",
2059 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2060 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue( formulaAddress
));
2062 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS_EQUAL
, 5 );
2063 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2064 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2065 CPPUNIT_ASSERT_EQUAL(SCROW(8), it
.GetRow());
2068 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS
, 5 );
2069 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2070 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2071 CPPUNIT_ASSERT_EQUAL(SCROW(2), it
.GetRow());
2074 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_EQUAL
, 5 );
2075 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2076 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2077 CPPUNIT_ASSERT_EQUAL(SCROW(8), it
.GetRow());
2081 // Descending, this should return the last 5.
2082 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(5;" + descendingRangeName
+ ";-1)",
2083 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2084 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc
->GetValue( formulaAddress
));
2086 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER_EQUAL
, 5 );
2087 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2088 CPPUNIT_ASSERT(it
.BinarySearch( descendingCol
));
2089 CPPUNIT_ASSERT_EQUAL(SCROW(6), it
.GetRow());
2092 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER
, 5 );
2093 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2094 CPPUNIT_ASSERT(it
.BinarySearch( descendingCol
));
2095 CPPUNIT_ASSERT_EQUAL(SCROW(1), it
.GetRow());
2099 // There's no 6, so this should return the last 5.
2100 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(6;" + ascendingRangeName
+ ";1)",
2101 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2102 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc
->GetValue( formulaAddress
));
2104 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS_EQUAL
, 6 );
2105 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2106 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2107 CPPUNIT_ASSERT_EQUAL(SCROW(8), it
.GetRow());
2110 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS
, 6 );
2111 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2112 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2113 CPPUNIT_ASSERT_EQUAL(SCROW(8), it
.GetRow());
2116 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_EQUAL
, 6 );
2117 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2118 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2119 CPPUNIT_ASSERT_EQUAL(SCROW(8), it
.GetRow());
2123 // Descending, there's no 6, so this should return the last 9.
2124 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(6;" + descendingRangeName
+ ";-1)",
2125 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2126 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc
->GetValue( formulaAddress
));
2128 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER_EQUAL
, 6 );
2129 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2130 CPPUNIT_ASSERT(it
.BinarySearch( descendingCol
));
2131 CPPUNIT_ASSERT_EQUAL(SCROW(1), it
.GetRow());
2134 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER
, 6 );
2135 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2136 CPPUNIT_ASSERT(it
.BinarySearch( descendingCol
));
2137 CPPUNIT_ASSERT_EQUAL(SCROW(1), it
.GetRow());
2141 // All values are larger than 0, so there should be no match.
2142 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(0;" + ascendingRangeName
+ ";1)",
2143 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2144 CPPUNIT_ASSERT_EQUAL( FormulaError::NotAvailable
, m_pDoc
->GetErrCode( formulaAddress
));
2146 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS_EQUAL
, 0 );
2147 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2148 CPPUNIT_ASSERT(!it
.BinarySearch( ascendingCol
));
2149 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2152 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS
, 0 );
2153 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2154 CPPUNIT_ASSERT(!it
.BinarySearch( ascendingCol
));
2155 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2158 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_EQUAL
, 0 );
2159 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2160 CPPUNIT_ASSERT(!it
.BinarySearch( ascendingCol
));
2161 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2165 // Descending, all values are larger than 0, so this should return the last item.
2166 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(0;" + descendingRangeName
+ ";-1)",
2167 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2168 CPPUNIT_ASSERT_EQUAL( 11.0, m_pDoc
->GetValue( formulaAddress
));
2170 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER_EQUAL
, 0 );
2171 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2172 CPPUNIT_ASSERT(it
.BinarySearch( descendingCol
));
2173 CPPUNIT_ASSERT_EQUAL(SCROW(10), it
.GetRow());
2176 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER
, 0 );
2177 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2178 CPPUNIT_ASSERT(it
.BinarySearch( descendingCol
));
2179 CPPUNIT_ASSERT_EQUAL(SCROW(10), it
.GetRow());
2183 // All values are smaller than 10, so this should return the last item.
2184 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(10;" + ascendingRangeName
+ ";1)",
2185 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2186 CPPUNIT_ASSERT_EQUAL( 11.0, m_pDoc
->GetValue( formulaAddress
));
2188 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS_EQUAL
, 10 );
2189 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2190 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2191 CPPUNIT_ASSERT_EQUAL(SCROW(10), it
.GetRow());
2194 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_LESS
, 10 );
2195 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2196 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2197 CPPUNIT_ASSERT_EQUAL(SCROW(10), it
.GetRow());
2200 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_EQUAL
, 10 );
2201 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2202 CPPUNIT_ASSERT(it
.BinarySearch( ascendingCol
));
2203 CPPUNIT_ASSERT_EQUAL(SCROW(10), it
.GetRow());
2207 // Descending, all values are smaller than 10, so there should be no match.
2208 m_pDoc
->SetFormula( formulaAddress
, "=MATCH(10;" + descendingRangeName
+ ";-1)",
2209 formula::FormulaGrammar::GRAM_NATIVE_UI
);
2210 CPPUNIT_ASSERT_EQUAL( FormulaError::NotAvailable
, m_pDoc
->GetErrCode( formulaAddress
));
2212 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER_EQUAL
, 10 );
2213 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2214 CPPUNIT_ASSERT(!it
.BinarySearch( descendingCol
));
2215 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2218 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_GREATER
, 10 );
2219 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2220 CPPUNIT_ASSERT(!it
.BinarySearch( descendingCol
));
2221 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2225 // Search as ascending but use descending range, will return no match.
2226 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_LESS_EQUAL
, 1 );
2227 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2228 CPPUNIT_ASSERT(!it
.BinarySearch( descendingCol
));
2229 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2233 // Search as descending but use ascending range, will return no match.
2234 ScQueryParam param
= makeSearchParam( range
, ascendingCol
, SC_GREATER_EQUAL
, 9 );
2235 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2236 CPPUNIT_ASSERT(!it
.BinarySearch( ascendingCol
));
2237 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2241 // SC_EQUAL with descending is considered an error, will return no match.
2242 ScQueryParam param
= makeSearchParam( range
, descendingCol
, SC_EQUAL
, 9 );
2243 TestQueryIterator
it( *m_pDoc
, m_pDoc
->GetNonThreadedContext(), 0, param
, false );
2244 CPPUNIT_ASSERT(!it
.BinarySearch( descendingCol
));
2245 CPPUNIT_ASSERT_EQUAL(SCROW(0), it
.GetRow());
2248 m_pDoc
->DeleteTab(0);
2251 CPPUNIT_TEST_SUITE_REGISTRATION(TestSort
);
2253 CPPUNIT_PLUGIN_IMPLEMENT();
2255 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */