Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / sc / qa / unit / ucalc_sort.cxx
blobf71e0d663a5923c61c85f784bf24c610baf85b9e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "helper/sorthelper.hxx"
11 #include "helper/debughelper.hxx"
12 #include "helper/qahelper.hxx"
14 #include <postit.hxx>
15 #include <sortparam.hxx>
16 #include <dbdata.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
36 public:
37 void testSort();
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 = {
95 { "2", "4" },
96 { "4", "1" },
97 { "1", "2" },
98 { "1", "23" },
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;
113 aSortData.nCol1 = 1;
114 aSortData.nCol2 = 1;
115 aSortData.nRow1 = 0;
116 aSortData.nRow2 = 2;
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);
130 ScRange aDataRange;
131 clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
133 // 0 = empty cell
134 const std::vector<std::vector<const char*>> aData = {
135 { "Title" },
136 { nullptr },
137 { nullptr },
138 { "12" },
139 { "b" },
140 { "1" },
141 { "9" },
142 { "123" }
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
159 // end.
160 CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos));
161 aPos.IncRow();
162 CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos));
163 aPos.IncRow();
164 CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos));
165 aPos.IncRow();
166 CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos));
167 aPos.IncRow();
168 CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos));
169 aPos.IncRow();
170 CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos));
171 aPos.IncRow();
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.
186 // 0 = empty cell
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;
211 aSortData.nCol1 = 0;
212 aSortData.nCol2 = 3;
213 aSortData.nRow1 = 0;
214 aSortData.nRow2 = 3;
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");
249 // 0 = empty cell
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)));
298 // Undo and check.
300 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
301 CPPUNIT_ASSERT(pUndoMgr);
303 pUndoMgr->Undo();
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)));
310 // Redo and check.
311 pUndoMgr->Redo();
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;
339 aSortData.nCol1 = 0;
340 aSortData.nCol2 = 1;
341 aSortData.nRow1 = 0;
342 aSortData.nRow2 = 0;
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);
357 // A1:G1
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.
371 aSortData.nCol1 = 0;
372 aSortData.nCol2 = 6;
373 aSortData.bByRow = false;
374 bSorted = aFunc.Sort(0, aSortData, true, true, true);
375 CPPUNIT_ASSERT(bSorted);
377 // Check the result.
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)));
386 // Undo and check.
387 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
388 CPPUNIT_ASSERT(pUndoMgr);
389 pUndoMgr->Undo();
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)));
399 // Redo and check.
400 pUndoMgr->Redo();
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] = {
431 "bob",
432 "tim",
433 "brian",
434 "larry",
437 const char* aResults[6] = {
438 "bob",
439 "brian",
440 "larry",
441 "tim",
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;
456 aSortData.nCol1 = 0;
457 aSortData.nCol2 = 0;
458 aSortData.nRow1 = 1;
459 aSortData.nRow2 = 7;
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)));
488 ScSortParam aParam;
489 aParam.nCol1 = 1;
490 aParam.nCol2 = 1;
491 aParam.nRow1 = 1;
492 aParam.nRow2 = 3;
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 {
520 SCCOL nCol;
521 SCROW nRow;
522 const char *pData;
523 } aEntries[] = {
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;
543 aSortData.nCol1 = 0;
544 aSortData.nCol2 = 1;
545 aSortData.nRow1 = 0;
546 aSortData.nRow2 = 9;
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[] = {
554 1.0, 1.0,
555 2.0, 3.0,
556 3.0, 3.0,
557 10.0, 11.0,
558 20.0, 20.0,
559 100.0, 100.0,
560 101.0, 101.0,
561 102.0, 102.0,
562 103.0, 103.0,
563 104.0, 104.0
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()
578 struct
580 bool isBold( const ScPatternAttr* pPat ) const
582 if (!pPat)
584 cerr << "Pattern is NULL!" << endl;
585 return false;
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;
592 return false;
595 CPPUNIT_ASSERT(pItem);
597 if (static_cast<const SvxWeightItem*>(pItem)->GetEnumValue() != WEIGHT_BOLD)
599 cerr << "Font weight should be bold." << endl;
600 return false;
603 return true;
606 bool isItalic( const ScPatternAttr* pPat ) const
608 if (!pPat)
610 cerr << "Pattern is NULL!" << endl;
611 return false;
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;
618 return false;
621 CPPUNIT_ASSERT(pItem);
623 if (static_cast<const SvxPostureItem*>(pItem)->GetEnumValue() != ITALIC_NORMAL)
625 cerr << "Italic should be applied.." << endl;
626 return false;
629 return true;
632 bool isNormal( const ScPatternAttr* pPat ) const
634 if (!pPat)
636 cerr << "Pattern is NULL!" << endl;
637 return false;
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;
647 return false;
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;
657 return false;
661 return true;
664 } aCheck;
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;
708 aSortData.nCol1 = 0;
709 aSortData.nCol2 = 0;
710 aSortData.nRow1 = 0;
711 aSortData.nRow2 = 3;
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;
778 aSortData.nCol1 = 0;
779 aSortData.nCol2 = 0;
780 aSortData.nRow1 = 0;
781 aSortData.nRow2 = 9;
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));
810 // Undo the sort.
811 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
812 pUndoMgr->Undo();
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));
833 // Redo.
834 pUndoMgr->Redo();
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));
854 // Undo again.
855 pUndoMgr->Undo();
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");
899 // Set up the sheet.
900 const char* aData[][2] = {
901 { "F1", "F2" },
902 { "9", "=RC[-1]" },
903 { "2", "=RC[-1]" },
904 { "6", "=RC[-1]" },
905 { "4", "=RC[-1]" },
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;
929 aSortData.nCol1 = 0;
930 aSortData.nCol2 = 1;
931 aSortData.nRow1 = 0;
932 aSortData.nRow2 = 4;
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)));
946 // and column B.
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();
960 pUndoMgr->Undo();
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)));
967 // and redo.
968 pUndoMgr->Redo();
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[] = {
986 "Header",
987 "1",
988 "=A2+10",
989 "2",
990 "=A4+10",
991 "=A2+A4",
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);
1008 // Sort A1:A6.
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.
1026 m_pDoc->CalcAll();
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();
1041 pUndoMgr->Undo();
1042 m_pDoc->CalcAll();
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.
1051 pUndoMgr->Redo();
1052 m_pDoc->CalcAll();
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 = {
1087 { "Name", "Note" },
1088 { "Student1", "1" },
1089 { "Student2", "2" },
1090 { "Student3", "3" },
1091 { "Student4", "4" },
1092 { "Student5", "5" },
1095 SCTAB nTab = 1;
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 = {
1104 { "Name", "Note" },
1105 { "=Lesson1.A2", "3" },
1106 { "=Lesson1.A3", "4" },
1107 { "=Lesson1.A4", "9" },
1108 { "=Lesson1.A5", "6" },
1109 { "=Lesson1.A6", "3" },
1112 SCTAB nTab = 2;
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);
1119 ScRange aSortRange;
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)" },
1130 SCTAB nTab = 0;
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.
1158 m_pDoc->CalcAll();
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();
1190 pUndoMgr->Undo();
1191 m_pDoc->CalcAll();
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.
1200 pUndoMgr->Redo();
1201 m_pDoc->CalcAll();
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.
1229 m_pDoc->CalcAll();
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.
1280 { 0, 4, 0 },
1281 { 0, 1, 0 },
1282 { 0, 3, 0 },
1283 { 0, 2, 0 },
1285 ScRange aSortRange;
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" },
1295 SCTAB nTab = 0;
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);
1312 // Sort A1:B5.
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.
1330 m_pDoc->CalcAll();
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[] = {
1340 // Volatile
1341 "=TODAY()-$A2",
1342 "=TODAY()-$A3",
1343 "=TODAY()-$A4",
1344 "=TODAY()-$A5",
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();
1353 pUndoMgr->Undo();
1354 m_pDoc->CalcAll();
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.
1362 pUndoMgr->Redo();
1363 m_pDoc->CalcAll();
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" },
1396 { "9", "1", "2" },
1397 { "1", "2", "4" },
1398 { "8", "3", "7" },
1401 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1402 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1405 ScDBDocFunc aFunc(*m_xDocShell);
1407 // Sort A1:C4.
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" },
1428 { "1", "2", "3" },
1429 { "8", "3", "6" },
1430 { "9", "1", "7" },
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));
1442 // Undo and check.
1443 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1444 CPPUNIT_ASSERT(pUndoMgr);
1446 pUndoMgr->Undo();
1449 // Expected output table content. 0 = empty cell
1450 std::vector<std::vector<const char*>> aOutputCheck = {
1451 { "Order", "Value", "1" },
1452 { "9", "1", "2" },
1453 { "1", "2", "4" },
1454 { "8", "3", "7" },
1457 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo");
1458 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1461 // Redo and check.
1462 pUndoMgr->Redo();
1464 // Expected output table content. 0 = empty cell
1465 std::vector<std::vector<const char*>> aOutputCheck = {
1466 { "Order", "Value", "1" },
1467 { "1", "2", "3" },
1468 { "8", "3", "6" },
1469 { "9", "1", "7" },
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" },
1483 { "1", "2", "13" },
1484 { "8", "3", "16" },
1485 { "9", "1", "17" },
1488 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Change the header value");
1489 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1492 // Undo and check.
1493 pUndoMgr->Undo();
1495 // Expected output table content. 0 = empty cell
1496 std::vector<std::vector<const char*>> aOutputCheck = {
1497 { "Order", "Value", "1" },
1498 { "1", "2", "3" },
1499 { "8", "3", "6" },
1500 { "9", "1", "7" },
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
1511 // broadcasted.
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);
1540 // Sort A1:B2.
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 = {
1608 { "1", "2" },
1609 { nullptr, "8" },
1610 { nullptr, nullptr },
1611 { "=A6", "=B6" },
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 = {
1624 { "1", "2" },
1625 { nullptr, "8" },
1626 { nullptr, nullptr },
1627 { "0", "8" },
1628 { "0", "8" },
1629 { "1", "10" },
1630 { "1", "10" },
1633 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1634 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1637 // Sort A5:B6.
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 = {
1660 { "2", "1" },
1661 { "8", nullptr },
1662 { nullptr, nullptr },
1663 { "8", "0" },
1664 { "8", "0" },
1665 { "10", "1" },
1666 { "10", "1" },
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 = {
1732 { "1", "1", "1" },
1733 { "2", "2", "2" },
1736 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1737 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1740 // Sort A1:A2.
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 = {
1763 { "2", "2", "2" },
1764 { "1", "1", "1" },
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[] = {
1787 "Header",
1788 "1",
1789 "23",
1790 "2",
1791 "9",
1792 "-2",
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] = {
1861 { "F1", "F2" },
1862 { "43", "=RC[-1]" },
1863 { "50", "=RC[-1]" },
1864 { "8", "=RC[-1]" },
1865 { "47", "=RC[-1]" },
1866 { "28", "=RC[-1]" },
1867 { nullptr, nullptr } // terminator
1870 // A1:B6.
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.
1910 // Column A
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)));
1917 // Column B
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 = {
1939 { "2" },
1940 { "1" },
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());
1954 // Anchor to cell
1955 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
1956 // Move to cell B2
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);
1980 pRowObjects
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);
1987 namespace
1990 class TestQueryIterator
1991 : public ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::Generic >
1993 typedef ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::Generic > Base;
1994 public:
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 )
2006 ScQueryParam param;
2007 param.nCol1 = param.nCol2 = col;
2008 param.nRow1 = range.aStart.Row();
2009 param.nRow2 = range.aEnd.Row();
2010 param.nTab = 0;
2011 ScQueryEntry& entry = param.GetEntry(0);
2012 ScQueryEntry::Item& item = entry.GetQueryItem();
2013 entry.bDoQuery = true;
2014 entry.eOp = op;
2015 item.mfVal = value;
2016 item.meType = ScQueryEntry::ByValue;
2017 return param;
2020 } // namespace
2022 void TestSort::testQueryBinarySearch()
2024 m_pDoc->InsertTab(0, "testQueryBinarySearch");
2026 const ScAddress formulaAddress( 10, 0, 0 );
2027 ScRange range;
2028 SCCOL ascendingCol;
2029 SCCOL descendingCol;
2030 OUString ascendingRangeName;
2031 OUString descendingRangeName;
2033 const std::vector<std::vector<const char*>> data = {
2034 { "1", "9" }, // 0
2035 { "2", "9" }, // 1
2036 { "4", "5" }, // 2
2037 { "5", "5" }, // 3
2038 { "5", "5" }, // 4
2039 { "5", "5" }, // 5
2040 { "5", "5" }, // 6
2041 { "5", "4" }, // 7
2042 { "5", "4" }, // 8
2043 { "9", "2" }, // 9
2044 { "9", "1" }, // 10
2046 ascendingCol = 0;
2047 descendingCol = 1;
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: */