1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "helper/sorthelper.hxx"
11 #include "helper/debughelper.hxx"
12 #include "helper/qahelper.hxx"
15 #include <sortparam.hxx>
17 #include <formulacell.hxx>
18 #include <scopetools.hxx>
19 #include <globalnames.hxx>
20 #include <dbdocfun.hxx>
21 #include <docfunc.hxx>
22 #include <scitems.hxx>
23 #include <editutil.hxx>
24 #include <drwlayer.hxx>
25 #include <queryiter.hxx>
26 #include <undomanager.hxx>
28 #include <editeng/wghtitem.hxx>
29 #include <editeng/postitem.hxx>
30 #include <svx/svdocirc.hxx>
31 #include <svx/svdpage.hxx>
32 #include <rtl/math.hxx>
37 class TestSort
: public ScUcalcTestBase
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
= {
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
;
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
);
90 clearRange(m_pDoc
, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
93 const std::vector
<std::vector
<const char*>> aData
= {
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
119 CPPUNIT_ASSERT_EQUAL(u
"Title"_ustr
, m_pDoc
->GetString(aPos
));
121 CPPUNIT_ASSERT_EQUAL(u
"1"_ustr
, m_pDoc
->GetString(aPos
));
123 CPPUNIT_ASSERT_EQUAL(u
"9"_ustr
, m_pDoc
->GetString(aPos
));
125 CPPUNIT_ASSERT_EQUAL(u
"12"_ustr
, m_pDoc
->GetString(aPos
));
127 CPPUNIT_ASSERT_EQUAL(u
"123"_ustr
, m_pDoc
->GetString(aPos
));
129 CPPUNIT_ASSERT_EQUAL(u
"b"_ustr
, m_pDoc
->GetString(aPos
));
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.
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
;
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
);
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)));
261 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
262 CPPUNIT_ASSERT(pUndoMgr
);
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)));
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
;
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);
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.
335 aSortData
.bByRow
= false;
336 bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
337 CPPUNIT_ASSERT(bSorted
);
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)));
349 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
350 CPPUNIT_ASSERT(pUndoMgr
);
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)));
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] = {
399 const char* aResults
[6] = {
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
;
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)));
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 {
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
;
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
[] = {
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
)
545 bool isBold( const ScPatternAttr
* pPat
) const
549 cerr
<< "Pattern is NULL!" << endl
;
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
;
560 CPPUNIT_ASSERT(pItem
);
562 if (static_cast<const SvxWeightItem
*>(pItem
)->GetEnumValue() != WEIGHT_BOLD
)
564 cerr
<< "Font weight should be bold." << endl
;
571 bool isItalic( const ScPatternAttr
* pPat
) const
575 cerr
<< "Pattern is NULL!" << endl
;
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
;
586 CPPUNIT_ASSERT(pItem
);
588 if (static_cast<const SvxPostureItem
*>(pItem
)->GetEnumValue() != ITALIC_NORMAL
)
590 cerr
<< "Italic should be applied.." << endl
;
597 bool isNormal( const ScPatternAttr
* pPat
) const
601 cerr
<< "Pattern is NULL!" << endl
;
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
;
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
;
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
;
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
;
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));
778 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
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));
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));
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
);
867 const char* aData
[][2] = {
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
;
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)));
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();
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)));
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
[] = {
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
);
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
;
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.
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();
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.
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
= {
1057 { "Student1", "1" },
1058 { "Student2", "2" },
1059 { "Student3", "3" },
1060 { "Student4", "4" },
1061 { "Student5", "5" },
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
= {
1074 { "=Lesson1.A2", "3" },
1075 { "=Lesson1.A3", "4" },
1076 { "=Lesson1.A4", "9" },
1077 { "=Lesson1.A5", "6" },
1078 { "=Lesson1.A6", "3" },
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
);
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)" },
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.
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();
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.
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.
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.
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" },
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
);
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.
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
[] = {
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();
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.
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" },
1373 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1374 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1377 ScDBDocFunc
aFunc(*m_xDocShell
);
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" },
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));
1416 SfxUndoManager
* pUndoMgr
= m_pDoc
->GetUndoManager();
1417 CPPUNIT_ASSERT(pUndoMgr
);
1422 // Expected output table content. 0 = empty cell
1423 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1424 { "Order", "Value", "1" },
1430 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "After undo");
1431 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1437 // Expected output table content. 0 = empty cell
1438 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1439 { "Order", "Value", "1" },
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" },
1461 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Change the header value");
1462 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
1468 // Expected output table content. 0 = empty cell
1469 std::vector
<std::vector
<const char*>> aOutputCheck
= {
1470 { "Order", "Value", "1" },
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
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
);
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
= {
1584 { nullptr, nullptr },
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
= {
1600 { nullptr, nullptr },
1607 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1608 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
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
= {
1637 { nullptr, nullptr },
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
= {
1711 bool bSuccess
= checkOutput(m_pDoc
, aDataRange
, aOutputCheck
, "Initial value");
1712 CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess
);
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
= {
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
[] = {
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] = {
1839 { "43", "=RC[-1]" },
1840 { "50", "=RC[-1]" },
1842 { "47", "=RC[-1]" },
1843 { "28", "=RC[-1]" },
1844 { nullptr, nullptr } // terminator
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.
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)));
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
= {
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());
1933 ScDrawLayer::SetCellAnchoredFromPosition(*pObj
, *m_pDoc
, 0, false);
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);
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);
1969 class TestQueryIterator
1970 : public ScQueryCellIteratorBase
< ScQueryCellIteratorAccess::Direct
, ScQueryCellIteratorType::Generic
>
1972 typedef ScQueryCellIteratorBase
< ScQueryCellIteratorAccess::Direct
, ScQueryCellIteratorType::Generic
> Base
;
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
)
1986 param
.nCol1
= param
.nCol2
= col
;
1987 param
.nRow1
= range
.aStart
.Row();
1988 param
.nRow2
= range
.aEnd
.Row();
1990 ScQueryEntry
& entry
= param
.GetEntry(0);
1991 ScQueryEntry::Item
& item
= entry
.GetQueryItem();
1992 entry
.bDoQuery
= true;
1995 item
.meType
= ScQueryEntry::ByValue
;
2001 CPPUNIT_TEST_FIXTURE(TestSort
, testQueryBinarySearch
)
2003 m_pDoc
->InsertTab(0, u
"testQueryBinarySearch"_ustr
);
2005 const ScAddress
formulaAddress( 10, 0, 0 );
2008 SCCOL descendingCol
;
2009 OUString ascendingRangeName
;
2010 OUString descendingRangeName
;
2012 const std::vector
<std::vector
<const char*>> data
= {
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: */