Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / qa / unit / ucalc_sort.cxx
blobb1178bc2930390b6c7e80f79eec179976b8ef218
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 using ::std::cerr;
35 using ::std::endl;
37 class TestSort : public ScUcalcTestBase
39 protected:
40 void testSortRefUpdate4_Impl();
43 CPPUNIT_TEST_FIXTURE(TestSort, testSort)
45 m_pDoc->InsertTab(0, u"test1"_ustr);
47 // We need a drawing layer in order to create caption objects.
48 m_pDoc->InitDrawLayer(m_xDocShell.get());
50 ScAddress aPos(0,0,0);
52 const std::vector<std::vector<const char*>> aData = {
53 { "2", "4" },
54 { "4", "1" },
55 { "1", "2" },
56 { "1", "23" },
59 clearRange(m_pDoc, ScRange(0, 0, 0, 1, aData.size(), 0));
60 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
61 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
64 // Insert note in cell B2.
65 ScAddress rAddr(1, 1, 0);
66 ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr);
67 pNote->SetText(rAddr, u"Hello"_ustr);
68 pNote->SetAuthor(u"Jim Bob"_ustr);
70 ScSortParam aSortData;
71 aSortData.nCol1 = 1;
72 aSortData.nCol2 = 1;
73 aSortData.nRow1 = 0;
74 aSortData.nRow2 = 2;
75 aSortData.maKeyState[0].bDoSort = true;
76 aSortData.maKeyState[0].nField = 1;
77 aSortData.maKeyState[0].bAscending = true;
78 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
80 m_pDoc->Sort(0, aSortData, false, true, nullptr, nullptr);
82 double nVal = m_pDoc->GetValue(1,0,0);
83 ASSERT_DOUBLES_EQUAL(1.0, nVal);
85 // check that note is also moved after sorting
86 pNote = m_pDoc->GetNote(1, 0, 0);
87 CPPUNIT_ASSERT(pNote);
89 ScRange aDataRange;
90 clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
92 // 0 = empty cell
93 const std::vector<std::vector<const char*>> aData = {
94 { "Title" },
95 { nullptr },
96 { nullptr },
97 { "12" },
98 { "b" },
99 { "1" },
100 { "9" },
101 { "123" }
104 aDataRange = insertRangeData(m_pDoc, aPos, aData);
105 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
108 aSortData.nCol1 = aDataRange.aStart.Col();
109 aSortData.nCol2 = aDataRange.aEnd.Col();
110 aSortData.nRow1 = aDataRange.aStart.Row();
111 aSortData.nRow2 = aDataRange.aEnd.Row();
112 aSortData.bHasHeader = true;
113 aSortData.maKeyState[0].nField = 0;
114 m_pDoc->Sort(0, aSortData, false, true, nullptr, nullptr);
116 // Title should stay at the top, numbers should be sorted numerically,
117 // numbers always come before strings, and empty cells always occur at the
118 // end.
119 CPPUNIT_ASSERT_EQUAL(u"Title"_ustr, m_pDoc->GetString(aPos));
120 aPos.IncRow();
121 CPPUNIT_ASSERT_EQUAL(u"1"_ustr, m_pDoc->GetString(aPos));
122 aPos.IncRow();
123 CPPUNIT_ASSERT_EQUAL(u"9"_ustr, m_pDoc->GetString(aPos));
124 aPos.IncRow();
125 CPPUNIT_ASSERT_EQUAL(u"12"_ustr, m_pDoc->GetString(aPos));
126 aPos.IncRow();
127 CPPUNIT_ASSERT_EQUAL(u"123"_ustr, m_pDoc->GetString(aPos));
128 aPos.IncRow();
129 CPPUNIT_ASSERT_EQUAL(u"b"_ustr, m_pDoc->GetString(aPos));
130 aPos.IncRow();
131 CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos));
133 m_pDoc->DeleteTab(0);
136 CPPUNIT_TEST_FIXTURE(TestSort, testSortHorizontal)
138 SortRefUpdateSetter aUpdateSet;
140 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
141 m_pDoc->InsertTab(0, u"Sort"_ustr);
143 // Test case from fdo#78079.
145 // 0 = empty cell
146 const std::vector<std::vector<const char*>> aData = {
147 { "table", "has UNIQUE", "Publish to EC2", "flag" },
148 { "w2gi.mobilehit", "Yes", "No", "=CONCATENATE(B2;\"-\";C2)" },
149 { "w2gi.visitors", "No", "No", "=CONCATENATE(B3;\"-\";C3)" },
150 { "w2gi.pagedimension", "Yes", "Yes", "=CONCATENATE(B4;\"-\";C4)" },
153 // Insert raw data into A1:D4.
154 ScRange aDataRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
155 CPPUNIT_ASSERT_EQUAL(u"A1:D4"_ustr, aDataRange.Format(*m_pDoc, ScRefFlags::VALID));
157 // Check the formula values.
158 CPPUNIT_ASSERT_EQUAL(u"Yes-No"_ustr, m_pDoc->GetString(ScAddress(3,1,0)));
159 CPPUNIT_ASSERT_EQUAL(u"No-No"_ustr, m_pDoc->GetString(ScAddress(3,2,0)));
160 CPPUNIT_ASSERT_EQUAL(u"Yes-Yes"_ustr, m_pDoc->GetString(ScAddress(3,3,0)));
162 // Define A1:D4 as sheet-local anonymous database range.
163 m_pDoc->SetAnonymousDBData(
164 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 3, 3)));
166 // Sort A1:D4 horizontally, ascending by row 1.
167 ScDBDocFunc aFunc(*m_xDocShell);
169 ScSortParam aSortData;
170 aSortData.nCol1 = 0;
171 aSortData.nCol2 = 3;
172 aSortData.nRow1 = 0;
173 aSortData.nRow2 = 3;
174 aSortData.bHasHeader = true;
175 aSortData.bByRow = false; // Sort by column (in horizontal direction).
176 aSortData.aDataAreaExtras.mbCellFormats = true;
177 aSortData.maKeyState[0].bDoSort = true;
178 aSortData.maKeyState[0].nField = 0;
179 aSortData.maKeyState[0].bAscending = true;
180 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
181 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
182 CPPUNIT_ASSERT(bSorted);
185 // Expected output table content. 0 = empty cell
186 std::vector<std::vector<const char*>> aOutputCheck = {
187 { "table", "flag", "has UNIQUE", "Publish to EC2" },
188 { "w2gi.mobilehit", "Yes-No", "Yes", "No" },
189 { "w2gi.visitors", "No-No", "No", "No" },
190 { "w2gi.pagedimension", "Yes-Yes", "Yes", "Yes" },
193 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Sorted by column with formula");
194 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
197 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=CONCATENATE(C2;\"-\";D2)"_ustr, m_pDoc->GetFormula(1,1,0));
198 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=CONCATENATE(C3;\"-\";D3)"_ustr, m_pDoc->GetFormula(1,2,0));
199 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=CONCATENATE(C4;\"-\";D4)"_ustr, m_pDoc->GetFormula(1,3,0));
201 m_pDoc->DeleteTab(0);
204 CPPUNIT_TEST_FIXTURE(TestSort, testSortHorizontalWholeColumn)
206 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
207 m_pDoc->InsertTab(0, u"Sort"_ustr);
209 // 0 = empty cell
210 const std::vector<std::vector<const char*>> aData = {
211 { "4", "2", "47", "a", "9" }
214 // Insert row data to C1:G1.
215 ScRange aSortRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData);
216 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,0,0)));
217 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,0,0)));
218 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(4,0,0)));
219 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, m_pDoc->GetString(ScAddress(5,0,0)));
220 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(6,0,0)));
222 // Extend the sort range to whole column.
223 aSortRange.aEnd.SetRow(m_pDoc->MaxRow());
225 SCCOL nCol1 = aSortRange.aStart.Col();
226 SCCOL nCol2 = aSortRange.aEnd.Col();
227 SCROW nRow1 = aSortRange.aStart.Row();
228 SCROW nRow2 = aSortRange.aEnd.Row();
230 // Define C:G as sheet-local anonymous database range.
231 m_pDoc->SetAnonymousDBData(
232 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, nCol1, nRow1, nCol2, nRow2, false, false)));
234 // Sort C:G horizontally ascending by row 1.
235 ScDBDocFunc aFunc(*m_xDocShell);
237 ScSortParam aSortData;
238 aSortData.nCol1 = nCol1;
239 aSortData.nCol2 = nCol2;
240 aSortData.nRow1 = nRow1;
241 aSortData.nRow2 = nRow2;
242 aSortData.bHasHeader = false;
243 aSortData.bByRow = false; // Sort by column (in horizontal direction).
244 aSortData.aDataAreaExtras.mbCellFormats = true;
245 aSortData.maKeyState[0].bDoSort = true;
246 aSortData.maKeyState[0].nField = 0;
247 aSortData.maKeyState[0].bAscending = true;
248 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
249 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
250 CPPUNIT_ASSERT(bSorted);
252 // Check the sort result.
253 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
254 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(3,0,0)));
255 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(4,0,0)));
256 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(5,0,0)));
257 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, m_pDoc->GetString(ScAddress(6,0,0)));
259 // Undo and check.
261 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
262 CPPUNIT_ASSERT(pUndoMgr);
264 pUndoMgr->Undo();
265 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,0,0)));
266 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,0,0)));
267 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(4,0,0)));
268 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, m_pDoc->GetString(ScAddress(5,0,0)));
269 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(6,0,0)));
271 // Redo and check.
272 pUndoMgr->Redo();
273 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
274 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(3,0,0)));
275 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(4,0,0)));
276 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(5,0,0)));
277 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, m_pDoc->GetString(ScAddress(6,0,0)));
279 m_pDoc->DeleteTab(0);
282 CPPUNIT_TEST_FIXTURE(TestSort, testSortSingleRow)
284 // This test case is from fdo#80462.
286 m_pDoc->InsertTab(0, u"Test"_ustr);
288 // Sort range consists of only one row.
289 m_pDoc->SetString(ScAddress(0,0,0), u"X"_ustr);
290 m_pDoc->SetString(ScAddress(1,0,0), u"Y"_ustr);
292 // Define A1:B1 as sheet-local anonymous database range.
293 m_pDoc->SetAnonymousDBData(
294 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 1, 0)));
296 // Sort A1:B1 horizontally, ascending by row 1.
297 ScDBDocFunc aFunc(*m_xDocShell);
299 ScSortParam aSortData;
300 aSortData.nCol1 = 0;
301 aSortData.nCol2 = 1;
302 aSortData.nRow1 = 0;
303 aSortData.nRow2 = 0;
304 aSortData.bHasHeader = true;
305 aSortData.bByRow = true;
306 aSortData.aDataAreaExtras.mbCellFormats = true;
307 aSortData.maKeyState[0].bDoSort = true;
308 aSortData.maKeyState[0].nField = 0;
309 aSortData.maKeyState[0].bAscending = true;
310 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
312 // Do the sorting. This should not crash.
313 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
314 CPPUNIT_ASSERT(bSorted);
316 // Another test case - single row horizontal sort with header column.
317 clearSheet(m_pDoc, 0);
319 // A1:G1
320 m_pDoc->SetString(ScAddress(0,0,0), u"Header"_ustr);
321 m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
322 m_pDoc->SetValue(ScAddress(2,0,0), 10.0);
323 m_pDoc->SetValue(ScAddress(3,0,0), 3.0);
324 m_pDoc->SetValue(ScAddress(4,0,0), 9.0);
325 m_pDoc->SetValue(ScAddress(5,0,0), 12.0);
326 m_pDoc->SetValue(ScAddress(6,0,0), 2.0);
328 // Define A1:G1 as sheet-local anonymous database range.
329 m_pDoc->SetAnonymousDBData(
330 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 6, 0, false, true)));
332 // Update the sort data.
333 aSortData.nCol1 = 0;
334 aSortData.nCol2 = 6;
335 aSortData.bByRow = false;
336 bSorted = aFunc.Sort(0, aSortData, true, true, true);
337 CPPUNIT_ASSERT(bSorted);
339 // Check the result.
340 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
341 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
342 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
343 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,0,0)));
344 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(4,0,0)));
345 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(5,0,0)));
346 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(6,0,0)));
348 // Undo and check.
349 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
350 CPPUNIT_ASSERT(pUndoMgr);
351 pUndoMgr->Undo();
353 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
354 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
355 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
356 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,0,0)));
357 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(4,0,0)));
358 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(5,0,0)));
359 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(6,0,0)));
361 // Redo and check.
362 pUndoMgr->Redo();
363 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
364 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
365 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
366 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,0,0)));
367 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(4,0,0)));
368 CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(5,0,0)));
369 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(6,0,0)));
371 m_pDoc->DeleteTab(0);
374 // regression test of fdo#53814, sorting doesn't work as expected
375 // if cells in the sort are referenced by formulas
376 CPPUNIT_TEST_FIXTURE(TestSort, testSortWithFormulaRefs)
378 SortRefUpdateSetter aUpdateSet;
380 m_pDoc->InsertTab(0, u"List1"_ustr);
381 m_pDoc->InsertTab(1, u"List2"_ustr);
383 const char* aFormulaData[6] = {
384 "=IF($List1.A2<>\"\";$List1.A2;\"\")",
385 "=IF($List1.A3<>\"\";$List1.A3;\"\")",
386 "=IF($List1.A4<>\"\";$List1.A4;\"\")",
387 "=IF($List1.A5<>\"\";$List1.A5;\"\")",
388 "=IF($List1.A6<>\"\";$List1.A6;\"\")",
389 "=IF($List1.A7<>\"\";$List1.A7;\"\")",
392 const char* const aTextData[4] = {
393 "bob",
394 "tim",
395 "brian",
396 "larry",
399 const char* aResults[6] = {
400 "bob",
401 "brian",
402 "larry",
403 "tim",
408 // Insert data to sort in A2:A5 on the 1st sheet.
409 for (SCROW i = 1; i <= 4; ++i)
410 m_pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
412 // Insert formulas in A1:A6 on the 2nd sheet.
413 for (size_t i = 0; i < std::size(aFormulaData); ++i)
414 m_pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
416 // Sort data in A2:A8 on the 1st sheet. No column header.
417 ScSortParam aSortData;
418 aSortData.nCol1 = 0;
419 aSortData.nCol2 = 0;
420 aSortData.nRow1 = 1;
421 aSortData.nRow2 = 7;
422 aSortData.maKeyState[0].bDoSort = true;
423 aSortData.maKeyState[0].nField = 0;
424 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
426 m_pDoc->Sort(0, aSortData, false, true, nullptr, nullptr);
428 for (size_t i = 0; i < std::size(aResults); ++i)
430 OUString sResult = m_pDoc->GetString(0, i + 1, 0);
431 CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[i] ), sResult );
433 m_pDoc->DeleteTab(1);
434 m_pDoc->DeleteTab(0);
437 CPPUNIT_TEST_FIXTURE(TestSort, testSortWithStrings)
439 m_pDoc->InsertTab(0, u"Test"_ustr);
441 ScFieldEditEngine& rEE = m_pDoc->GetEditEngine();
442 rEE.SetTextCurrentDefaults(u"Val1"_ustr);
443 m_pDoc->SetString(ScAddress(1,1,0), u"Header"_ustr);
444 m_pDoc->SetString(ScAddress(1,2,0), u"Val2"_ustr);
445 m_pDoc->SetEditText(ScAddress(1,3,0), rEE.CreateTextObject());
447 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(1,1,0)));
448 CPPUNIT_ASSERT_EQUAL(u"Val2"_ustr, m_pDoc->GetString(ScAddress(1,2,0)));
449 CPPUNIT_ASSERT_EQUAL(u"Val1"_ustr, m_pDoc->GetString(ScAddress(1,3,0)));
451 ScSortParam aParam;
452 aParam.nCol1 = 1;
453 aParam.nCol2 = 1;
454 aParam.nRow1 = 1;
455 aParam.nRow2 = 3;
456 aParam.bHasHeader = true;
457 aParam.maKeyState[0].bDoSort = true;
458 aParam.maKeyState[0].bAscending = true;
459 aParam.maKeyState[0].nField = 1;
460 aParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;
462 m_pDoc->Sort(0, aParam, false, true, nullptr, nullptr);
464 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(1,1,0)));
465 CPPUNIT_ASSERT_EQUAL(u"Val1"_ustr, m_pDoc->GetString(ScAddress(1,2,0)));
466 CPPUNIT_ASSERT_EQUAL(u"Val2"_ustr, m_pDoc->GetString(ScAddress(1,3,0)));
468 aParam.maKeyState[0].bAscending = false;
470 m_pDoc->Sort(0, aParam, false, true, nullptr, nullptr);
472 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(1,1,0)));
473 CPPUNIT_ASSERT_EQUAL(u"Val2"_ustr, m_pDoc->GetString(ScAddress(1,2,0)));
474 CPPUNIT_ASSERT_EQUAL(u"Val1"_ustr, m_pDoc->GetString(ScAddress(1,3,0)));
476 m_pDoc->DeleteTab(0);
479 CPPUNIT_TEST_FIXTURE(TestSort, testSortInFormulaGroup)
481 SortRefUpdateSetter aUpdateSet;
483 static const struct {
484 SCCOL nCol;
485 SCROW nRow;
486 const char *pData;
487 } aEntries[] = {
488 { 0, 0, "3" }, { 1, 0, "=A1" },
489 { 0, 1, "1" }, { 1, 1, "=A2" },
490 { 0, 2, "20" }, { 1, 2, "=A3" },
491 { 0, 3, "10" }, { 1, 3, "=A4+1" }, // swap across groups
492 { 0, 4, "2" }, { 1, 4, "=A5+1" },
493 { 0, 5, "101" }, { 1, 5, "=A6" }, // swap inside contiguous group
494 { 0, 6, "100" }, { 1, 6, "=A7" },
495 { 0, 7, "102" }, { 1, 7, "=A8" },
496 { 0, 8, "104" }, { 1, 8, "=A9" },
497 { 0, 9, "103" }, { 1, 9, "=A10" },
500 m_pDoc->InsertTab(0, u"sorttest"_ustr);
502 for ( auto const & i: aEntries )
503 m_pDoc->SetString( i.nCol, i.nRow, 0,
504 OUString::createFromAscii( i.pData) );
506 ScSortParam aSortData;
507 aSortData.nCol1 = 0;
508 aSortData.nCol2 = 1;
509 aSortData.nRow1 = 0;
510 aSortData.nRow2 = 9;
511 aSortData.maKeyState[0].bDoSort = true;
512 aSortData.maKeyState[0].nField = 0;
513 aSortData.maKeyState[0].bAscending = true;
514 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
516 m_pDoc->Sort(0, aSortData, false, true, nullptr, nullptr);
518 static const double aResults[] = {
519 1.0, 1.0,
520 2.0, 3.0,
521 3.0, 3.0,
522 10.0, 11.0,
523 20.0, 20.0,
524 100.0, 100.0,
525 101.0, 101.0,
526 102.0, 102.0,
527 103.0, 103.0,
528 104.0, 104.0
531 for ( SCROW i = 0; i < SCROW(std::size( aEntries )); ++i )
533 double val = m_pDoc->GetValue( aEntries[i].nCol, aEntries[i].nRow, 0 );
534 CPPUNIT_ASSERT_MESSAGE("Mis-matching value after sort.",
535 rtl::math::approxEqual(val, aResults[i]));
538 m_pDoc->DeleteTab( 0 );
541 CPPUNIT_TEST_FIXTURE(TestSort, testSortWithCellFormats)
543 struct
545 bool isBold( const ScPatternAttr* pPat ) const
547 if (!pPat)
549 cerr << "Pattern is NULL!" << endl;
550 return false;
553 const SfxPoolItem* pItem = nullptr;
554 if (!pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT, &pItem))
556 cerr << "Pattern does not have a font weight item, but it should." << endl;
557 return false;
560 CPPUNIT_ASSERT(pItem);
562 if (static_cast<const SvxWeightItem*>(pItem)->GetEnumValue() != WEIGHT_BOLD)
564 cerr << "Font weight should be bold." << endl;
565 return false;
568 return true;
571 bool isItalic( const ScPatternAttr* pPat ) const
573 if (!pPat)
575 cerr << "Pattern is NULL!" << endl;
576 return false;
579 const SfxPoolItem* pItem = nullptr;
580 if (!pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE, &pItem))
582 cerr << "Pattern does not have a font posture item, but it should." << endl;
583 return false;
586 CPPUNIT_ASSERT(pItem);
588 if (static_cast<const SvxPostureItem*>(pItem)->GetEnumValue() != ITALIC_NORMAL)
590 cerr << "Italic should be applied.." << endl;
591 return false;
594 return true;
597 bool isNormal( const ScPatternAttr* pPat ) const
599 if (!pPat)
601 cerr << "Pattern is NULL!" << endl;
602 return false;
605 const SfxPoolItem* pItem = nullptr;
606 if (pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT, &pItem))
608 // Check if the font weight is applied.
609 if (static_cast<const SvxWeightItem*>(pItem)->GetEnumValue() == WEIGHT_BOLD)
611 cerr << "This cell is bold, but shouldn't." << endl;
612 return false;
616 if (pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE, &pItem))
618 // Check if the italics is applied.
619 if (static_cast<const SvxPostureItem*>(pItem)->GetEnumValue() == ITALIC_NORMAL)
621 cerr << "This cell is italic, but shouldn't." << endl;
622 return false;
626 return true;
629 } aCheck;
631 m_pDoc->InsertTab(0, u"Test"_ustr);
633 // Insert some values into A1:A4.
634 m_pDoc->SetString(ScAddress(0,0,0), u"Header"_ustr);
635 m_pDoc->SetString(ScAddress(0,1,0), u"Normal"_ustr);
636 m_pDoc->SetString(ScAddress(0,2,0), u"Bold"_ustr);
637 m_pDoc->SetString(ScAddress(0,3,0), u"Italic"_ustr);
639 // Set A3 bold and A4 italic.
640 const ScPatternAttr* pPat = m_pDoc->GetPattern(ScAddress(0,2,0));
641 CPPUNIT_ASSERT(pPat);
643 ScPatternAttr aNewPat(*pPat);
644 SfxItemSet& rSet = aNewPat.GetItemSet();
645 rSet.Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
646 m_pDoc->ApplyPattern(0, 2, 0, aNewPat);
648 // Make sure it's really in.
649 bool bGood = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,2,0)));
650 CPPUNIT_ASSERT_MESSAGE("A3 is not bold but it should.", bGood);
653 pPat = m_pDoc->GetPattern(ScAddress(0,3,0));
654 CPPUNIT_ASSERT(pPat);
656 ScPatternAttr aNewPat(*pPat);
657 SfxItemSet& rSet = aNewPat.GetItemSet();
658 rSet.Put(SvxPostureItem(ITALIC_NORMAL, ATTR_FONT_POSTURE));
659 m_pDoc->ApplyPattern(0, 3, 0, aNewPat);
661 bool bGood = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,3,0)));
662 CPPUNIT_ASSERT_MESSAGE("A4 is not italic but it should.", bGood);
665 // Define A1:A4 as sheet-local anonymous database range, else sort wouldn't run.
666 m_pDoc->SetAnonymousDBData(
667 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 3)));
669 // Sort A1:A4 ascending with cell formats.
670 ScDBDocFunc aFunc(*m_xDocShell);
672 ScSortParam aSortData;
673 aSortData.nCol1 = 0;
674 aSortData.nCol2 = 0;
675 aSortData.nRow1 = 0;
676 aSortData.nRow2 = 3;
677 aSortData.bHasHeader = true;
678 aSortData.aDataAreaExtras.mbCellFormats = true;
679 aSortData.maKeyState[0].bDoSort = true;
680 aSortData.maKeyState[0].nField = 0;
681 aSortData.maKeyState[0].bAscending = true;
682 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
683 bool bSorted = aFunc.Sort(0, aSortData, true, false, true);
684 CPPUNIT_ASSERT(bSorted);
686 // Check the sort result.
687 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
688 CPPUNIT_ASSERT_EQUAL(u"Bold"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
689 CPPUNIT_ASSERT_EQUAL(u"Italic"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
690 CPPUNIT_ASSERT_EQUAL(u"Normal"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
692 // A2 should be bold now.
693 bool bBold = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,1,0)));
694 CPPUNIT_ASSERT_MESSAGE("A2 should be bold after the sort.", bBold);
696 // and A3 should be italic.
697 bool bItalic = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,2,0)));
698 CPPUNIT_ASSERT_MESSAGE("A3 should be italic.", bItalic);
700 // A4 should have neither bold nor italic.
701 bool bNormal = aCheck.isNormal(m_pDoc->GetPattern(ScAddress(0,3,0)));
702 CPPUNIT_ASSERT_MESSAGE("A4 should be neither bold nor italic.", bNormal);
704 m_pDoc->DeleteTab(0);
707 CPPUNIT_TEST_FIXTURE(TestSort, testSortRefUpdate)
709 SortTypeSetter aSortTypeSet(true);
711 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
712 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
714 m_pDoc->InsertTab(0, u"Sort"_ustr);
716 // Set values to sort in column A.
717 m_pDoc->SetString(ScAddress(0,0,0), u"Header"_ustr);
719 double aValues[] = { 4.0, 36.0, 14.0, 29.0, 98.0, 78.0, 0.0, 99.0, 1.0 };
720 size_t nCount = std::size(aValues);
721 for (size_t i = 0; i < nCount; ++i)
722 m_pDoc->SetValue(ScAddress(0,i+1,0), aValues[i]);
724 // Set formulas to reference these values in column C.
725 m_pDoc->SetString(ScAddress(2,0,0), u"Formula"_ustr);
726 for (size_t i = 0; i < nCount; ++i)
727 m_pDoc->SetString(ScAddress(2,1+i,0), u"=RC[-2]"_ustr);
729 // Check the values in column C.
730 for (size_t i = 0; i < nCount; ++i)
732 double fCheck = aValues[i];
733 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0)));
736 ScDBDocFunc aFunc(*m_xDocShell);
738 // Define A1:A10 as sheet-local anonymous database range, else sort wouldn't run.
739 m_pDoc->SetAnonymousDBData(
740 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 9)));
742 // Sort A1:A10 (with a header row).
743 ScSortParam aSortData;
744 aSortData.nCol1 = 0;
745 aSortData.nCol2 = 0;
746 aSortData.nRow1 = 0;
747 aSortData.nRow2 = 9;
748 aSortData.bHasHeader = true;
749 aSortData.maKeyState[0].bDoSort = true;
750 aSortData.maKeyState[0].nField = 0;
751 aSortData.maKeyState[0].bAscending = true;
752 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
753 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
754 CPPUNIT_ASSERT(bSorted);
756 double aSorted[] = { 0.0, 1.0, 4.0, 14.0, 29.0, 36.0, 78.0, 98.0, 99.0 };
758 // Check the sort result.
759 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
760 for (size_t i = 0; i < nCount; ++i)
762 double fCheck = aSorted[i];
763 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0)));
766 // Sorting should not alter the values in column C.
767 m_pDoc->CalcAll(); // just in case...
768 for (size_t i = 0; i < nCount; ++i)
770 double fCheck = aValues[i];
771 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0)));
774 // C2 should now point to A4.
775 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2!", u"=R[2]C[-2]"_ustr, m_pDoc->GetFormula(2,1,0));
777 // Undo the sort.
778 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
779 pUndoMgr->Undo();
781 // Check the undo result.
782 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
783 for (size_t i = 0; i < nCount; ++i)
785 double fCheck = aValues[i];
786 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0)));
789 // Values in column C should still be unaltered.
790 m_pDoc->CalcAll(); // just in case...
791 for (size_t i = 0; i < nCount; ++i)
793 double fCheck = aValues[i];
794 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0)));
797 // C2 should now point to A2.
798 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2!", u"=RC[-2]"_ustr, m_pDoc->GetFormula(2,1,0));
800 // Redo.
801 pUndoMgr->Redo();
803 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
804 for (size_t i = 0; i < nCount; ++i)
806 double fCheck = aSorted[i];
807 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0)));
810 // Sorting should not alter the values in column C.
811 m_pDoc->CalcAll(); // just in case...
812 for (size_t i = 0; i < nCount; ++i)
814 double fCheck = aValues[i];
815 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0)));
818 // C2 should now point to A4.
819 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in C2!", u"=R[2]C[-2]"_ustr, m_pDoc->GetFormula(2,1,0));
821 // Undo again.
822 pUndoMgr->Undo();
824 // Formulas in column C should all be "RC[-2]" again.
825 for (size_t i = 0; i < nCount; ++i)
826 m_pDoc->SetString(ScAddress(2,1+i,0), u"=RC[-2]"_ustr);
828 // Turn off reference update on sort.
829 SortTypeSetter::changeTo(false);
831 bSorted = aFunc.Sort(0, aSortData, true, true, true);
832 CPPUNIT_ASSERT(bSorted);
834 // Check the sort result again.
835 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
836 for (size_t i = 0; i < nCount; ++i)
838 double fCheck = aSorted[i];
839 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0)));
842 // Formulas in column C should all remain "RC[-2]".
843 for (size_t i = 0; i < nCount; ++i)
844 m_pDoc->SetString(ScAddress(2,1+i,0), u"=RC[-2]"_ustr);
846 // The values in column C should now be the same as sorted values in column A.
847 m_pDoc->CalcAll(); // just in case...
848 for (size_t i = 0; i < nCount; ++i)
850 double fCheck = aSorted[i];
851 CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); // column C
854 m_pDoc->DeleteTab(0);
857 CPPUNIT_TEST_FIXTURE(TestSort, testSortRefUpdate2)
859 SortRefUpdateSetter aUpdateSet;
861 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
862 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
864 m_pDoc->InsertTab(0, u"Sort"_ustr);
866 // Set up the sheet.
867 const char* aData[][2] = {
868 { "F1", "F2" },
869 { "9", "=RC[-1]" },
870 { "2", "=RC[-1]" },
871 { "6", "=RC[-1]" },
872 { "4", "=RC[-1]" },
873 { nullptr, nullptr } // terminator
876 for (SCROW i = 0; aData[i][0]; ++i)
878 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
879 m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
882 // Check the values in B2:B5.
883 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,1,0)));
884 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0)));
885 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
886 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,4,0)));
888 ScDBDocFunc aFunc(*m_xDocShell);
890 // Define A1:B5 as sheet-local anonymous database range, else sort wouldn't run.
891 m_pDoc->SetAnonymousDBData(
892 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 1, 4)));
894 // Sort A1:B5 by column A (with a row header).
895 ScSortParam aSortData;
896 aSortData.nCol1 = 0;
897 aSortData.nCol2 = 1;
898 aSortData.nRow1 = 0;
899 aSortData.nRow2 = 4;
900 aSortData.bHasHeader = true;
901 aSortData.maKeyState[0].bDoSort = true;
902 aSortData.maKeyState[0].nField = 0;
903 aSortData.maKeyState[0].bAscending = true;
904 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
905 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
906 CPPUNIT_ASSERT(bSorted);
908 // Check the sort result in column A.
909 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
910 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,2,0)));
911 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,3,0)));
912 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
914 // and column B.
915 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
916 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
917 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
918 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,4,0)));
920 // Formulas in column B should still point to their respective left neighbor cell.
921 for (SCROW i = 1; i <= 4; ++i)
923 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=RC[-1]"_ustr, m_pDoc->GetFormula(1,i,0));
926 // Undo and check the result in column B.
927 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
928 pUndoMgr->Undo();
930 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,1,0)));
931 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0)));
932 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
933 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,4,0)));
935 // and redo.
936 pUndoMgr->Redo();
938 CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
939 CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
940 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
941 CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,4,0)));
943 m_pDoc->DeleteTab(0);
946 CPPUNIT_TEST_FIXTURE(TestSort, testSortRefUpdate3)
948 SortRefUpdateSetter aUpdateSet;
950 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
951 m_pDoc->InsertTab(0, u"Sort"_ustr);
953 const char* pData[] = {
954 "Header",
955 "1",
956 "=A2+10",
957 "2",
958 "=A4+10",
959 "=A2+A4",
960 nullptr // terminator
963 for (SCROW i = 0; pData[i]; ++i)
964 m_pDoc->SetString(ScAddress(0,i,0), OUString::createFromAscii(pData[i]));
966 // Check the initial values.
967 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
968 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0)));
969 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,2,0)));
970 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
971 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,4,0)));
972 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
974 ScDBDocFunc aFunc(*m_xDocShell);
976 // Sort A1:A6.
977 m_pDoc->SetAnonymousDBData(
978 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 5)));
980 // Sort A1:A6 by column A (with a row header).
981 ScSortParam aSortData;
982 aSortData.nCol1 = 0;
983 aSortData.nCol2 = 0;
984 aSortData.nRow1 = 0;
985 aSortData.nRow2 = 5;
986 aSortData.bHasHeader = true;
987 aSortData.maKeyState[0].bDoSort = true;
988 aSortData.maKeyState[0].nField = 0;
989 aSortData.maKeyState[0].bAscending = true;
990 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
991 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
992 CPPUNIT_ASSERT(bSorted);
994 // Check the sorted values.
995 m_pDoc->CalcAll();
996 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
997 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0)));
998 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
999 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1000 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1001 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1003 // Make sure the formula cells have been adjusted correctly.
1004 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A4.", u"=A2+A3"_ustr, m_pDoc->GetFormula(0,3,0));
1005 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A5.", u"=A2+10"_ustr, m_pDoc->GetFormula(0,4,0));
1006 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in A6.", u"=A3+10"_ustr, m_pDoc->GetFormula(0,5,0));
1008 // Undo and check the result.
1009 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1010 pUndoMgr->Undo();
1011 m_pDoc->CalcAll();
1012 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1013 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1014 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1015 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1016 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1017 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1019 // Redo and check the result.
1020 pUndoMgr->Redo();
1021 m_pDoc->CalcAll();
1022 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1023 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1024 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1025 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1026 CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1027 CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1029 m_pDoc->DeleteTab(0);
1032 // Derived from fdo#79441 https://bugs.freedesktop.org/attachment.cgi?id=100144
1033 // testRefInterne.ods
1034 CPPUNIT_TEST_FIXTURE(TestSort, testSortRefUpdate4)
1036 // This test has to work in both update reference modes.
1038 SortRefNoUpdateSetter aUpdateSet;
1039 testSortRefUpdate4_Impl();
1042 SortRefUpdateSetter aUpdateSet;
1043 testSortRefUpdate4_Impl();
1047 void TestSort::testSortRefUpdate4_Impl()
1049 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1050 m_pDoc->InsertTab(0, u"Sort"_ustr);
1051 m_pDoc->InsertTab(1, u"Lesson1"_ustr);
1052 m_pDoc->InsertTab(2, u"Lesson2"_ustr);
1055 const std::vector<std::vector<const char*>> aData = {
1056 { "Name", "Note" },
1057 { "Student1", "1" },
1058 { "Student2", "2" },
1059 { "Student3", "3" },
1060 { "Student4", "4" },
1061 { "Student5", "5" },
1064 SCTAB nTab = 1;
1065 ScAddress aPos(0,0,nTab);
1066 clearRange(m_pDoc, ScRange(0, 0, nTab, 1, aData.size(), nTab));
1067 ScRange aLesson1Range = insertRangeData(m_pDoc, aPos, aData);
1068 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aLesson1Range.aStart);
1072 const std::vector<std::vector<const char*>> aData = {
1073 { "Name", "Note" },
1074 { "=Lesson1.A2", "3" },
1075 { "=Lesson1.A3", "4" },
1076 { "=Lesson1.A4", "9" },
1077 { "=Lesson1.A5", "6" },
1078 { "=Lesson1.A6", "3" },
1081 SCTAB nTab = 2;
1082 ScAddress aPos(0,0,nTab);
1083 clearRange(m_pDoc, ScRange(0, 0, nTab, 1, aData.size(), nTab));
1084 ScRange aLesson2Range = insertRangeData(m_pDoc, aPos, aData);
1085 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aLesson2Range.aStart);
1088 ScRange aSortRange;
1090 const std::vector<std::vector<const char*>> aData = {
1091 { "Name", "Lesson1", "Lesson2", "Average" },
1092 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B2:C2)" },
1093 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B3:C3)" },
1094 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B4:C4)" },
1095 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B5:C5)" },
1096 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B6:C6)" },
1099 SCTAB nTab = 0;
1100 ScAddress aPos(0,0,nTab);
1101 clearRange(m_pDoc, ScRange(0, 0, nTab, 1, aData.size(), nTab));
1102 aSortRange = insertRangeData(m_pDoc, aPos, aData);
1103 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aSortRange.aStart);
1106 ScDBDocFunc aFunc(*m_xDocShell);
1108 // Sort A1:D6 by column D (Average, with a row header).
1110 ScSortParam aSortData;
1111 aSortData.nCol1 = aSortRange.aStart.Col();
1112 aSortData.nCol2 = aSortRange.aEnd.Col();
1113 aSortData.nRow1 = aSortRange.aStart.Row();
1114 aSortData.nRow2 = aSortRange.aEnd.Row();
1115 aSortData.bHasHeader = true;
1116 aSortData.maKeyState[0].bDoSort = true; // sort on
1117 aSortData.maKeyState[0].nField = 3; // Average
1118 aSortData.maKeyState[0].bAscending = false; // descending
1119 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1121 m_pDoc->SetAnonymousDBData( 0, std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME, aSortRange.aStart.Tab(),
1122 aSortData.nCol1, aSortData.nRow1, aSortData.nCol2, aSortData.nRow2)));
1124 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1125 CPPUNIT_ASSERT(bSorted);
1127 // Check the sorted values.
1128 m_pDoc->CalcAll();
1129 CPPUNIT_ASSERT_EQUAL(u"Name"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1130 CPPUNIT_ASSERT_EQUAL(u"Student3"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
1131 CPPUNIT_ASSERT_EQUAL(u"Student4"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
1132 CPPUNIT_ASSERT_EQUAL(u"Student5"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
1133 CPPUNIT_ASSERT_EQUAL(u"Student2"_ustr, m_pDoc->GetString(ScAddress(0,4,0)));
1134 CPPUNIT_ASSERT_EQUAL(u"Student1"_ustr, m_pDoc->GetString(ScAddress(0,5,0)));
1135 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,1,0)));
1136 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,2,0)));
1137 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,3,0)));
1138 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,4,0)));
1139 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,5,0)));
1141 // Make sure the formula cells have been adjusted correctly.
1142 const char* aCheck[][4] = {
1143 // Name Lesson1 Lesson2 Average
1144 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B2:C2)" },
1145 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B3:C3)" },
1146 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B4:C4)" },
1147 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B5:C5)" },
1148 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B6:C6)" },
1150 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aCheck)); ++nRow)
1152 for (SCCOL nCol=0; nCol < 4; ++nCol)
1154 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));
1158 // Undo and check the result.
1159 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1160 pUndoMgr->Undo();
1161 m_pDoc->CalcAll();
1162 CPPUNIT_ASSERT_EQUAL(u"Name"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1163 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,1,0)));
1164 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,2,0)));
1165 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
1166 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,4,0)));
1167 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,5,0)));
1169 // Redo and check the result.
1170 pUndoMgr->Redo();
1171 m_pDoc->CalcAll();
1172 CPPUNIT_ASSERT_EQUAL(u"Name"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1173 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,1,0)));
1174 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,2,0)));
1175 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,3,0)));
1176 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,4,0)));
1177 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,5,0)));
1180 // Sort A2:AMJ6 by column A (Name, without header).
1182 ScSortParam aSortData;
1183 aSortData.nCol1 = 0;
1184 aSortData.nCol2 = m_pDoc->MaxCol();
1185 aSortData.nRow1 = aSortRange.aStart.Row()+1;
1186 aSortData.nRow2 = aSortRange.aEnd.Row();
1187 aSortData.bHasHeader = false;
1188 aSortData.maKeyState[0].bDoSort = true; // sort on
1189 aSortData.maKeyState[0].nField = 0; // Name
1190 aSortData.maKeyState[0].bAscending = false; // descending
1191 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1193 m_pDoc->SetAnonymousDBData( 0, std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME, aSortRange.aStart.Tab(),
1194 aSortData.nCol1, aSortData.nRow1, aSortData.nCol2, aSortData.nRow2)));
1196 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1197 CPPUNIT_ASSERT(bSorted);
1199 // Check the sorted values.
1200 m_pDoc->CalcAll();
1201 CPPUNIT_ASSERT_EQUAL(u"Name"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1202 CPPUNIT_ASSERT_EQUAL(u"Student5"_ustr, m_pDoc->GetString(ScAddress(0,1,0)));
1203 CPPUNIT_ASSERT_EQUAL(u"Student4"_ustr, m_pDoc->GetString(ScAddress(0,2,0)));
1204 CPPUNIT_ASSERT_EQUAL(u"Student3"_ustr, m_pDoc->GetString(ScAddress(0,3,0)));
1205 CPPUNIT_ASSERT_EQUAL(u"Student2"_ustr, m_pDoc->GetString(ScAddress(0,4,0)));
1206 CPPUNIT_ASSERT_EQUAL(u"Student1"_ustr, m_pDoc->GetString(ScAddress(0,5,0)));
1207 CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,1,0)));
1208 CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,2,0)));
1209 CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
1210 CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,4,0)));
1211 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,5,0)));
1213 // Make sure the formula cells have been adjusted correctly.
1214 const char* aCheck[][4] = {
1215 // Name Lesson1 Lesson2 Average
1216 { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B2:C2)" },
1217 { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B3:C3)" },
1218 { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B4:C4)" },
1219 { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B5:C5)" },
1220 { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B6:C6)" },
1222 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aCheck)); ++nRow)
1224 for (SCCOL nCol=0; nCol < 4; ++nCol)
1226 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));
1231 m_pDoc->DeleteTab(2);
1232 m_pDoc->DeleteTab(1);
1233 m_pDoc->DeleteTab(0);
1236 // Make sure the refupdate works also with volatile cells, see fdo#83067
1237 /* FIXME: this test is not roll-over-midnight safe and will fail then! We may
1238 * want to have something different, but due to the nature of volatile
1239 * functions it's not that easy to come up with something reproducible staying
1240 * stable over sorts... ;-) Check for time and don't run test a few seconds
1241 * before midnight, ermm... */
1242 CPPUNIT_TEST_FIXTURE(TestSort, testSortRefUpdate5)
1244 SortRefUpdateSetter aUpdateSet;
1246 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1247 m_pDoc->InsertTab(0, u"Sort"_ustr);
1249 double aValCheck[][3] = {
1250 // Result, Unsorted order, Sorted result.
1251 { 0, 4, 0 },
1252 { 0, 1, 0 },
1253 { 0, 3, 0 },
1254 { 0, 2, 0 },
1256 ScRange aSortRange;
1258 const std::vector<std::vector<const char*>> aData = {
1259 { "Date", "Volatile", "Order" },
1260 { "1999-05-05", "=TODAY()-$A2", "4" },
1261 { "1994-10-18", "=TODAY()-$A3", "1" },
1262 { "1996-06-30", "=TODAY()-$A4", "3" },
1263 { "1995-11-21", "=TODAY()-$A5", "2" },
1266 SCTAB nTab = 0;
1267 ScAddress aPos(0,0,nTab);
1268 clearRange(m_pDoc, ScRange(0, 0, nTab, 2, aData.size(), nTab));
1269 aSortRange = insertRangeData(m_pDoc, aPos, aData);
1270 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aSortRange.aStart);
1272 // Actual results and expected sorted results.
1273 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
1275 double fVal = m_pDoc->GetValue(ScAddress(1,nRow+1,0));
1276 aValCheck[nRow][0] = fVal;
1277 aValCheck[static_cast<size_t>(aValCheck[nRow][1])-1][2] = fVal;
1281 ScDBDocFunc aFunc(*m_xDocShell);
1283 // Sort A1:B5.
1284 m_pDoc->SetAnonymousDBData( 0, std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME, aSortRange.aStart.Tab(),
1285 aSortRange.aStart.Col(), aSortRange.aStart.Row(), aSortRange.aEnd.Col(), aSortRange.aEnd.Row())));
1287 // Sort by column A.
1288 ScSortParam aSortData;
1289 aSortData.nCol1 = aSortRange.aStart.Col();
1290 aSortData.nCol2 = aSortRange.aEnd.Col();
1291 aSortData.nRow1 = aSortRange.aStart.Row();
1292 aSortData.nRow2 = aSortRange.aEnd.Row();
1293 aSortData.bHasHeader = true;
1294 aSortData.maKeyState[0].bDoSort = true; // sort on
1295 aSortData.maKeyState[0].nField = 0; // Date
1296 aSortData.maKeyState[0].bAscending = true; // ascending
1297 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1298 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1299 CPPUNIT_ASSERT(bSorted);
1301 // Check the sorted values.
1302 m_pDoc->CalcAll();
1303 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
1305 size_t i = static_cast<size_t>(m_pDoc->GetValue(ScAddress(2,nRow+1,0))); // order 1..4
1306 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(nRow+1), i);
1307 CPPUNIT_ASSERT_EQUAL( aValCheck[i-1][2], m_pDoc->GetValue(ScAddress(1,nRow+1,0)));
1310 // Make sure the formula cells have been adjusted correctly.
1311 const char* aFormulaCheck[] = {
1312 // Volatile
1313 "=TODAY()-$A2",
1314 "=TODAY()-$A3",
1315 "=TODAY()-$A4",
1316 "=TODAY()-$A5",
1318 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aFormulaCheck)); ++nRow)
1320 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));
1323 // Undo and check the result.
1324 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1325 pUndoMgr->Undo();
1326 m_pDoc->CalcAll();
1327 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
1329 CPPUNIT_ASSERT_EQUAL( aValCheck[nRow][0], m_pDoc->GetValue(ScAddress(1,nRow+1,0)));
1330 CPPUNIT_ASSERT_EQUAL( aValCheck[nRow][1], m_pDoc->GetValue(ScAddress(2,nRow+1,0)));
1333 // Redo and check the result.
1334 pUndoMgr->Redo();
1335 m_pDoc->CalcAll();
1336 for (SCROW nRow=0; nRow < static_cast<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
1338 size_t i = static_cast<size_t>(m_pDoc->GetValue(ScAddress(2,nRow+1,0))); // order 1..4
1339 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(nRow+1), i);
1340 CPPUNIT_ASSERT_EQUAL( aValCheck[i-1][2], m_pDoc->GetValue(ScAddress(1,nRow+1,0)));
1343 m_pDoc->DeleteTab(0);
1346 CPPUNIT_TEST_FIXTURE(TestSort, testSortRefUpdate6)
1348 SortRefNoUpdateSetter aUpdateSet;
1350 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1351 m_pDoc->InsertTab(0, u"Sort"_ustr);
1353 const std::vector<std::vector<const char*>> aData = {
1354 { "Order", "Value", "1" },
1355 { "9", "1", "=C1+B2" },
1356 { "1", "2", "=C2+B3" },
1357 { "8", "3", "=C3+B4" },
1360 ScAddress aPos(0,0,0);
1361 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
1362 CPPUNIT_ASSERT_EQUAL(aPos, aDataRange.aStart);
1365 // Expected output table content. 0 = empty cell
1366 std::vector<std::vector<const char*>> aOutputCheck = {
1367 { "Order", "Value", "1" },
1368 { "9", "1", "2" },
1369 { "1", "2", "4" },
1370 { "8", "3", "7" },
1373 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1374 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1377 ScDBDocFunc aFunc(*m_xDocShell);
1379 // Sort A1:C4.
1380 m_pDoc->SetAnonymousDBData(
1381 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 2, 3)));
1383 // Sort A1:A6 by column A (with a row header).
1384 ScSortParam aSortData;
1385 aSortData.nCol1 = 0;
1386 aSortData.nCol2 = 2;
1387 aSortData.nRow1 = 0;
1388 aSortData.nRow2 = 3;
1389 aSortData.bHasHeader = true;
1390 aSortData.maKeyState[0].bDoSort = true;
1391 aSortData.maKeyState[0].nField = 0;
1392 aSortData.maKeyState[0].bAscending = true;
1393 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1394 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1395 CPPUNIT_ASSERT(bSorted);
1398 // Expected output table content. 0 = empty cell
1399 std::vector<std::vector<const char*>> aOutputCheck = {
1400 { "Order", "Value", "1" },
1401 { "1", "2", "3" },
1402 { "8", "3", "6" },
1403 { "9", "1", "7" },
1406 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Sorted without reference update");
1407 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1410 // Make sure that the formulas in C2:C4 are not adjusted.
1411 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=C1+B2"_ustr, m_pDoc->GetFormula(2,1,0));
1412 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=C2+B3"_ustr, m_pDoc->GetFormula(2,2,0));
1413 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=C3+B4"_ustr, m_pDoc->GetFormula(2,3,0));
1415 // Undo and check.
1416 SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
1417 CPPUNIT_ASSERT(pUndoMgr);
1419 pUndoMgr->Undo();
1422 // Expected output table content. 0 = empty cell
1423 std::vector<std::vector<const char*>> aOutputCheck = {
1424 { "Order", "Value", "1" },
1425 { "9", "1", "2" },
1426 { "1", "2", "4" },
1427 { "8", "3", "7" },
1430 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo");
1431 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1434 // Redo and check.
1435 pUndoMgr->Redo();
1437 // Expected output table content. 0 = empty cell
1438 std::vector<std::vector<const char*>> aOutputCheck = {
1439 { "Order", "Value", "1" },
1440 { "1", "2", "3" },
1441 { "8", "3", "6" },
1442 { "9", "1", "7" },
1445 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After redo");
1446 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1449 // Change the value of C1 and make sure the formula broadcasting chain still works.
1450 ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
1451 rFunc.SetValueCell(ScAddress(2,0,0), 11.0, false);
1453 // Expected output table content. 0 = empty cell
1454 std::vector<std::vector<const char*>> aOutputCheck = {
1455 { "Order", "Value", "11" },
1456 { "1", "2", "13" },
1457 { "8", "3", "16" },
1458 { "9", "1", "17" },
1461 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Change the header value");
1462 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1465 // Undo and check.
1466 pUndoMgr->Undo();
1468 // Expected output table content. 0 = empty cell
1469 std::vector<std::vector<const char*>> aOutputCheck = {
1470 { "Order", "Value", "1" },
1471 { "1", "2", "3" },
1472 { "8", "3", "6" },
1473 { "9", "1", "7" },
1476 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "After undo of header value change");
1477 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1480 m_pDoc->DeleteTab(0);
1483 // fdo#86762 check that broadcasters are sorted correctly and empty cell is
1484 // broadcasted.
1485 CPPUNIT_TEST_FIXTURE(TestSort, testSortBroadcaster)
1487 SortRefNoUpdateSetter aUpdateSet;
1489 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1490 m_pDoc->InsertTab(0, u"Sort"_ustr);
1493 const std::vector<std::vector<const char*>> aData = {
1494 { "1", nullptr, nullptr, "=B1", "=$B$1", "=SUM(A1:B1)", "=SUM($A$1:$B$1)" },
1495 { "2", "8", nullptr, "=B2", "=$B$2", "=SUM(A2:B2)", "=SUM($A$2:$B$2)" },
1498 ScAddress aPos(0,0,0);
1499 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
1500 CPPUNIT_ASSERT_EQUAL(aPos, aDataRange.aStart);
1503 // Expected output table content. 0 = empty cell
1504 std::vector<std::vector<const char*>> aOutputCheck = {
1505 { "1", nullptr, nullptr, "0", "0", "1", "1" },
1506 { "2", "8", nullptr, "8", "8", "10", "10" },
1509 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1510 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1513 // Sort A1:B2.
1514 m_pDoc->SetAnonymousDBData(
1515 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 1, 1)));
1517 ScDBDocFunc aFunc(*m_xDocShell);
1519 // Sort A1:B2 by column A descending.
1520 ScSortParam aSortData;
1521 aSortData.nCol1 = 0;
1522 aSortData.nCol2 = 1;
1523 aSortData.nRow1 = 0;
1524 aSortData.nRow2 = 1;
1525 aSortData.bHasHeader = false;
1526 aSortData.bByRow = true;
1527 aSortData.maKeyState[0].bDoSort = true;
1528 aSortData.maKeyState[0].nField = 0;
1529 aSortData.maKeyState[0].bAscending = false;
1530 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1531 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1532 CPPUNIT_ASSERT(bSorted);
1535 // Expected output table content. 0 = empty cell
1536 std::vector<std::vector<const char*>> aOutputCheck = {
1537 { "2", "8", nullptr, "8", "8", "10", "10" },
1538 { "1", nullptr, nullptr, "0", "0", "1", "1" },
1541 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Sorted without reference update");
1542 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1545 // Make sure that the formulas in D1:G2 are not adjusted.
1546 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=B1"_ustr, m_pDoc->GetFormula(3,0,0));
1547 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=B2"_ustr, m_pDoc->GetFormula(3,1,0));
1548 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=$B$1"_ustr, m_pDoc->GetFormula(4,0,0));
1549 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=$B$2"_ustr, m_pDoc->GetFormula(4,1,0));
1550 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(A1:B1)"_ustr, m_pDoc->GetFormula(5,0,0));
1551 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(A2:B2)"_ustr, m_pDoc->GetFormula(5,1,0));
1552 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM($A$1:$B$1)"_ustr, m_pDoc->GetFormula(6,0,0));
1553 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM($A$2:$B$2)"_ustr, m_pDoc->GetFormula(6,1,0));
1555 // Enter new value and check that it is broadcasted. First in empty cell.
1556 m_pDoc->SetString(1,1,0, u"16"_ustr);
1557 double nVal = m_pDoc->GetValue(3,1,0);
1558 ASSERT_DOUBLES_EQUAL( 16.0, nVal);
1559 nVal = m_pDoc->GetValue(4,1,0);
1560 ASSERT_DOUBLES_EQUAL( 16.0, nVal);
1561 nVal = m_pDoc->GetValue(5,1,0);
1562 ASSERT_DOUBLES_EQUAL( 17.0, nVal);
1563 nVal = m_pDoc->GetValue(6,1,0);
1564 ASSERT_DOUBLES_EQUAL( 17.0, nVal);
1566 // Enter new value and check that it is broadcasted. Now overwriting data.
1567 m_pDoc->SetString(1,0,0, u"32"_ustr);
1568 nVal = m_pDoc->GetValue(3,0,0);
1569 ASSERT_DOUBLES_EQUAL( 32.0, nVal);
1570 nVal = m_pDoc->GetValue(4,0,0);
1571 ASSERT_DOUBLES_EQUAL( 32.0, nVal);
1572 nVal = m_pDoc->GetValue(5,0,0);
1573 ASSERT_DOUBLES_EQUAL( 34.0, nVal);
1574 nVal = m_pDoc->GetValue(6,0,0);
1575 ASSERT_DOUBLES_EQUAL( 34.0, nVal);
1578 // The same for sort by column. Start data at A5.
1581 const std::vector<std::vector<const char*>> aData = {
1582 { "1", "2" },
1583 { nullptr, "8" },
1584 { nullptr, nullptr },
1585 { "=A6", "=B6" },
1586 { "=$A$6", "=$B$6" },
1587 { "=SUM(A5:A6)", "=SUM(B5:B6)" },
1588 { "=SUM($A$5:$A$6)", "=SUM($B$5:$B$6)" },
1591 ScAddress aPos(0,4,0);
1592 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
1593 CPPUNIT_ASSERT_EQUAL(aPos, aDataRange.aStart);
1596 // Expected output table content. 0 = empty cell
1597 std::vector<std::vector<const char*>> aOutputCheck = {
1598 { "1", "2" },
1599 { nullptr, "8" },
1600 { nullptr, nullptr },
1601 { "0", "8" },
1602 { "0", "8" },
1603 { "1", "10" },
1604 { "1", "10" },
1607 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1608 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1611 // Sort A5:B6.
1612 m_pDoc->SetAnonymousDBData(
1613 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 4, 1, 5)));
1615 ScDBDocFunc aFunc(*m_xDocShell);
1617 // Sort A5:B6 by row 5 descending.
1618 ScSortParam aSortData;
1619 aSortData.nCol1 = 0;
1620 aSortData.nCol2 = 1;
1621 aSortData.nRow1 = 4;
1622 aSortData.nRow2 = 5;
1623 aSortData.bHasHeader = false;
1624 aSortData.bByRow = false;
1625 aSortData.maKeyState[0].bDoSort = true;
1626 aSortData.maKeyState[0].nField = 0;
1627 aSortData.maKeyState[0].bAscending = false;
1628 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1629 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1630 CPPUNIT_ASSERT(bSorted);
1633 // Expected output table content. 0 = empty cell
1634 std::vector<std::vector<const char*>> aOutputCheck = {
1635 { "2", "1" },
1636 { "8", nullptr },
1637 { nullptr, nullptr },
1638 { "8", "0" },
1639 { "8", "0" },
1640 { "10", "1" },
1641 { "10", "1" },
1644 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Sorted without reference update");
1645 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1648 // Make sure that the formulas in A8:B11 are not adjusted.
1649 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=A6"_ustr, m_pDoc->GetFormula(0,7,0));
1650 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=B6"_ustr, m_pDoc->GetFormula(1,7,0));
1651 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=$A$6"_ustr, m_pDoc->GetFormula(0,8,0));
1652 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=$B$6"_ustr, m_pDoc->GetFormula(1,8,0));
1653 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(A5:A6)"_ustr, m_pDoc->GetFormula(0,9,0));
1654 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(B5:B6)"_ustr, m_pDoc->GetFormula(1,9,0));
1655 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM($A$5:$A$6)"_ustr, m_pDoc->GetFormula(0,10,0));
1656 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM($B$5:$B$6)"_ustr, m_pDoc->GetFormula(1,10,0));
1658 // Enter new value and check that it is broadcasted. First in empty cell.
1659 m_pDoc->SetString(1,5,0, u"16"_ustr);
1660 double nVal = m_pDoc->GetValue(1,7,0);
1661 ASSERT_DOUBLES_EQUAL(16.0, nVal);
1662 nVal = m_pDoc->GetValue(1,8,0);
1663 ASSERT_DOUBLES_EQUAL(16.0, nVal);
1664 nVal = m_pDoc->GetValue(1,9,0);
1665 ASSERT_DOUBLES_EQUAL(17.0, nVal);
1666 nVal = m_pDoc->GetValue(1,10,0);
1667 ASSERT_DOUBLES_EQUAL(17.0, nVal);
1669 // Enter new value and check that it is broadcasted. Now overwriting data.
1670 m_pDoc->SetString(0,5,0, u"32"_ustr);
1671 nVal = m_pDoc->GetValue(0,7,0);
1672 ASSERT_DOUBLES_EQUAL(32.0, nVal);
1673 nVal = m_pDoc->GetValue(0,8,0);
1674 ASSERT_DOUBLES_EQUAL(32.0, nVal);
1675 nVal = m_pDoc->GetValue(0,9,0);
1676 ASSERT_DOUBLES_EQUAL(34.0, nVal);
1677 nVal = m_pDoc->GetValue(0,10,0);
1678 ASSERT_DOUBLES_EQUAL(34.0, nVal);
1681 m_pDoc->DeleteTab(0);
1684 // tdf#99417 check that formulas are tracked that *only* indirectly depend on
1685 // sorted data and no other broadcasting than BroadcastBroadcasters is
1686 // involved (for which this test can not be included in testSortBroadcaster()).
1687 CPPUNIT_TEST_FIXTURE(TestSort, testSortBroadcastBroadcaster)
1689 SortRefNoUpdateSetter aUpdateSet;
1691 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1692 m_pDoc->InsertTab(0, u"Sort"_ustr);
1695 const std::vector<std::vector<const char*>> aData = {
1696 { "1", "=A1", "=B1" },
1697 { "2", "=A2", "=B2" },
1700 ScAddress aPos(0,0,0);
1701 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
1702 CPPUNIT_ASSERT_EQUAL(aPos, aDataRange.aStart);
1705 // Expected output table content. 0 = empty cell
1706 std::vector<std::vector<const char*>> aOutputCheck = {
1707 { "1", "1", "1" },
1708 { "2", "2", "2" },
1711 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Initial value");
1712 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1715 // Sort A1:A2.
1716 m_pDoc->SetAnonymousDBData(
1717 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 1)));
1719 ScDBDocFunc aFunc(*m_xDocShell);
1721 // Sort A1:A2 by column A descending.
1722 ScSortParam aSortData;
1723 aSortData.nCol1 = 0;
1724 aSortData.nCol2 = 0;
1725 aSortData.nRow1 = 0;
1726 aSortData.nRow2 = 1;
1727 aSortData.bHasHeader = false;
1728 aSortData.bByRow = true;
1729 aSortData.maKeyState[0].bDoSort = true;
1730 aSortData.maKeyState[0].nField = 0;
1731 aSortData.maKeyState[0].bAscending = false;
1732 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1733 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1734 CPPUNIT_ASSERT(bSorted);
1737 // Expected output table content. 0 = empty cell
1738 std::vector<std::vector<const char*>> aOutputCheck = {
1739 { "2", "2", "2" },
1740 { "1", "1", "1" },
1743 bool bSuccess = checkOutput(m_pDoc, aDataRange, aOutputCheck, "Sorted without reference update");
1744 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
1747 // Make sure that the formulas in B1:C2 are not adjusted.
1748 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=A1"_ustr, m_pDoc->GetFormula(1,0,0));
1749 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=A2"_ustr, m_pDoc->GetFormula(1,1,0));
1750 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=B1"_ustr, m_pDoc->GetFormula(2,0,0));
1751 CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=B2"_ustr, m_pDoc->GetFormula(2,1,0));
1754 m_pDoc->DeleteTab(0);
1757 CPPUNIT_TEST_FIXTURE(TestSort, testSortOutOfPlaceResult)
1759 m_pDoc->InsertTab(0, u"Sort"_ustr);
1760 m_pDoc->InsertTab(1, u"Result"_ustr);
1762 const char* pData[] = {
1763 "Header",
1764 "1",
1765 "23",
1766 "2",
1767 "9",
1768 "-2",
1769 nullptr // terminator
1772 // source data in A1:A6.
1773 for (SCROW i = 0; pData[i]; ++i)
1774 m_pDoc->SetString(ScAddress(0,i,0), OUString::createFromAscii(pData[i]));
1776 // Check the initial values.
1777 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1778 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1779 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1780 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1781 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1782 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1784 ScDBDocFunc aFunc(*m_xDocShell);
1786 // Sort A1:A6, and set the result to C2:C7
1787 m_pDoc->SetAnonymousDBData(
1788 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 5)));
1790 ScSortParam aSortData;
1791 aSortData.nCol1 = 0;
1792 aSortData.nCol2 = 0;
1793 aSortData.nRow1 = 0;
1794 aSortData.nRow2 = 5;
1795 aSortData.bHasHeader = true;
1796 aSortData.bInplace = false;
1797 aSortData.nDestTab = 1;
1798 aSortData.nDestCol = 2;
1799 aSortData.nDestRow = 1;
1800 aSortData.maKeyState[0].bDoSort = true;
1801 aSortData.maKeyState[0].nField = 0;
1802 aSortData.maKeyState[0].bAscending = true;
1803 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1804 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1805 CPPUNIT_ASSERT(bSorted);
1807 // Source data still intact.
1808 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(0,0,0)));
1809 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1810 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1811 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1812 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1813 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1815 // Sort result in C2:C7 on sheet "Result".
1816 CPPUNIT_ASSERT_EQUAL(u"Header"_ustr, m_pDoc->GetString(ScAddress(2,1,1)));
1817 CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(2,2,1)));
1818 CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(2,3,1)));
1819 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(2,4,1)));
1820 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(2,5,1)));
1821 CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(2,6,1)));
1823 m_pDoc->DeleteTab(1);
1824 m_pDoc->DeleteTab(0);
1827 CPPUNIT_TEST_FIXTURE(TestSort, testSortPartialFormulaGroup)
1829 SortRefUpdateSetter aUpdateSet;
1831 sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
1832 FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
1834 m_pDoc->InsertTab(0, u"Sort"_ustr);
1836 // Set up the sheet.
1837 const char* aData[][2] = {
1838 { "F1", "F2" },
1839 { "43", "=RC[-1]" },
1840 { "50", "=RC[-1]" },
1841 { "8", "=RC[-1]" },
1842 { "47", "=RC[-1]" },
1843 { "28", "=RC[-1]" },
1844 { nullptr, nullptr } // terminator
1847 // A1:B6.
1848 for (SCROW i = 0; aData[i][0]; ++i)
1850 m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
1851 m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
1854 // Check the initial condition.
1855 for (SCROW i = 1; i <= 5; ++i)
1856 // A2:A6 should equal B2:B6.
1857 CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(ScAddress(0,i,0)), m_pDoc->GetValue(ScAddress(1,i,0)));
1859 const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
1860 CPPUNIT_ASSERT(pFC);
1861 CPPUNIT_ASSERT_MESSAGE("This formula cell should be the first in a group.", pFC->IsSharedTop());
1862 CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect formula group length.", static_cast<SCROW>(5), pFC->GetSharedLength());
1864 ScDBDocFunc aFunc(*m_xDocShell);
1866 // Sort only B2:B4. This caused crash at one point (c.f. fdo#81617).
1868 m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 1, 1, 1, 3)));
1870 ScSortParam aSortData;
1871 aSortData.nCol1 = 1;
1872 aSortData.nCol2 = 1;
1873 aSortData.nRow1 = 1;
1874 aSortData.nRow2 = 3;
1875 aSortData.bHasHeader = false;
1876 aSortData.bInplace = true;
1877 aSortData.maKeyState[0].bDoSort = true;
1878 aSortData.maKeyState[0].nField = 0;
1879 aSortData.maKeyState[0].bAscending = true;
1880 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1881 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
1882 CPPUNIT_ASSERT(bSorted);
1884 m_pDoc->CalcAll(); // just in case...
1886 // Check the cell values after the partial sort.
1888 // Column A
1889 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(0,1,0)));
1890 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(ScAddress(0,2,0)));
1891 CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc->GetValue(ScAddress(0,3,0)));
1892 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(0,4,0)));
1893 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
1895 // Column B
1896 CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc->GetValue(ScAddress(1,1,0)));
1897 CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(1,2,0)));
1898 CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(ScAddress(1,3,0)));
1899 CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(1,4,0)));
1900 CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(1,5,0)));
1902 m_pDoc->DeleteTab(0);
1905 CPPUNIT_TEST_FIXTURE(TestSort, testSortImages)
1907 m_pDoc->InsertTab(0, u"testSortImages"_ustr);
1909 // We need a drawing layer in order to create caption objects.
1910 m_pDoc->InitDrawLayer(m_xDocShell.get());
1911 ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
1912 CPPUNIT_ASSERT(pDrawLayer);
1914 ScAddress aPos(0,0,0);
1916 const std::vector<std::vector<const char*>> aData = {
1917 { "2" },
1918 { "1" },
1921 clearRange(m_pDoc, ScRange(0, 0, 0, 1, aData.size(), 0));
1922 ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData);
1923 CPPUNIT_ASSERT_EQUAL_MESSAGE("failed to insert range data at correct position", aPos, aDataRange.aStart);
1926 // Insert graphic in cell B2.
1927 const tools::Rectangle aOrigRect(1000, 1000, 1200, 1200);
1928 rtl::Reference<SdrCircObj> pObj = new SdrCircObj(*pDrawLayer, SdrCircKind::Full, aOrigRect);
1929 SdrPage* pPage = pDrawLayer->GetPage(0);
1930 CPPUNIT_ASSERT(pPage);
1931 pPage->InsertObject(pObj.get());
1932 // Anchor to cell
1933 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0, false);
1934 // Move to cell B2
1935 ScAddress aCellPos(1, 1, 0);
1936 pDrawLayer->MoveObject(pObj.get(), aCellPos);
1938 std::map<SCROW, std::vector<SdrObject*>> pRowObjects
1939 = pDrawLayer->GetObjectsAnchoredToRange(aCellPos.Tab(), aCellPos.Col(), aCellPos.Row(), aCellPos.Row());
1940 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pRowObjects[aCellPos.Row()].size());
1942 ScSortParam aSortData;
1943 aSortData.nCol1 = 0;
1944 aSortData.nCol2 = 1;
1945 aSortData.nRow1 = 0;
1946 aSortData.nRow2 = 1;
1947 aSortData.maKeyState[0].bDoSort = true;
1948 aSortData.maKeyState[0].nField = 0;
1949 aSortData.maKeyState[0].bAscending = true;
1950 aSortData.maKeyState[0].aColorSortMode = ScColorSortMode::None;
1952 m_pDoc->Sort(0, aSortData, false, true, nullptr, nullptr);
1954 double nVal = m_pDoc->GetValue(0,0,0);
1955 ASSERT_DOUBLES_EQUAL(1.0, nVal);
1957 // check that note is also moved after sorting
1958 aCellPos = ScAddress(1, 0, 0);
1959 pRowObjects
1960 = pDrawLayer->GetObjectsAnchoredToRange(aCellPos.Tab(), aCellPos.Col(), aCellPos.Row(), aCellPos.Row());
1961 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pRowObjects[aCellPos.Row()].size());
1963 m_pDoc->DeleteTab(0);
1966 namespace
1969 class TestQueryIterator
1970 : public ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::Generic >
1972 typedef ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::Generic > Base;
1973 public:
1974 TestQueryIterator( ScDocument& rDocument, ScInterpreterContext& rContext, SCTAB nTable,
1975 const ScQueryParam& aParam, bool bMod, bool bReverse = false )
1976 : Base( rDocument, rContext, nTable, aParam, bMod, bReverse )
1979 using Base::BinarySearch; // make public
1980 SCROW GetRow() const { return nRow; }
1983 ScQueryParam makeSearchParam( const ScRange& range, SCCOL col, ScQueryOp op, double value )
1985 ScQueryParam param;
1986 param.nCol1 = param.nCol2 = col;
1987 param.nRow1 = range.aStart.Row();
1988 param.nRow2 = range.aEnd.Row();
1989 param.nTab = 0;
1990 ScQueryEntry& entry = param.GetEntry(0);
1991 ScQueryEntry::Item& item = entry.GetQueryItem();
1992 entry.bDoQuery = true;
1993 entry.eOp = op;
1994 item.mfVal = value;
1995 item.meType = ScQueryEntry::ByValue;
1996 return param;
1999 } // namespace
2001 CPPUNIT_TEST_FIXTURE(TestSort, testQueryBinarySearch)
2003 m_pDoc->InsertTab(0, u"testQueryBinarySearch"_ustr);
2005 const ScAddress formulaAddress( 10, 0, 0 );
2006 ScRange range;
2007 SCCOL ascendingCol;
2008 SCCOL descendingCol;
2009 OUString ascendingRangeName;
2010 OUString descendingRangeName;
2012 const std::vector<std::vector<const char*>> data = {
2013 { "1", "9" }, // 0
2014 { "2", "9" }, // 1
2015 { "4", "5" }, // 2
2016 { "5", "5" }, // 3
2017 { "5", "5" }, // 4
2018 { "5", "5" }, // 5
2019 { "5", "5" }, // 6
2020 { "5", "4" }, // 7
2021 { "5", "4" }, // 8
2022 { "9", "2" }, // 9
2023 { "9", "1" }, // 10
2025 ascendingCol = 0;
2026 descendingCol = 1;
2027 ascendingRangeName = u"$A$1:$A$" + OUString::number(data.size());
2028 descendingRangeName = u"$B$1:$B$" + OUString::number(data.size());
2030 ScAddress pos(0,0,0);
2031 range = insertRangeData(m_pDoc, pos, data);
2032 CPPUNIT_ASSERT_EQUAL( ScRange( 0, 0, 0, data[ 0 ].size() - 1, data.size() - 1, 0 ), range );
2036 // This should return the last 5.
2037 m_pDoc->SetFormula( formulaAddress, "=MATCH(5;" + ascendingRangeName + ";1)",
2038 formula::FormulaGrammar::GRAM_NATIVE_UI);
2039 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue( formulaAddress ));
2041 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS_EQUAL, 5 );
2042 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2043 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2044 CPPUNIT_ASSERT_EQUAL(SCROW(8), it.GetRow());
2047 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS, 5 );
2048 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2049 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2050 CPPUNIT_ASSERT_EQUAL(SCROW(2), it.GetRow());
2053 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_EQUAL, 5 );
2054 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2055 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2056 CPPUNIT_ASSERT_EQUAL(SCROW(8), it.GetRow());
2060 // Descending, this should return the last 5.
2061 m_pDoc->SetFormula( formulaAddress, "=MATCH(5;" + descendingRangeName + ";-1)",
2062 formula::FormulaGrammar::GRAM_NATIVE_UI);
2063 CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue( formulaAddress ));
2065 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER_EQUAL, 5 );
2066 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2067 CPPUNIT_ASSERT(it.BinarySearch( descendingCol ));
2068 CPPUNIT_ASSERT_EQUAL(SCROW(6), it.GetRow());
2071 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER, 5 );
2072 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2073 CPPUNIT_ASSERT(it.BinarySearch( descendingCol ));
2074 CPPUNIT_ASSERT_EQUAL(SCROW(1), it.GetRow());
2078 // There's no 6, so this should return the last 5.
2079 m_pDoc->SetFormula( formulaAddress, "=MATCH(6;" + ascendingRangeName + ";1)",
2080 formula::FormulaGrammar::GRAM_NATIVE_UI);
2081 CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue( formulaAddress ));
2083 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS_EQUAL, 6 );
2084 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2085 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2086 CPPUNIT_ASSERT_EQUAL(SCROW(8), it.GetRow());
2089 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS, 6 );
2090 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2091 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2092 CPPUNIT_ASSERT_EQUAL(SCROW(8), it.GetRow());
2095 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_EQUAL, 6 );
2096 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2097 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2098 CPPUNIT_ASSERT_EQUAL(SCROW(8), it.GetRow());
2102 // Descending, there's no 6, so this should return the last 9.
2103 m_pDoc->SetFormula( formulaAddress, "=MATCH(6;" + descendingRangeName + ";-1)",
2104 formula::FormulaGrammar::GRAM_NATIVE_UI);
2105 CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue( formulaAddress ));
2107 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER_EQUAL, 6 );
2108 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2109 CPPUNIT_ASSERT(it.BinarySearch( descendingCol ));
2110 CPPUNIT_ASSERT_EQUAL(SCROW(1), it.GetRow());
2113 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER, 6 );
2114 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2115 CPPUNIT_ASSERT(it.BinarySearch( descendingCol ));
2116 CPPUNIT_ASSERT_EQUAL(SCROW(1), it.GetRow());
2120 // All values are larger than 0, so there should be no match.
2121 m_pDoc->SetFormula( formulaAddress, "=MATCH(0;" + ascendingRangeName + ";1)",
2122 formula::FormulaGrammar::GRAM_NATIVE_UI);
2123 CPPUNIT_ASSERT_EQUAL( FormulaError::NotAvailable, m_pDoc->GetErrCode( formulaAddress ));
2125 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS_EQUAL, 0 );
2126 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2127 CPPUNIT_ASSERT(!it.BinarySearch( ascendingCol ));
2128 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2131 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS, 0 );
2132 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2133 CPPUNIT_ASSERT(!it.BinarySearch( ascendingCol ));
2134 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2137 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_EQUAL, 0 );
2138 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2139 CPPUNIT_ASSERT(!it.BinarySearch( ascendingCol ));
2140 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2144 // Descending, all values are larger than 0, so this should return the last item.
2145 m_pDoc->SetFormula( formulaAddress, "=MATCH(0;" + descendingRangeName + ";-1)",
2146 formula::FormulaGrammar::GRAM_NATIVE_UI);
2147 CPPUNIT_ASSERT_EQUAL( 11.0, m_pDoc->GetValue( formulaAddress ));
2149 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER_EQUAL, 0 );
2150 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2151 CPPUNIT_ASSERT(it.BinarySearch( descendingCol ));
2152 CPPUNIT_ASSERT_EQUAL(SCROW(10), it.GetRow());
2155 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER, 0 );
2156 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2157 CPPUNIT_ASSERT(it.BinarySearch( descendingCol ));
2158 CPPUNIT_ASSERT_EQUAL(SCROW(10), it.GetRow());
2162 // All values are smaller than 10, so this should return the last item.
2163 m_pDoc->SetFormula( formulaAddress, "=MATCH(10;" + ascendingRangeName + ";1)",
2164 formula::FormulaGrammar::GRAM_NATIVE_UI);
2165 CPPUNIT_ASSERT_EQUAL( 11.0, m_pDoc->GetValue( formulaAddress ));
2167 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS_EQUAL, 10 );
2168 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2169 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2170 CPPUNIT_ASSERT_EQUAL(SCROW(10), it.GetRow());
2173 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_LESS, 10 );
2174 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2175 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2176 CPPUNIT_ASSERT_EQUAL(SCROW(10), it.GetRow());
2179 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_EQUAL, 10 );
2180 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2181 CPPUNIT_ASSERT(it.BinarySearch( ascendingCol ));
2182 CPPUNIT_ASSERT_EQUAL(SCROW(10), it.GetRow());
2186 // Descending, all values are smaller than 10, so there should be no match.
2187 m_pDoc->SetFormula( formulaAddress, "=MATCH(10;" + descendingRangeName + ";-1)",
2188 formula::FormulaGrammar::GRAM_NATIVE_UI);
2189 CPPUNIT_ASSERT_EQUAL( FormulaError::NotAvailable, m_pDoc->GetErrCode( formulaAddress ));
2191 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER_EQUAL, 10 );
2192 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2193 CPPUNIT_ASSERT(!it.BinarySearch( descendingCol ));
2194 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2197 ScQueryParam param = makeSearchParam( range, descendingCol, SC_GREATER, 10 );
2198 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2199 CPPUNIT_ASSERT(!it.BinarySearch( descendingCol ));
2200 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2204 // Search as ascending but use descending range, will return no match.
2205 ScQueryParam param = makeSearchParam( range, descendingCol, SC_LESS_EQUAL, 1 );
2206 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2207 CPPUNIT_ASSERT(!it.BinarySearch( descendingCol ));
2208 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2212 // Search as descending but use ascending range, will return no match.
2213 ScQueryParam param = makeSearchParam( range, ascendingCol, SC_GREATER_EQUAL, 9 );
2214 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2215 CPPUNIT_ASSERT(!it.BinarySearch( ascendingCol ));
2216 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2220 // SC_EQUAL with descending is considered an error, will return no match.
2221 ScQueryParam param = makeSearchParam( range, descendingCol, SC_EQUAL, 9 );
2222 TestQueryIterator it( *m_pDoc, m_pDoc->GetNonThreadedContext(), 0, param, false );
2223 CPPUNIT_ASSERT(!it.BinarySearch( descendingCol ));
2224 CPPUNIT_ASSERT_EQUAL(SCROW(0), it.GetRow());
2227 m_pDoc->DeleteTab(0);
2230 CPPUNIT_PLUGIN_IMPLEMENT();
2232 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */