File indexing completed on 2024-09-15 03:33:12
0001 /* 0002 File : SpreadsheetTest.cpp 0003 Project : LabPlot 0004 Description : Tests for the Spreadsheet 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2020-2023 Alexander Semke <alexander.semke@web.de> 0007 SPDX-FileCopyrightText: 2022 Stefan Gerlach <stefan.gerlach@uni.kn> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "SpreadsheetTest.h" 0013 #include "backend/core/Project.h" 0014 #include "backend/core/datatypes/DateTime2StringFilter.h" 0015 #include "backend/datasources/filters/VectorBLFFilter.h" 0016 #include "backend/spreadsheet/Spreadsheet.h" 0017 #include "backend/spreadsheet/SpreadsheetModel.h" 0018 #include "backend/spreadsheet/StatisticsSpreadsheet.h" 0019 #include "commonfrontend/ProjectExplorer.h" 0020 #include "commonfrontend/spreadsheet/SpreadsheetView.h" 0021 #include "kdefrontend/dockwidgets/SpreadsheetDock.h" 0022 #include "kdefrontend/spreadsheet/FlattenColumnsDialog.h" 0023 #include "kdefrontend/spreadsheet/SearchReplaceWidget.h" 0024 0025 #ifdef HAVE_VECTOR_BLF 0026 #include <Vector/BLF.h> 0027 #endif 0028 0029 #include <QClipboard> 0030 #include <QModelIndex> 0031 #include <QRandomGenerator> 0032 #include <QUndoStack> 0033 0034 //********************************************************** 0035 //****************** Copy&Paste tests ********************** 0036 //********************************************************** 0037 0038 //********************************************************** 0039 //********** Handling of different columns modes *********** 0040 //********************************************************** 0041 /*! 0042 insert two columns with float values into an empty spreadsheet 0043 */ 0044 void SpreadsheetTest::testCopyPasteColumnMode00() { 0045 QLocale::setDefault(QLocale::C); // . as decimal separator 0046 Spreadsheet sheet(QStringLiteral("test"), false); 0047 sheet.setColumnCount(2); 0048 sheet.setRowCount(100); 0049 0050 const QString str = QStringLiteral("10.0 100.0\n20.0 200.0"); 0051 QApplication::clipboard()->setText(str); 0052 0053 SpreadsheetView view(&sheet, false); 0054 view.pasteIntoSelection(); 0055 0056 // spreadsheet size 0057 QCOMPARE(sheet.columnCount(), 2); 0058 QCOMPARE(sheet.rowCount(), 100); 0059 0060 // column modes 0061 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::Double); 0062 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0063 0064 // values 0065 QCOMPARE(sheet.column(0)->valueAt(0), 10.0); 0066 QCOMPARE(sheet.column(1)->valueAt(0), 100.0); 0067 QCOMPARE(sheet.column(0)->valueAt(1), 20.0); 0068 QCOMPARE(sheet.column(1)->valueAt(1), 200.0); 0069 } 0070 0071 /*! 0072 insert one column with integer and one column with big integer values into an empty spreadsheet. 0073 the first column has to be converted to integer column, the second to big integer. 0074 */ 0075 void SpreadsheetTest::testCopyPasteColumnMode01() { 0076 Spreadsheet sheet(QStringLiteral("test"), false); 0077 sheet.setColumnCount(2); 0078 sheet.setRowCount(100); 0079 0080 const QString str = QStringLiteral("10 ") + QString::number(std::numeric_limits<long long>::min()) + QStringLiteral("\n20 ") 0081 + QString::number(std::numeric_limits<long long>::max()); 0082 QApplication::clipboard()->setText(str); 0083 0084 SpreadsheetView view(&sheet, false); 0085 view.pasteIntoSelection(); 0086 0087 // spreadsheet size 0088 QCOMPARE(sheet.columnCount(), 2); 0089 QCOMPARE(sheet.rowCount(), 100); 0090 0091 // column modes 0092 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::Integer); 0093 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::BigInt); 0094 0095 // values 0096 QCOMPARE(sheet.column(0)->integerAt(0), 10); 0097 QCOMPARE(sheet.column(1)->bigIntAt(0), std::numeric_limits<long long>::min()); 0098 QCOMPARE(sheet.column(0)->integerAt(1), 20); 0099 QCOMPARE(sheet.column(1)->bigIntAt(1), std::numeric_limits<long long>::max()); 0100 } 0101 0102 /*! 0103 insert one column with integer values and one column with float numbers into an empty spreadsheet. 0104 the first column has to be converted to integer column, the second to float. 0105 */ 0106 void SpreadsheetTest::testCopyPasteColumnMode02() { 0107 QLocale::setDefault(QLocale::C); // . as decimal separator 0108 Spreadsheet sheet(QStringLiteral("test"), false); 0109 sheet.setColumnCount(2); 0110 sheet.setRowCount(100); 0111 0112 const QString str = QStringLiteral("10 100.0\n20 200.0"); 0113 QApplication::clipboard()->setText(str); 0114 0115 SpreadsheetView view(&sheet, false); 0116 view.pasteIntoSelection(); 0117 0118 // spreadsheet size 0119 QCOMPARE(sheet.columnCount(), 2); 0120 QCOMPARE(sheet.rowCount(), 100); 0121 0122 // column modes 0123 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::Integer); 0124 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0125 0126 // values 0127 QCOMPARE(sheet.column(0)->integerAt(0), 10); 0128 QCOMPARE(sheet.column(1)->valueAt(0), 100.0); 0129 QCOMPARE(sheet.column(0)->integerAt(1), 20); 0130 QCOMPARE(sheet.column(1)->valueAt(1), 200.0); 0131 } 0132 0133 /*! 0134 Properly handle empty values in the tab separated data. 0135 */ 0136 void SpreadsheetTest::testCopyPasteColumnMode03() { 0137 QLocale::setDefault(QLocale::C); // . as decimal separator 0138 Spreadsheet sheet(QStringLiteral("test"), false); 0139 sheet.setColumnCount(2); 0140 sheet.setRowCount(100); 0141 0142 const QString str = QStringLiteral( 0143 "1000 1000 1000\n" 0144 "985 985 985\n" 0145 "970 -7.06562 970 970\n" 0146 "955 -5.93881 955 955\n" 0147 "940 -4.97594 940 -4.97594 940"); 0148 0149 QApplication::clipboard()->setText(str); 0150 0151 SpreadsheetView view(&sheet, false); 0152 view.pasteIntoSelection(); 0153 0154 // spreadsheet size 0155 QCOMPARE(sheet.columnCount(), 5); 0156 QCOMPARE(sheet.rowCount(), 100); 0157 0158 // column modes 0159 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::Integer); 0160 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0161 QCOMPARE(sheet.column(2)->columnMode(), AbstractColumn::ColumnMode::Integer); 0162 QCOMPARE(sheet.column(3)->columnMode(), AbstractColumn::ColumnMode::Double); 0163 QCOMPARE(sheet.column(4)->columnMode(), AbstractColumn::ColumnMode::Integer); 0164 0165 // values 0166 QCOMPARE(sheet.column(0)->integerAt(0), 1000); 0167 QCOMPARE((bool)std::isnan(sheet.column(1)->valueAt(0)), true); 0168 QCOMPARE(sheet.column(2)->integerAt(0), 1000); 0169 QCOMPARE((bool)std::isnan(sheet.column(3)->valueAt(0)), true); 0170 QCOMPARE(sheet.column(4)->integerAt(0), 1000); 0171 0172 QCOMPARE(sheet.column(0)->integerAt(1), 985); 0173 QCOMPARE((bool)std::isnan(sheet.column(1)->valueAt(1)), true); 0174 QCOMPARE(sheet.column(2)->integerAt(1), 985); 0175 QCOMPARE((bool)std::isnan(sheet.column(3)->valueAt(1)), true); 0176 QCOMPARE(sheet.column(4)->integerAt(1), 985); 0177 0178 QCOMPARE(sheet.column(0)->integerAt(2), 970); 0179 QCOMPARE(sheet.column(1)->valueAt(2), -7.06562); 0180 QCOMPARE(sheet.column(2)->integerAt(2), 970); 0181 QCOMPARE((bool)std::isnan(sheet.column(3)->valueAt(2)), true); 0182 QCOMPARE(sheet.column(4)->integerAt(2), 970); 0183 0184 QCOMPARE(sheet.column(0)->integerAt(3), 955); 0185 QCOMPARE(sheet.column(1)->valueAt(3), -5.93881); 0186 QCOMPARE(sheet.column(2)->integerAt(3), 955); 0187 QCOMPARE((bool)std::isnan(sheet.column(3)->valueAt(3)), true); 0188 QCOMPARE(sheet.column(4)->integerAt(3), 955); 0189 0190 QCOMPARE(sheet.column(0)->integerAt(4), 940); 0191 QCOMPARE(sheet.column(1)->valueAt(4), -4.97594); 0192 QCOMPARE(sheet.column(2)->integerAt(4), 940); 0193 QCOMPARE(sheet.column(1)->valueAt(4), -4.97594); 0194 QCOMPARE(sheet.column(4)->integerAt(4), 940); 0195 } 0196 0197 /*! 0198 automatically detect the proper format for the datetime columns 0199 */ 0200 void SpreadsheetTest::testCopyPasteColumnMode04() { 0201 QLocale::setDefault(QLocale::C); // . as decimal separator 0202 Spreadsheet sheet(QStringLiteral("test"), false); 0203 sheet.setColumnCount(2); 0204 sheet.setRowCount(100); 0205 0206 const QString str = QStringLiteral( 0207 "2020-09-20 11:21:40:849 7.7\n" 0208 "2020-09-20 11:21:41:830 4.2"); 0209 0210 QApplication::clipboard()->setText(str); 0211 0212 SpreadsheetView view(&sheet, false); 0213 view.pasteIntoSelection(); 0214 0215 // spreadsheet size 0216 QCOMPARE(sheet.columnCount(), 2); 0217 QCOMPARE(sheet.rowCount(), 100); 0218 0219 // column modes 0220 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::DateTime); 0221 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0222 0223 // values 0224 auto* filter = static_cast<DateTime2StringFilter*>(sheet.column(0)->outputFilter()); 0225 const QString& format = filter->format(); 0226 0227 QCOMPARE(sheet.column(0)->dateTimeAt(0).toString(format), QLatin1String("2020-09-20 11:21:40:849")); 0228 QCOMPARE(sheet.column(1)->valueAt(0), 7.7); 0229 0230 QCOMPARE(sheet.column(0)->dateTimeAt(1).toString(format), QLatin1String("2020-09-20 11:21:41:830")); 0231 QCOMPARE(sheet.column(1)->valueAt(1), 4.2); 0232 } 0233 0234 /*! 0235 automatically detect the proper format for the datetime columns, time part only 0236 */ 0237 void SpreadsheetTest::testCopyPasteColumnMode05() { 0238 QLocale::setDefault(QLocale::C); // . as decimal separator 0239 Spreadsheet sheet(QStringLiteral("test"), false); 0240 sheet.setColumnCount(2); 0241 sheet.setRowCount(100); 0242 0243 const QString str = QStringLiteral( 0244 "11:21:40 7.7\n" 0245 "11:21:41 4.2"); 0246 0247 QApplication::clipboard()->setText(str); 0248 0249 SpreadsheetView view(&sheet, false); 0250 view.pasteIntoSelection(); 0251 0252 // spreadsheet size 0253 QCOMPARE(sheet.columnCount(), 2); 0254 QCOMPARE(sheet.rowCount(), 100); 0255 0256 // column modes 0257 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::DateTime); 0258 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0259 0260 // values 0261 auto* filter = static_cast<DateTime2StringFilter*>(sheet.column(0)->outputFilter()); 0262 const QString& format = filter->format(); 0263 0264 QCOMPARE(sheet.column(0)->dateTimeAt(0).toString(format), QLatin1String("11:21:40")); 0265 QCOMPARE(sheet.column(1)->valueAt(0), 7.7); 0266 0267 QCOMPARE(sheet.column(0)->dateTimeAt(1).toString(format), QLatin1String("11:21:41")); 0268 QCOMPARE(sheet.column(1)->valueAt(1), 4.2); 0269 } 0270 0271 /*! 0272 automatically detect the proper format for the datetime columns having the format "yyyy-MM-dd hh:mm:ss" 0273 */ 0274 void SpreadsheetTest::testCopyPasteColumnMode06() { 0275 Spreadsheet sheet(QStringLiteral("test"), false); 0276 sheet.setColumnCount(2); 0277 sheet.setRowCount(100); 0278 0279 const QString str = QStringLiteral( 0280 "2018-03-21 10:00:00 1\n" 0281 "2018-03-21 10:30:00 2"); 0282 0283 QApplication::clipboard()->setText(str); 0284 0285 SpreadsheetView view(&sheet, false); 0286 view.pasteIntoSelection(); 0287 0288 // spreadsheet size 0289 QCOMPARE(sheet.columnCount(), 2); 0290 QCOMPARE(sheet.rowCount(), 100); 0291 0292 // column modes 0293 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::DateTime); 0294 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Integer); 0295 0296 // values 0297 auto* filter = static_cast<DateTime2StringFilter*>(sheet.column(0)->outputFilter()); 0298 const QString& format = filter->format(); 0299 0300 QCOMPARE(sheet.column(0)->dateTimeAt(0).toString(format), QLatin1String("2018-03-21 10:00:00")); 0301 QCOMPARE(sheet.column(1)->integerAt(0), 1); 0302 0303 QCOMPARE(sheet.column(0)->dateTimeAt(1).toString(format), QLatin1String("2018-03-21 10:30:00")); 0304 QCOMPARE(sheet.column(1)->integerAt(1), 2); 0305 } 0306 0307 //********************************************************** 0308 //********* Handling of spreadsheet size changes *********** 0309 //********************************************************** 0310 /*! 0311 insert irregular data, new columns should be added appropriately. 0312 */ 0313 void SpreadsheetTest::testCopyPasteSizeChange00() { 0314 Project project; 0315 Spreadsheet* sheet = new Spreadsheet(QStringLiteral("test"), false); 0316 project.addChild(sheet); 0317 sheet->setColumnCount(2); 0318 sheet->setRowCount(100); 0319 0320 const QString str = QStringLiteral( 0321 "0\n" 0322 "10 20\n" 0323 "11 21 31\n" 0324 "12 22 32 42\n" 0325 "13 23\n" 0326 "14"); 0327 QApplication::clipboard()->setText(str); 0328 0329 SpreadsheetView view(sheet, false); 0330 view.pasteIntoSelection(); 0331 0332 // spreadsheet size 0333 QCOMPARE(sheet->columnCount(), 4); 0334 QCOMPARE(sheet->rowCount(), 100); 0335 0336 // column modes 0337 QCOMPARE(sheet->column(0)->columnMode(), AbstractColumn::ColumnMode::Integer); 0338 QCOMPARE(sheet->column(1)->columnMode(), AbstractColumn::ColumnMode::Integer); 0339 QCOMPARE(sheet->column(2)->columnMode(), AbstractColumn::ColumnMode::Integer); 0340 QCOMPARE(sheet->column(3)->columnMode(), AbstractColumn::ColumnMode::Integer); 0341 0342 // values 0343 QCOMPARE(sheet->column(0)->integerAt(0), 0); 0344 QCOMPARE(sheet->column(1)->integerAt(0), 0); 0345 QCOMPARE(sheet->column(2)->integerAt(0), 0); 0346 QCOMPARE(sheet->column(3)->integerAt(0), 0); 0347 0348 QCOMPARE(sheet->column(0)->integerAt(1), 10); 0349 QCOMPARE(sheet->column(1)->integerAt(1), 20); 0350 QCOMPARE(sheet->column(2)->integerAt(1), 0); 0351 QCOMPARE(sheet->column(3)->integerAt(1), 0); 0352 0353 QCOMPARE(sheet->column(0)->integerAt(2), 11); 0354 QCOMPARE(sheet->column(1)->integerAt(2), 21); 0355 QCOMPARE(sheet->column(2)->integerAt(2), 31); 0356 QCOMPARE(sheet->column(3)->integerAt(2), 0); 0357 0358 QCOMPARE(sheet->column(0)->integerAt(3), 12); 0359 QCOMPARE(sheet->column(1)->integerAt(3), 22); 0360 QCOMPARE(sheet->column(2)->integerAt(3), 32); 0361 QCOMPARE(sheet->column(3)->integerAt(3), 42); 0362 0363 QCOMPARE(sheet->column(0)->integerAt(4), 13); 0364 QCOMPARE(sheet->column(1)->integerAt(4), 23); 0365 QCOMPARE(sheet->column(2)->integerAt(4), 0); 0366 QCOMPARE(sheet->column(3)->integerAt(4), 0); 0367 0368 QCOMPARE(sheet->column(0)->integerAt(5), 14); 0369 QCOMPARE(sheet->column(1)->integerAt(5), 0); 0370 QCOMPARE(sheet->column(2)->integerAt(5), 0); 0371 QCOMPARE(sheet->column(3)->integerAt(5), 0); 0372 0373 // undo the changes and check the results again 0374 project.undoStack()->undo(); 0375 0376 // spreadsheet size 0377 QCOMPARE(sheet->columnCount(), 2); 0378 QCOMPARE(sheet->rowCount(), 100); 0379 0380 // column modes 0381 QCOMPARE(sheet->column(0)->columnMode(), AbstractColumn::ColumnMode::Double); 0382 QCOMPARE(sheet->column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0383 0384 // values 0385 QCOMPARE((bool)std::isnan(sheet->column(0)->valueAt(0)), true); 0386 QCOMPARE((bool)std::isnan(sheet->column(1)->valueAt(0)), true); 0387 0388 QCOMPARE((bool)std::isnan(sheet->column(0)->valueAt(1)), true); 0389 QCOMPARE((bool)std::isnan(sheet->column(1)->valueAt(1)), true); 0390 0391 QCOMPARE((bool)std::isnan(sheet->column(0)->valueAt(2)), true); 0392 QCOMPARE((bool)std::isnan(sheet->column(1)->valueAt(2)), true); 0393 0394 QCOMPARE((bool)std::isnan(sheet->column(0)->valueAt(3)), true); 0395 QCOMPARE((bool)std::isnan(sheet->column(1)->valueAt(3)), true); 0396 0397 QCOMPARE((bool)std::isnan(sheet->column(0)->valueAt(4)), true); 0398 QCOMPARE((bool)std::isnan(sheet->column(1)->valueAt(4)), true); 0399 0400 QCOMPARE((bool)std::isnan(sheet->column(0)->valueAt(5)), true); 0401 QCOMPARE((bool)std::isnan(sheet->column(1)->valueAt(5)), true); 0402 } 0403 0404 void SpreadsheetTest::testCopyPasteUtf8() { 0405 Spreadsheet sheet(QStringLiteral("test"), false); 0406 sheet.setColumnCount(2); 0407 sheet.setRowCount(100); 0408 0409 const QString str = QString::fromUtf8("тест1 1\nтест2 2"); 0410 0411 QApplication::clipboard()->setText(str); 0412 0413 SpreadsheetView view(&sheet, false); 0414 view.pasteIntoSelection(); 0415 0416 // data types 0417 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::Text); 0418 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Integer); 0419 0420 // values 0421 QCOMPARE(sheet.column(0)->textAt(0), QString::fromUtf8("тест1")); 0422 QCOMPARE(sheet.column(1)->integerAt(0), 1); 0423 0424 QCOMPARE(sheet.column(0)->textAt(1), QString::fromUtf8("тест2")); 0425 QCOMPARE(sheet.column(1)->integerAt(1), 2); 0426 } 0427 /*! 0428 insert the data at the edge of the spreadsheet and paste the data. 0429 the spreadsheet has to be extended accordingly 0430 */ 0431 void SpreadsheetTest::testCopyPasteSizeChange01() { 0432 QLocale::setDefault(QLocale::C); // . as decimal separator 0433 Spreadsheet sheet(QStringLiteral("test"), false); 0434 sheet.setColumnCount(2); 0435 sheet.setRowCount(100); 0436 0437 const QString str = QStringLiteral( 0438 "1.1 2.2\n" 0439 "3.3 4.4"); 0440 QApplication::clipboard()->setText(str); 0441 0442 SpreadsheetView view(&sheet, false); 0443 view.goToCell(1, 1); // navigate to the edge of the spreadsheet 0444 view.pasteIntoSelection(); 0445 0446 // spreadsheet size 0447 QCOMPARE(sheet.columnCount(), 3); 0448 QCOMPARE(sheet.rowCount(), 100); 0449 0450 // column modes 0451 QCOMPARE(sheet.column(0)->columnMode(), AbstractColumn::ColumnMode::Double); 0452 QCOMPARE(sheet.column(1)->columnMode(), AbstractColumn::ColumnMode::Double); 0453 QCOMPARE(sheet.column(2)->columnMode(), AbstractColumn::ColumnMode::Double); 0454 0455 // values 0456 QCOMPARE((bool)std::isnan(sheet.column(0)->valueAt(0)), true); 0457 QCOMPARE((bool)std::isnan(sheet.column(1)->valueAt(0)), true); 0458 QCOMPARE((bool)std::isnan(sheet.column(2)->valueAt(0)), true); 0459 0460 QCOMPARE((bool)std::isnan(sheet.column(0)->valueAt(1)), true); 0461 QCOMPARE(sheet.column(1)->valueAt(1), 1.1); 0462 QCOMPARE(sheet.column(2)->valueAt(1), 2.2); 0463 0464 QCOMPARE((bool)std::isnan(sheet.column(0)->valueAt(2)), true); 0465 QCOMPARE(sheet.column(1)->valueAt(2), 3.3); 0466 QCOMPARE(sheet.column(2)->valueAt(2), 4.4); 0467 } 0468 0469 // ********************************************************** 0470 // *********************** sorting ************************* 0471 // ********************************************************** 0472 // single column 0473 0474 /* 0475 * check sorting single column of double values with NaN ascending 0476 */ 0477 void SpreadsheetTest::testSortSingleNumeric1() { 0478 const QVector<double> xData{0.5, -0.2, GSL_NAN, 2.0, -1.0}; 0479 0480 Spreadsheet sheet(QStringLiteral("test"), false); 0481 sheet.setColumnCount(1); 0482 sheet.setRowCount(7); 0483 auto* col = sheet.column(0); 0484 col->replaceValues(0, xData); 0485 0486 // sort 0487 sheet.sortColumns(nullptr, {col}, true); 0488 0489 // values 0490 QCOMPARE(col->valueAt(0), -1.0); 0491 QCOMPARE(col->valueAt(1), -0.2); 0492 QCOMPARE(col->valueAt(2), 0.5); 0493 QCOMPARE(col->valueAt(3), 2.0); 0494 } 0495 0496 /* 0497 * check sorting single column of double values with NaN descending 0498 */ 0499 void SpreadsheetTest::testSortSingleNumeric2() { 0500 const QVector<double> xData{0.5, -0.2, GSL_NAN, 2.0, -1.0}; 0501 0502 Spreadsheet sheet(QStringLiteral("test"), false); 0503 sheet.setColumnCount(1); 0504 sheet.setRowCount(7); 0505 auto* col = sheet.column(0); 0506 col->replaceValues(0, xData); 0507 0508 // sort 0509 sheet.sortColumns(nullptr, {col}, false); 0510 0511 // values 0512 QCOMPARE(col->valueAt(0), 2.0); 0513 QCOMPARE(col->valueAt(1), 0.5); 0514 QCOMPARE(col->valueAt(2), -0.2); 0515 QCOMPARE(col->valueAt(3), -1.0); 0516 } 0517 0518 /* 0519 * check sorting single column of integer values with empty entries ascending 0520 */ 0521 void SpreadsheetTest::testSortSingleInteger1() { 0522 const QVector<int> xData1{4, 5, 2}; 0523 const QVector<int> xData2{3, 6, -1}; 0524 0525 Spreadsheet sheet(QStringLiteral("test"), false); 0526 sheet.setColumnCount(1); 0527 sheet.setRowCount(7); 0528 auto* col = sheet.column(0); 0529 col->setColumnMode(AbstractColumn::ColumnMode::Integer); 0530 col->replaceInteger(0, xData1); 0531 col->replaceInteger(4, xData2); 0532 0533 // sort 0534 sheet.sortColumns(nullptr, {col}, true); 0535 0536 // values 0537 QCOMPARE(col->integerAt(0), -1); 0538 QCOMPARE(col->integerAt(1), 0); 0539 QCOMPARE(col->integerAt(2), 2); 0540 QCOMPARE(col->integerAt(3), 3); 0541 QCOMPARE(col->integerAt(4), 4); 0542 QCOMPARE(col->integerAt(5), 5); 0543 QCOMPARE(col->integerAt(6), 6); 0544 } 0545 0546 /* 0547 * check sorting single column of integer values with empty entries ascending 0548 */ 0549 void SpreadsheetTest::testSortSingleInteger2() { 0550 const QVector<int> xData1{4, 5, 2}; 0551 const QVector<int> xData2{3, 6, -1}; 0552 0553 Spreadsheet sheet(QStringLiteral("test"), false); 0554 sheet.setColumnCount(1); 0555 sheet.setRowCount(7); 0556 auto* col = sheet.column(0); 0557 col->setColumnMode(AbstractColumn::ColumnMode::Integer); 0558 col->replaceInteger(0, xData1); 0559 col->replaceInteger(4, xData2); 0560 0561 // sort 0562 sheet.sortColumns(nullptr, {col}, false); 0563 0564 // values 0565 QCOMPARE(col->integerAt(6), -1); 0566 QCOMPARE(col->integerAt(5), 0); 0567 QCOMPARE(col->integerAt(4), 2); 0568 QCOMPARE(col->integerAt(3), 3); 0569 QCOMPARE(col->integerAt(2), 4); 0570 QCOMPARE(col->integerAt(1), 5); 0571 QCOMPARE(col->integerAt(0), 6); 0572 } 0573 0574 /* 0575 * check sorting single column of big int values with empty entries ascending 0576 */ 0577 void SpreadsheetTest::testSortSingleBigInt1() { 0578 const QVector<qint64> xData1{40000000000, 50000000000, 20000000000}; 0579 const QVector<qint64> xData2{30000000000, 60000000000, -10000000000}; 0580 0581 Spreadsheet sheet(QStringLiteral("test"), false); 0582 sheet.setColumnCount(1); 0583 sheet.setRowCount(7); 0584 auto* col = sheet.column(0); 0585 col->setColumnMode(AbstractColumn::ColumnMode::BigInt); 0586 col->replaceBigInt(0, xData1); 0587 col->replaceBigInt(4, xData2); 0588 0589 // sort 0590 sheet.sortColumns(nullptr, {col}, true); 0591 0592 // values 0593 QCOMPARE(col->bigIntAt(0), -10000000000ll); 0594 QCOMPARE(col->bigIntAt(1), 0); 0595 QCOMPARE(col->bigIntAt(2), 20000000000ll); 0596 QCOMPARE(col->bigIntAt(3), 30000000000ll); 0597 QCOMPARE(col->bigIntAt(4), 40000000000ll); 0598 QCOMPARE(col->bigIntAt(5), 50000000000ll); 0599 QCOMPARE(col->bigIntAt(6), 60000000000ll); 0600 } 0601 0602 /* 0603 * check sorting single column of big int values with empty entries descending 0604 */ 0605 void SpreadsheetTest::testSortSingleBigInt2() { 0606 const QVector<qint64> xData1{40000000000, 50000000000, 20000000000}; 0607 const QVector<qint64> xData2{30000000000, 60000000000, -10000000000}; 0608 0609 Spreadsheet sheet(QStringLiteral("test"), false); 0610 sheet.setColumnCount(1); 0611 sheet.setRowCount(7); 0612 auto* col = sheet.column(0); 0613 col->setColumnMode(AbstractColumn::ColumnMode::BigInt); 0614 col->replaceBigInt(0, xData1); 0615 col->replaceBigInt(4, xData2); 0616 0617 // sort 0618 sheet.sortColumns(nullptr, {col}, false); 0619 0620 // values 0621 QCOMPARE(col->bigIntAt(6), -10000000000ll); 0622 QCOMPARE(col->bigIntAt(5), 0); 0623 QCOMPARE(col->bigIntAt(4), 20000000000ll); 0624 QCOMPARE(col->bigIntAt(3), 30000000000ll); 0625 QCOMPARE(col->bigIntAt(2), 40000000000ll); 0626 QCOMPARE(col->bigIntAt(1), 50000000000ll); 0627 QCOMPARE(col->bigIntAt(0), 60000000000ll); 0628 } 0629 0630 /* 0631 * check sorting single column of text with empty entries ascending 0632 */ 0633 void SpreadsheetTest::testSortSingleText1() { 0634 const QVector<QString> xData{QStringLiteral("ben"), 0635 QStringLiteral("amy"), 0636 QStringLiteral("eddy"), 0637 QString(), 0638 QStringLiteral("carl"), 0639 QStringLiteral("dan")}; 0640 0641 Spreadsheet sheet(QStringLiteral("test"), false); 0642 sheet.setColumnCount(1); 0643 sheet.setRowCount(8); 0644 auto* col = sheet.column(0); 0645 col->setColumnMode(AbstractColumn::ColumnMode::Text); 0646 col->replaceTexts(0, xData); 0647 0648 // sort 0649 sheet.sortColumns(nullptr, {col}, true); 0650 0651 // values 0652 QCOMPARE(col->textAt(0), QLatin1String("amy")); 0653 QCOMPARE(col->textAt(1), QLatin1String("ben")); 0654 QCOMPARE(col->textAt(2), QLatin1String("carl")); 0655 QCOMPARE(col->textAt(3), QLatin1String("dan")); 0656 QCOMPARE(col->textAt(4), QLatin1String("eddy")); 0657 QCOMPARE(col->textAt(5), QString()); 0658 QCOMPARE(col->textAt(6), QString()); 0659 } 0660 0661 /* 0662 * check sorting single column of text with empty entries descending 0663 */ 0664 void SpreadsheetTest::testSortSingleText2() { 0665 const QVector<QString> xData = 0666 {QStringLiteral("ben"), QStringLiteral("amy"), QStringLiteral("eddy"), QString(), QStringLiteral("carl"), QStringLiteral("dan")}; 0667 0668 Spreadsheet sheet(QStringLiteral("test"), false); 0669 sheet.setColumnCount(1); 0670 sheet.setRowCount(8); 0671 auto* col = sheet.column(0); 0672 col->setColumnMode(AbstractColumn::ColumnMode::Text); 0673 col->replaceTexts(0, xData); 0674 0675 // sort 0676 sheet.sortColumns(nullptr, {col}, false); 0677 0678 // values 0679 QCOMPARE(col->textAt(0), QLatin1String("eddy")); 0680 QCOMPARE(col->textAt(1), QLatin1String("dan")); 0681 QCOMPARE(col->textAt(2), QLatin1String("carl")); 0682 QCOMPARE(col->textAt(3), QLatin1String("ben")); 0683 QCOMPARE(col->textAt(4), QLatin1String("amy")); 0684 QCOMPARE(col->textAt(5), QString()); 0685 QCOMPARE(col->textAt(6), QString()); 0686 } 0687 0688 /* 0689 * check sorting single column of datetimes with invalid entries ascending 0690 */ 0691 void SpreadsheetTest::testSortSingleDateTime1() { 0692 const QVector<QDateTime> xData{ 0693 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12)), 0694 QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12)), 0695 QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12)), 0696 QDateTime(QDate(2019, 02, 29), QTime(12, 12, 12)), // invalid 0697 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13)), 0698 QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12)), 0699 }; 0700 0701 Spreadsheet sheet(QStringLiteral("test"), false); 0702 sheet.setColumnCount(1); 0703 sheet.setRowCount(8); 0704 auto* col{sheet.column(0)}; 0705 col->setColumnMode(AbstractColumn::ColumnMode::DateTime); 0706 col->replaceDateTimes(0, xData); 0707 0708 // sort 0709 sheet.sortColumns(nullptr, {col}, true); 0710 0711 // values 0712 QCOMPARE(col->dateTimeAt(0), QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12))); 0713 QCOMPARE(col->dateTimeAt(1), QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12))); 0714 QCOMPARE(col->dateTimeAt(2), QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12))); 0715 QCOMPARE(col->dateTimeAt(3), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12))); 0716 QCOMPARE(col->dateTimeAt(4), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13))); 0717 } 0718 0719 /* 0720 * check sorting single column of datetimes with invalid entries descending 0721 */ 0722 void SpreadsheetTest::testSortSingleDateTime2() { 0723 const QVector<QDateTime> xData{ 0724 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12)), 0725 QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12)), 0726 QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12)), 0727 QDateTime(QDate(2019, 02, 29), QTime(12, 12, 12)), // invalid 0728 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13)), 0729 QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12)), 0730 }; 0731 0732 Spreadsheet sheet(QStringLiteral("test"), false); 0733 sheet.setColumnCount(1); 0734 sheet.setRowCount(8); 0735 auto* col = sheet.column(0); 0736 col->setColumnMode(AbstractColumn::ColumnMode::DateTime); 0737 col->replaceDateTimes(0, xData); 0738 0739 // sort 0740 sheet.sortColumns(nullptr, {col}, false); 0741 0742 // values 0743 QCOMPARE(col->dateTimeAt(4), QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12))); 0744 QCOMPARE(col->dateTimeAt(3), QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12))); 0745 QCOMPARE(col->dateTimeAt(2), QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12))); 0746 QCOMPARE(col->dateTimeAt(1), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12))); 0747 QCOMPARE(col->dateTimeAt(0), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13))); 0748 } 0749 0750 // multiple column 0751 /* 0752 * check sorting double values with NaN ascending as leading column 0753 */ 0754 void SpreadsheetTest::testSortNumeric1() { 0755 const QVector<double> xData{0.5, -0.2, GSL_NAN, 2.0, -1.0}; 0756 const QVector<int> yData{1, 2, 3, 4, 5, 6}; 0757 0758 Spreadsheet sheet(QStringLiteral("test"), false); 0759 sheet.setColumnCount(2); 0760 sheet.setRowCount(10); 0761 auto* col0{sheet.column(0)}; 0762 auto* col1{sheet.column(1)}; 0763 col0->replaceValues(0, xData); 0764 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 0765 col1->replaceInteger(0, yData); 0766 0767 // sort 0768 sheet.sortColumns(col0, {col0, col1}, true); 0769 0770 // values 0771 QCOMPARE(col0->valueAt(0), -1.0); 0772 QCOMPARE(col0->valueAt(1), -0.2); 0773 QCOMPARE(col0->valueAt(2), 0.5); 0774 QCOMPARE(col0->valueAt(3), 2.0); 0775 // QCOMPARE(col0->valueAt(4), GSL_NAN); 0776 QCOMPARE(col1->integerAt(0), 5); 0777 QCOMPARE(col1->integerAt(1), 2); 0778 QCOMPARE(col1->integerAt(2), 1); 0779 QCOMPARE(col1->integerAt(3), 4); 0780 QCOMPARE(col1->integerAt(4), 3); 0781 QCOMPARE(col1->integerAt(5), 6); 0782 } 0783 0784 /* 0785 * check sorting double values with NaN descending as leading column 0786 */ 0787 void SpreadsheetTest::testSortNumeric2() { 0788 const QVector<double> xData{0.5, -0.2, GSL_NAN, 2.0, -1.0}; 0789 const QVector<int> yData{1, 2, 3, 4, 5, 6, 7}; 0790 0791 Spreadsheet sheet(QStringLiteral("test"), false); 0792 sheet.setColumnCount(2); 0793 sheet.setRowCount(10); 0794 auto* col0{sheet.column(0)}; 0795 auto* col1{sheet.column(1)}; 0796 col0->replaceValues(0, xData); 0797 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 0798 col1->replaceInteger(0, yData); 0799 0800 // sort 0801 sheet.sortColumns(col0, {col0, col1}, false); 0802 0803 // values 0804 QCOMPARE(col0->valueAt(0), 2.0); 0805 QCOMPARE(col0->valueAt(1), 0.5); 0806 QCOMPARE(col0->valueAt(2), -0.2); 0807 QCOMPARE(col0->valueAt(3), -1.0); 0808 QCOMPARE(col1->integerAt(0), 4); 0809 QCOMPARE(col1->integerAt(1), 1); 0810 QCOMPARE(col1->integerAt(2), 2); 0811 QCOMPARE(col1->integerAt(3), 5); 0812 QCOMPARE(col1->integerAt(4), 3); 0813 QCOMPARE(col1->integerAt(5), 6); 0814 QCOMPARE(col1->integerAt(6), 7); 0815 } 0816 0817 /* 0818 * check sorting integer values with empty entries ascending as leading column 0819 */ 0820 void SpreadsheetTest::testSortInteger1() { 0821 const QVector<int> xData1{4, 5, 2}; 0822 const QVector<int> xData2{3, 6, -1}; 0823 const QVector<int> yData{1, 2, 3, 4, 5, 6, 7}; 0824 0825 Spreadsheet sheet(QStringLiteral("test"), false); 0826 sheet.setColumnCount(2); 0827 sheet.setRowCount(7); 0828 auto* col0{sheet.column(0)}; 0829 auto* col1{sheet.column(1)}; 0830 col0->setColumnMode(AbstractColumn::ColumnMode::Integer); 0831 col0->replaceInteger(0, xData1); 0832 col0->replaceInteger(4, xData2); 0833 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 0834 col1->replaceInteger(0, yData); 0835 0836 // sort 0837 sheet.sortColumns(col0, {col0, col1}, true); 0838 0839 // values 0840 QCOMPARE(col0->integerAt(0), -1); 0841 QCOMPARE(col0->integerAt(1), 0); 0842 QCOMPARE(col0->integerAt(2), 2); 0843 QCOMPARE(col0->integerAt(3), 3); 0844 QCOMPARE(col0->integerAt(4), 4); 0845 QCOMPARE(col0->integerAt(5), 5); 0846 QCOMPARE(col0->integerAt(6), 6); 0847 QCOMPARE(col1->integerAt(0), 7); 0848 QCOMPARE(col1->integerAt(1), 4); 0849 QCOMPARE(col1->integerAt(2), 3); 0850 QCOMPARE(col1->integerAt(3), 5); 0851 QCOMPARE(col1->integerAt(4), 1); 0852 QCOMPARE(col1->integerAt(5), 2); 0853 QCOMPARE(col1->integerAt(6), 6); 0854 } 0855 0856 /* 0857 * check sorting integer values with empty entries descending as leading column 0858 */ 0859 void SpreadsheetTest::testSortInteger2() { 0860 const QVector<int> xData1{4, 5, 2}; 0861 const QVector<int> xData2{3, 6, -1}; 0862 const QVector<int> yData{1, 2, 3, 4, 5, 6, 7}; 0863 0864 Spreadsheet sheet(QStringLiteral("test"), false); 0865 sheet.setColumnCount(2); 0866 sheet.setRowCount(7); 0867 auto* col0{sheet.column(0)}; 0868 auto* col1{sheet.column(1)}; 0869 col0->setColumnMode(AbstractColumn::ColumnMode::Integer); 0870 col0->replaceInteger(0, xData1); 0871 col0->replaceInteger(4, xData2); 0872 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 0873 col1->replaceInteger(0, yData); 0874 0875 // sort 0876 sheet.sortColumns(col0, {col0, col1}, false); 0877 0878 // values 0879 QCOMPARE(col0->integerAt(6), -1); 0880 QCOMPARE(col0->integerAt(5), 0); 0881 QCOMPARE(col0->integerAt(4), 2); 0882 QCOMPARE(col0->integerAt(3), 3); 0883 QCOMPARE(col0->integerAt(2), 4); 0884 QCOMPARE(col0->integerAt(1), 5); 0885 QCOMPARE(col0->integerAt(0), 6); 0886 QCOMPARE(col1->integerAt(6), 7); 0887 QCOMPARE(col1->integerAt(5), 4); 0888 QCOMPARE(col1->integerAt(4), 3); 0889 QCOMPARE(col1->integerAt(3), 5); 0890 QCOMPARE(col1->integerAt(2), 1); 0891 QCOMPARE(col1->integerAt(1), 2); 0892 QCOMPARE(col1->integerAt(0), 6); 0893 } 0894 0895 /* 0896 * check sorting big int values with empty entries ascending as leading column 0897 */ 0898 void SpreadsheetTest::testSortBigInt1() { 0899 Spreadsheet sheet(QStringLiteral("test"), false); 0900 sheet.setColumnCount(2); 0901 sheet.setRowCount(7); 0902 0903 QVector<qint64> xData1{40000000000, 50000000000, 20000000000}; 0904 QVector<qint64> xData2{30000000000, 60000000000, -10000000000}; 0905 QVector<qint64> yData{1, 2, 3, 4, 5, 6, 7}; 0906 0907 auto* col0{sheet.column(0)}; 0908 auto* col1{sheet.column(1)}; 0909 col0->setColumnMode(AbstractColumn::ColumnMode::BigInt); 0910 col0->replaceBigInt(0, xData1); 0911 col0->replaceBigInt(4, xData2); 0912 col1->setColumnMode(AbstractColumn::ColumnMode::BigInt); 0913 col1->replaceBigInt(0, yData); 0914 0915 // sort 0916 sheet.sortColumns(col0, {col0, col1}, true); 0917 0918 // values 0919 QCOMPARE(col0->bigIntAt(0), -10000000000ll); 0920 QCOMPARE(col0->bigIntAt(1), 0); 0921 QCOMPARE(col0->bigIntAt(2), 20000000000ll); 0922 QCOMPARE(col0->bigIntAt(3), 30000000000ll); 0923 QCOMPARE(col0->bigIntAt(4), 40000000000ll); 0924 QCOMPARE(col0->bigIntAt(5), 50000000000ll); 0925 QCOMPARE(col0->bigIntAt(6), 60000000000ll); 0926 QCOMPARE(col1->bigIntAt(0), 7); 0927 QCOMPARE(col1->bigIntAt(1), 4); 0928 QCOMPARE(col1->bigIntAt(2), 3); 0929 QCOMPARE(col1->bigIntAt(3), 5); 0930 QCOMPARE(col1->bigIntAt(4), 1); 0931 QCOMPARE(col1->bigIntAt(5), 2); 0932 QCOMPARE(col1->bigIntAt(6), 6); 0933 } 0934 0935 /* 0936 * check sorting big int values with empty entries descending as leading column 0937 */ 0938 void SpreadsheetTest::testSortBigInt2() { 0939 Spreadsheet sheet(QStringLiteral("test"), false); 0940 sheet.setColumnCount(2); 0941 sheet.setRowCount(7); 0942 0943 QVector<qint64> xData1{40000000000, 50000000000, 20000000000}; 0944 QVector<qint64> xData2{30000000000, 60000000000, -10000000000}; 0945 QVector<qint64> yData{1, 2, 3, 4, 5, 6, 7}; 0946 0947 auto* col0{sheet.column(0)}; 0948 auto* col1{sheet.column(1)}; 0949 col0->setColumnMode(AbstractColumn::ColumnMode::BigInt); 0950 col0->replaceBigInt(0, xData1); 0951 col0->replaceBigInt(4, xData2); 0952 col1->setColumnMode(AbstractColumn::ColumnMode::BigInt); 0953 col1->replaceBigInt(0, yData); 0954 0955 // sort 0956 sheet.sortColumns(col0, {col0, col1}, false); 0957 0958 // values 0959 QCOMPARE(col0->bigIntAt(6), -10000000000ll); 0960 QCOMPARE(col0->bigIntAt(5), 0); 0961 QCOMPARE(col0->bigIntAt(4), 20000000000ll); 0962 QCOMPARE(col0->bigIntAt(3), 30000000000ll); 0963 QCOMPARE(col0->bigIntAt(2), 40000000000ll); 0964 QCOMPARE(col0->bigIntAt(1), 50000000000ll); 0965 QCOMPARE(col0->bigIntAt(0), 60000000000ll); 0966 QCOMPARE(col1->bigIntAt(6), 7); 0967 QCOMPARE(col1->bigIntAt(5), 4); 0968 QCOMPARE(col1->bigIntAt(4), 3); 0969 QCOMPARE(col1->bigIntAt(3), 5); 0970 QCOMPARE(col1->bigIntAt(2), 1); 0971 QCOMPARE(col1->bigIntAt(1), 2); 0972 QCOMPARE(col1->bigIntAt(0), 6); 0973 } 0974 0975 /* 0976 * check sorting text with empty entries ascending as leading column 0977 */ 0978 void SpreadsheetTest::testSortText1() { 0979 Spreadsheet sheet(QStringLiteral("test"), false); 0980 sheet.setColumnCount(2); 0981 sheet.setRowCount(8); 0982 0983 QVector<QString> xData{QStringLiteral("ben"), QStringLiteral("amy"), QStringLiteral("eddy"), QString(), QStringLiteral("carl"), QStringLiteral("dan")}; 0984 QVector<int> yData{1, 2, 3, 4, 5, 6, 7}; 0985 0986 auto* col0{sheet.column(0)}; 0987 auto* col1{sheet.column(1)}; 0988 col0->setColumnMode(AbstractColumn::ColumnMode::Text); 0989 col0->replaceTexts(0, xData); 0990 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 0991 col1->replaceInteger(0, yData); 0992 0993 // sort 0994 sheet.sortColumns(col0, {col0, col1}, true); 0995 0996 // values 0997 QCOMPARE(col0->textAt(0), QLatin1String("amy")); 0998 QCOMPARE(col0->textAt(1), QLatin1String("ben")); 0999 QCOMPARE(col0->textAt(2), QLatin1String("carl")); 1000 QCOMPARE(col0->textAt(3), QLatin1String("dan")); 1001 QCOMPARE(col0->textAt(4), QLatin1String("eddy")); 1002 QCOMPARE(col0->textAt(5), QString()); 1003 QCOMPARE(col0->textAt(6), QString()); 1004 QCOMPARE(col1->integerAt(0), 2); 1005 QCOMPARE(col1->integerAt(1), 1); 1006 QCOMPARE(col1->integerAt(2), 5); 1007 QCOMPARE(col1->integerAt(3), 6); 1008 QCOMPARE(col1->integerAt(4), 3); 1009 QCOMPARE(col1->integerAt(5), 4); 1010 QCOMPARE(col1->integerAt(6), 7); 1011 } 1012 1013 /* 1014 * check sorting text with empty entries descending as leading column 1015 */ 1016 void SpreadsheetTest::testSortText2() { 1017 Spreadsheet sheet(QStringLiteral("test"), false); 1018 sheet.setColumnCount(2); 1019 sheet.setRowCount(8); 1020 1021 QVector<QString> xData{QStringLiteral("ben"), QStringLiteral("amy"), QStringLiteral("eddy"), QString(), QStringLiteral("carl"), QStringLiteral("dan")}; 1022 QVector<int> yData{1, 2, 3, 4, 5, 6, 7}; 1023 1024 auto* col0{sheet.column(0)}; 1025 auto* col1{sheet.column(1)}; 1026 col0->setColumnMode(AbstractColumn::ColumnMode::Text); 1027 col0->replaceTexts(0, xData); 1028 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 1029 col1->replaceInteger(0, yData); 1030 1031 // sort 1032 sheet.sortColumns(col0, {col0, col1}, false); 1033 1034 // values 1035 QCOMPARE(col0->textAt(4), QLatin1String("amy")); 1036 QCOMPARE(col0->textAt(3), QLatin1String("ben")); 1037 QCOMPARE(col0->textAt(2), QLatin1String("carl")); 1038 QCOMPARE(col0->textAt(1), QLatin1String("dan")); 1039 QCOMPARE(col0->textAt(0), QLatin1String("eddy")); 1040 QCOMPARE(col0->textAt(5), QString()); 1041 QCOMPARE(col0->textAt(6), QString()); 1042 QCOMPARE(col1->integerAt(4), 2); 1043 QCOMPARE(col1->integerAt(3), 1); 1044 QCOMPARE(col1->integerAt(2), 5); 1045 QCOMPARE(col1->integerAt(1), 6); 1046 QCOMPARE(col1->integerAt(0), 3); 1047 QCOMPARE(col1->integerAt(5), 4); 1048 QCOMPARE(col1->integerAt(6), 7); 1049 } 1050 1051 /* 1052 * check sorting datetimes with invalid entries ascending as leading column 1053 */ 1054 void SpreadsheetTest::testSortDateTime1() { 1055 Spreadsheet sheet(QStringLiteral("test"), false); 1056 sheet.setColumnCount(2); 1057 sheet.setRowCount(8); 1058 1059 QVector<QDateTime> xData{ 1060 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12)), 1061 QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12)), 1062 QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12)), 1063 QDateTime(QDate(2019, 02, 29), QTime(12, 12, 12)), // invalid 1064 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13)), 1065 QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12)), 1066 }; 1067 QVector<int> yData = {1, 2, 3, 4, 5, 6, 7}; 1068 1069 auto* col0{sheet.column(0)}; 1070 auto* col1{sheet.column(1)}; 1071 col0->setColumnMode(AbstractColumn::ColumnMode::DateTime); 1072 col0->replaceDateTimes(0, xData); 1073 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 1074 col1->replaceInteger(0, yData); 1075 1076 // sort 1077 sheet.sortColumns(col0, {col0, col1}, true); 1078 1079 // values 1080 QCOMPARE(col0->dateTimeAt(0), QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12))); 1081 QCOMPARE(col0->dateTimeAt(1), QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12))); 1082 QCOMPARE(col0->dateTimeAt(2), QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12))); 1083 QCOMPARE(col0->dateTimeAt(3), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12))); 1084 QCOMPARE(col0->dateTimeAt(4), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13))); 1085 QCOMPARE(col1->integerAt(0), 3); 1086 QCOMPARE(col1->integerAt(1), 2); 1087 QCOMPARE(col1->integerAt(2), 6); 1088 QCOMPARE(col1->integerAt(3), 1); 1089 QCOMPARE(col1->integerAt(4), 5); 1090 QCOMPARE(col1->integerAt(5), 4); 1091 QCOMPARE(col1->integerAt(6), 7); 1092 } 1093 1094 /* 1095 * check sorting datetimes with invalid entries descending as leading column 1096 */ 1097 void SpreadsheetTest::testSortDateTime2() { 1098 const QVector<QDateTime> xData{ 1099 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12)), 1100 QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12)), 1101 QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12)), 1102 QDateTime(QDate(2019, 02, 29), QTime(12, 12, 12)), // invalid 1103 QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13)), 1104 QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12)), 1105 }; 1106 const QVector<int> yData = {1, 2, 3, 4, 5, 6, 7}; 1107 1108 Spreadsheet sheet(QStringLiteral("test"), false); 1109 sheet.setColumnCount(2); 1110 sheet.setRowCount(8); 1111 auto* col0{sheet.column(0)}; 1112 auto* col1{sheet.column(1)}; 1113 col0->setColumnMode(AbstractColumn::ColumnMode::DateTime); 1114 col0->replaceDateTimes(0, xData); 1115 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 1116 col1->replaceInteger(0, yData); 1117 1118 // sort 1119 sheet.sortColumns(col0, {col0, col1}, false); 1120 1121 // values 1122 QCOMPARE(col0->dateTimeAt(4), QDateTime(QDate(2019, 02, 28), QTime(12, 12, 12))); 1123 QCOMPARE(col0->dateTimeAt(3), QDateTime(QDate(2020, 02, 28), QTime(12, 12, 12))); 1124 QCOMPARE(col0->dateTimeAt(2), QDateTime(QDate(2020, 02, 29), QTime(11, 12, 12))); 1125 QCOMPARE(col0->dateTimeAt(1), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 12))); 1126 QCOMPARE(col0->dateTimeAt(0), QDateTime(QDate(2020, 02, 29), QTime(12, 12, 13))); 1127 QCOMPARE(col1->integerAt(4), 3); 1128 QCOMPARE(col1->integerAt(3), 2); 1129 QCOMPARE(col1->integerAt(2), 6); 1130 QCOMPARE(col1->integerAt(1), 1); 1131 QCOMPARE(col1->integerAt(0), 5); 1132 QCOMPARE(col1->integerAt(5), 4); 1133 QCOMPARE(col1->integerAt(6), 7); 1134 } 1135 1136 // performance 1137 1138 /* 1139 * check performance of sorting double values in single column 1140 */ 1141 void SpreadsheetTest::testSortPerformanceNumeric1() { 1142 Spreadsheet sheet(QStringLiteral("test"), false); 1143 sheet.setColumnCount(1); 1144 sheet.setRowCount(10000); 1145 1146 QVector<double> xData; 1147 WARN("CREATE DATA") 1148 for (int i = 0; i < sheet.rowCount(); i++) 1149 xData << QRandomGenerator::global()->generateDouble(); 1150 1151 auto* col = sheet.column(0); 1152 col->replaceValues(0, xData); 1153 1154 // sort 1155 QBENCHMARK { sheet.sortColumns(nullptr, {col}, true); } 1156 } 1157 1158 /* 1159 * check performance of sorting double values with two columns 1160 */ 1161 void SpreadsheetTest::testSortPerformanceNumeric2() { 1162 Spreadsheet sheet(QStringLiteral("test"), false); 1163 sheet.setColumnCount(2); 1164 sheet.setRowCount(10000); 1165 1166 QVector<double> xData; 1167 QVector<int> yData; 1168 WARN("CREATE DATA") 1169 for (int i = 0; i < sheet.rowCount(); i++) { 1170 xData << QRandomGenerator::global()->generateDouble(); 1171 yData << i + 1; 1172 } 1173 1174 auto* col0{sheet.column(0)}; 1175 auto* col1{sheet.column(1)}; 1176 col0->replaceValues(0, xData); 1177 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 1178 col1->replaceInteger(0, yData); 1179 1180 // sort 1181 QBENCHMARK { sheet.sortColumns(col0, {col0, col1}, true); } 1182 } 1183 1184 // ********************************************************** 1185 // ********************* drop/mask ************************* 1186 // ********************************************************** 1187 void SpreadsheetTest::testRemoveRowsWithMissingValues() { 1188 // prepare the spreadsheet 1189 Spreadsheet sheet(QStringLiteral("test"), false); 1190 sheet.setColumnCount(2); 1191 sheet.setRowCount(5); 1192 1193 auto* col0{sheet.column(0)}; 1194 col0->setColumnMode(AbstractColumn::ColumnMode::Double); 1195 col0->setValueAt(0, 0.); 1196 // missing value for row = 1 1197 col0->setValueAt(2, 2.); 1198 col0->setValueAt(3, 3.); 1199 1200 auto* col1{sheet.column(1)}; 1201 col1->setColumnMode(AbstractColumn::ColumnMode::Double); 1202 col1->setValueAt(0, 0.); 1203 col1->setValueAt(1, 1.); 1204 // missing value for row = 2 1205 col1->setValueAt(3, 3.); 1206 1207 // remove rows with empty values and check the results 1208 sheet.removeEmptyRows(); 1209 QCOMPARE(sheet.rowCount(), 2); 1210 QCOMPARE(col0->valueAt(0), 0.); 1211 QCOMPARE(col0->valueAt(1), 3.); 1212 QCOMPARE(col1->valueAt(0), 0.); 1213 QCOMPARE(col1->valueAt(1), 3.); 1214 } 1215 1216 void SpreadsheetTest::testMaskRowsWithMissingValues() { 1217 // prepare the spreadsheet 1218 Spreadsheet sheet(QStringLiteral("test"), false); 1219 sheet.setColumnCount(2); 1220 sheet.setRowCount(5); 1221 1222 auto* col0{sheet.column(0)}; 1223 col0->setColumnMode(AbstractColumn::ColumnMode::Double); 1224 col0->setValueAt(0, 0.); 1225 // missing value for row = 1 1226 col0->setValueAt(2, 2.); 1227 col0->setValueAt(3, 3.); 1228 1229 auto* col1{sheet.column(1)}; 1230 col1->setColumnMode(AbstractColumn::ColumnMode::Double); 1231 col1->setValueAt(0, 0.); 1232 col1->setValueAt(1, 1.); 1233 // missing value for row = 2 1234 col1->setValueAt(3, 3.); 1235 1236 // mask rows with empty values and check the results 1237 sheet.maskEmptyRows(); 1238 QCOMPARE(sheet.rowCount(), 5); 1239 QCOMPARE(col0->isMasked(0), false); 1240 QCOMPARE(col0->isMasked(1), true); 1241 QCOMPARE(col0->isMasked(2), true); 1242 QCOMPARE(col0->isMasked(3), false); 1243 QCOMPARE(col0->isMasked(4), true); 1244 QCOMPARE(col1->isMasked(0), false); 1245 QCOMPARE(col1->isMasked(1), true); 1246 QCOMPARE(col1->isMasked(2), true); 1247 QCOMPARE(col1->isMasked(3), false); 1248 QCOMPARE(col1->isMasked(4), true); 1249 } 1250 1251 // ********************************************************** 1252 // ********************* flattening ************************ 1253 // ********************************************************** 1254 void SpreadsheetTest::testFlatten00() { 1255 Project project; 1256 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 1257 project.addChild(sheet); 1258 sheet->setColumnCount(5); 1259 sheet->setRowCount(4); 1260 1261 // "Year" 1262 auto* col1 = sheet->column(0); 1263 col1->setName(QStringLiteral("Year")); 1264 col1->setColumnMode(AbstractColumn::ColumnMode::Text); 1265 col1->setTextAt(0, QStringLiteral("2021")); 1266 col1->setTextAt(1, QStringLiteral("2022")); 1267 col1->setTextAt(2, QStringLiteral("2021")); 1268 col1->setTextAt(3, QStringLiteral("2022")); 1269 1270 // "Country" 1271 auto* col2 = sheet->column(1); 1272 col2->setName(QStringLiteral("Country")); 1273 col2->setColumnMode(AbstractColumn::ColumnMode::Text); 1274 col2->setTextAt(0, QStringLiteral("Germany")); 1275 col2->setTextAt(1, QStringLiteral("Germany")); 1276 col2->setTextAt(2, QStringLiteral("Poland")); 1277 col2->setTextAt(3, QStringLiteral("Poland")); 1278 1279 // "Sales for Product 1" 1280 auto* col3 = sheet->column(2); 1281 col3->setName(QStringLiteral("Product 1")); 1282 col3->setColumnMode(AbstractColumn::ColumnMode::Integer); 1283 col3->setIntegerAt(0, 1); 1284 col3->setIntegerAt(1, 10); 1285 col3->setIntegerAt(2, 4); 1286 col3->setIntegerAt(3, 40); 1287 1288 // "Sales for Product 2" 1289 auto* col4 = sheet->column(3); 1290 col4->setName(QStringLiteral("Product 2")); 1291 col4->setColumnMode(AbstractColumn::ColumnMode::Integer); 1292 col4->setIntegerAt(0, 2); 1293 col4->setIntegerAt(1, 20); 1294 col4->setIntegerAt(2, 5); 1295 col4->setIntegerAt(3, 50); 1296 1297 // "Sales for Product 3" 1298 auto* col5 = sheet->column(4); 1299 col5->setName(QStringLiteral("Product 3")); 1300 col5->setColumnMode(AbstractColumn::ColumnMode::Integer); 1301 col5->setIntegerAt(0, 3); 1302 col5->setIntegerAt(1, 30); 1303 col5->setIntegerAt(2, 6); 1304 col5->setIntegerAt(3, 60); 1305 1306 // flatten the product columns relatively to year and country 1307 FlattenColumnsDialog dlg(sheet); 1308 QVector<Column*> referenceColumns; 1309 referenceColumns << col1; 1310 referenceColumns << col2; 1311 1312 QVector<Column*> valueColumns; 1313 valueColumns << col3; 1314 valueColumns << col4; 1315 valueColumns << col5; 1316 1317 dlg.flatten(sheet, valueColumns, referenceColumns); 1318 1319 // checks 1320 // make sure a new target spreadsheet with the flattened data was created 1321 const auto& sheets = project.children<Spreadsheet>(); 1322 QCOMPARE(sheets.count(), 2); 1323 auto* targetSheet = sheets.at(1); 1324 QCOMPARE(targetSheet->columnCount(), 4); // two reference columns, column "Category" and column "Value" 1325 QCOMPARE(targetSheet->rowCount(), 12); 1326 1327 // check values 1328 col1 = targetSheet->column(0); 1329 QCOMPARE(col1->textAt(0), QStringLiteral("2021")); 1330 QCOMPARE(col1->textAt(1), QStringLiteral("2021")); 1331 QCOMPARE(col1->textAt(2), QStringLiteral("2021")); 1332 QCOMPARE(col1->textAt(3), QStringLiteral("2022")); 1333 QCOMPARE(col1->textAt(4), QStringLiteral("2022")); 1334 QCOMPARE(col1->textAt(5), QStringLiteral("2022")); 1335 QCOMPARE(col1->textAt(6), QStringLiteral("2021")); 1336 QCOMPARE(col1->textAt(7), QStringLiteral("2021")); 1337 QCOMPARE(col1->textAt(8), QStringLiteral("2021")); 1338 QCOMPARE(col1->textAt(9), QStringLiteral("2022")); 1339 QCOMPARE(col1->textAt(10), QStringLiteral("2022")); 1340 QCOMPARE(col1->textAt(11), QStringLiteral("2022")); 1341 1342 col4 = targetSheet->column(3); 1343 QCOMPARE(col4->integerAt(0), 1); 1344 QCOMPARE(col4->integerAt(1), 2); 1345 QCOMPARE(col4->integerAt(2), 3); 1346 QCOMPARE(col4->integerAt(3), 10); 1347 QCOMPARE(col4->integerAt(4), 20); 1348 QCOMPARE(col4->integerAt(5), 30); 1349 QCOMPARE(col4->integerAt(6), 4); 1350 QCOMPARE(col4->integerAt(7), 5); 1351 QCOMPARE(col4->integerAt(8), 6); 1352 QCOMPARE(col4->integerAt(9), 40); 1353 QCOMPARE(col4->integerAt(10), 50); 1354 QCOMPARE(col4->integerAt(11), 60); 1355 } 1356 1357 // test with a missing value in one of the reference columns 1358 void SpreadsheetTest::testFlatten01() { 1359 Project project; 1360 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 1361 project.addChild(sheet); 1362 sheet->setColumnCount(5); 1363 sheet->setRowCount(2); 1364 1365 // "Year" 1366 auto* col1 = sheet->column(0); 1367 col1->setName(QStringLiteral("Year")); 1368 col1->setColumnMode(AbstractColumn::ColumnMode::Text); 1369 col1->setTextAt(0, QStringLiteral("2021")); 1370 col1->setTextAt(1, QStringLiteral("2022")); 1371 1372 // "Country" 1373 auto* col2 = sheet->column(1); 1374 col2->setName(QStringLiteral("Country")); 1375 col2->setColumnMode(AbstractColumn::ColumnMode::Text); 1376 col2->setTextAt(0, QStringLiteral("Germany")); 1377 // missing value in the second row 1378 1379 // "Sales for Product 1" 1380 auto* col3 = sheet->column(2); 1381 col3->setName(QStringLiteral("Product 1")); 1382 col3->setColumnMode(AbstractColumn::ColumnMode::Integer); 1383 col3->setIntegerAt(0, 1); 1384 col3->setIntegerAt(1, 10); 1385 1386 // "Sales for Product 2" 1387 auto* col4 = sheet->column(3); 1388 col4->setName(QStringLiteral("Product 2")); 1389 col4->setColumnMode(AbstractColumn::ColumnMode::Integer); 1390 col4->setIntegerAt(0, 2); 1391 col4->setIntegerAt(1, 20); 1392 1393 // "Sales for Product 3" 1394 auto* col5 = sheet->column(4); 1395 col5->setName(QStringLiteral("Product 3")); 1396 col5->setColumnMode(AbstractColumn::ColumnMode::Integer); 1397 col5->setIntegerAt(0, 3); 1398 col5->setIntegerAt(1, 30); 1399 1400 // flatten the product columns relatively to year and country 1401 FlattenColumnsDialog dlg(sheet); 1402 QVector<Column*> referenceColumns; 1403 referenceColumns << col1; 1404 referenceColumns << col2; 1405 1406 QVector<Column*> valueColumns; 1407 valueColumns << col3; 1408 valueColumns << col4; 1409 valueColumns << col5; 1410 1411 dlg.flatten(sheet, valueColumns, referenceColumns); 1412 1413 // checks 1414 // make sure a new target spreadsheet with the flattened data was created 1415 const auto& sheets = project.children<Spreadsheet>(); 1416 QCOMPARE(sheets.count(), 2); 1417 auto* targetSheet = sheets.at(1); 1418 QCOMPARE(targetSheet->columnCount(), 4); // two reference columns, column "Category" and column "Value" 1419 QCOMPARE(targetSheet->rowCount(), 6); 1420 1421 // check values 1422 col1 = targetSheet->column(0); 1423 QCOMPARE(col1->textAt(0), QStringLiteral("2021")); 1424 QCOMPARE(col1->textAt(1), QStringLiteral("2021")); 1425 QCOMPARE(col1->textAt(2), QStringLiteral("2021")); 1426 QCOMPARE(col1->textAt(3), QStringLiteral("2022")); 1427 QCOMPARE(col1->textAt(4), QStringLiteral("2022")); 1428 QCOMPARE(col1->textAt(5), QStringLiteral("2022")); 1429 1430 col1 = targetSheet->column(1); 1431 QCOMPARE(col1->textAt(0), QStringLiteral("Germany")); 1432 QCOMPARE(col1->textAt(1), QStringLiteral("Germany")); 1433 QCOMPARE(col1->textAt(2), QStringLiteral("Germany")); 1434 QCOMPARE(col1->textAt(3), QString()); 1435 QCOMPARE(col1->textAt(4), QString()); 1436 QCOMPARE(col1->textAt(5), QString()); 1437 1438 col4 = targetSheet->column(3); 1439 QCOMPARE(col4->integerAt(0), 1); 1440 QCOMPARE(col4->integerAt(1), 2); 1441 QCOMPARE(col4->integerAt(2), 3); 1442 QCOMPARE(col4->integerAt(3), 10); 1443 QCOMPARE(col4->integerAt(4), 20); 1444 QCOMPARE(col4->integerAt(5), 30); 1445 } 1446 1447 // test with missing values in the reference columns - no result should be produced for these rows 1448 void SpreadsheetTest::testFlatten02() { 1449 Project project; 1450 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 1451 project.addChild(sheet); 1452 sheet->setColumnCount(5); 1453 sheet->setRowCount(2); 1454 1455 // "Year" 1456 auto* col1 = sheet->column(0); 1457 col1->setName(QStringLiteral("Year")); 1458 col1->setColumnMode(AbstractColumn::ColumnMode::Text); 1459 col1->setTextAt(0, QStringLiteral("2021")); 1460 // missing value in the second row 1461 1462 // "Country" 1463 auto* col2 = sheet->column(1); 1464 col2->setName(QStringLiteral("Country")); 1465 col2->setColumnMode(AbstractColumn::ColumnMode::Text); 1466 col2->setTextAt(0, QStringLiteral("Germany")); 1467 // missing value in the second rows 1468 1469 // "Sales for Product 1" 1470 auto* col3 = sheet->column(2); 1471 col3->setName(QStringLiteral("Product 1")); 1472 col3->setColumnMode(AbstractColumn::ColumnMode::Integer); 1473 col3->setIntegerAt(0, 1); 1474 col3->setIntegerAt(1, 10); 1475 1476 // "Sales for Product 2" 1477 auto* col4 = sheet->column(3); 1478 col4->setName(QStringLiteral("Product 2")); 1479 col4->setColumnMode(AbstractColumn::ColumnMode::Integer); 1480 col4->setIntegerAt(0, 2); 1481 col4->setIntegerAt(1, 20); 1482 1483 // "Sales for Product 3" 1484 auto* col5 = sheet->column(4); 1485 col5->setName(QStringLiteral("Product 3")); 1486 col5->setColumnMode(AbstractColumn::ColumnMode::Integer); 1487 col5->setIntegerAt(0, 3); 1488 col5->setIntegerAt(1, 30); 1489 1490 // flatten the product columns relatively to year and country 1491 FlattenColumnsDialog dlg(sheet); 1492 QVector<Column*> referenceColumns; 1493 referenceColumns << col1; 1494 referenceColumns << col2; 1495 1496 QVector<Column*> valueColumns; 1497 valueColumns << col3; 1498 valueColumns << col4; 1499 valueColumns << col5; 1500 1501 dlg.flatten(sheet, valueColumns, referenceColumns); 1502 1503 // checks 1504 // make sure a new target spreadsheet with the flattened data was created 1505 const auto& sheets = project.children<Spreadsheet>(); 1506 QCOMPARE(sheets.count(), 2); 1507 auto* targetSheet = sheets.at(1); 1508 QCOMPARE(targetSheet->columnCount(), 4); // two reference columns, column "Category" and column "Value" 1509 QCOMPARE(targetSheet->rowCount(), 3); 1510 1511 // check values 1512 col1 = targetSheet->column(0); 1513 QCOMPARE(col1->textAt(0), QStringLiteral("2021")); 1514 QCOMPARE(col1->textAt(1), QStringLiteral("2021")); 1515 QCOMPARE(col1->textAt(2), QStringLiteral("2021")); 1516 1517 col1 = targetSheet->column(1); 1518 QCOMPARE(col1->textAt(0), QStringLiteral("Germany")); 1519 QCOMPARE(col1->textAt(1), QStringLiteral("Germany")); 1520 QCOMPARE(col1->textAt(2), QStringLiteral("Germany")); 1521 1522 col4 = targetSheet->column(3); 1523 QCOMPARE(col4->integerAt(0), 1); 1524 QCOMPARE(col4->integerAt(1), 2); 1525 QCOMPARE(col4->integerAt(2), 3); 1526 } 1527 1528 // test with missing no reference columns 1529 void SpreadsheetTest::testFlatten03() { 1530 Project project; 1531 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 1532 project.addChild(sheet); 1533 sheet->setColumnCount(3); 1534 sheet->setRowCount(2); 1535 1536 // "Sales for Product 1" 1537 auto* col1 = sheet->column(0); 1538 col1->setName(QStringLiteral("Product 1")); 1539 col1->setColumnMode(AbstractColumn::ColumnMode::Integer); 1540 col1->setIntegerAt(0, 1); 1541 col1->setIntegerAt(1, 10); 1542 1543 // "Sales for Product 2" 1544 auto* col2 = sheet->column(1); 1545 col2->setName(QStringLiteral("Product 2")); 1546 col2->setColumnMode(AbstractColumn::ColumnMode::Integer); 1547 col2->setIntegerAt(0, 2); 1548 col2->setIntegerAt(1, 20); 1549 1550 // "Sales for Product 3" 1551 auto* col3 = sheet->column(2); 1552 col3->setName(QStringLiteral("Product 3")); 1553 col3->setColumnMode(AbstractColumn::ColumnMode::Integer); 1554 col3->setIntegerAt(0, 3); 1555 col3->setIntegerAt(1, 30); 1556 1557 // flatten the product columns without any reference columns 1558 QVector<Column*> valueColumns; 1559 valueColumns << col1; 1560 valueColumns << col2; 1561 valueColumns << col3; 1562 1563 FlattenColumnsDialog dlg(sheet); 1564 dlg.flatten(sheet, valueColumns, QVector<Column*>()); 1565 1566 // checks 1567 // make sure a new target spreadsheet with the flattened data was created 1568 const auto& sheets = project.children<Spreadsheet>(); 1569 QCOMPARE(sheets.count(), 2); 1570 auto* targetSheet = sheets.at(1); 1571 QCOMPARE(targetSheet->columnCount(), 2); // no reference columns, only column "Category" and column "Value" 1572 QCOMPARE(targetSheet->rowCount(), 6); 1573 1574 // check values 1575 col1 = targetSheet->column(0); 1576 QCOMPARE(col1->textAt(0), QStringLiteral("Product 1")); 1577 QCOMPARE(col1->textAt(1), QStringLiteral("Product 2")); 1578 QCOMPARE(col1->textAt(2), QStringLiteral("Product 3")); 1579 QCOMPARE(col1->textAt(3), QStringLiteral("Product 1")); 1580 QCOMPARE(col1->textAt(4), QStringLiteral("Product 2")); 1581 QCOMPARE(col1->textAt(5), QStringLiteral("Product 3")); 1582 1583 col2 = targetSheet->column(1); 1584 QCOMPARE(col2->integerAt(0), 1); 1585 QCOMPARE(col2->integerAt(1), 2); 1586 QCOMPARE(col2->integerAt(2), 3); 1587 QCOMPARE(col2->integerAt(3), 10); 1588 QCOMPARE(col2->integerAt(4), 20); 1589 QCOMPARE(col2->integerAt(5), 30); 1590 } 1591 1592 // ********************************************************** 1593 // ******************** search&replace ********************* 1594 // ********************************************************** 1595 Spreadsheet* SpreadsheetTest::createSearchReplaceSpreadsheet() { 1596 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 1597 1598 sheet->setColumnCount(4); 1599 sheet->setRowCount(4); 1600 1601 // first text column 1602 auto* col1 = sheet->column(0); 1603 col1->setName(QStringLiteral("text")); 1604 col1->setColumnMode(AbstractColumn::ColumnMode::Text); 1605 col1->setTextAt(0, QStringLiteral("A")); 1606 col1->setTextAt(1, QStringLiteral("B")); 1607 col1->setTextAt(2, QStringLiteral("A")); 1608 col1->setTextAt(3, QStringLiteral("C")); 1609 1610 // first numeric (integer) column 1611 auto* col2 = sheet->column(1); 1612 col2->setName(QStringLiteral("integer")); 1613 col2->setColumnMode(AbstractColumn::ColumnMode::Integer); 1614 col2->setIntegerAt(0, 1); 1615 col2->setIntegerAt(1, 2); 1616 col2->setIntegerAt(2, 4); 1617 col2->setIntegerAt(3, 2); 1618 1619 // second text column 1620 auto* col3 = sheet->column(2); 1621 col3->setName(QStringLiteral("text2")); 1622 col3->setColumnMode(AbstractColumn::ColumnMode::Text); 1623 col3->setTextAt(0, QStringLiteral("B1")); 1624 col3->setTextAt(1, QStringLiteral("A")); 1625 col3->setTextAt(2, QStringLiteral("C2")); 1626 col3->setTextAt(3, QStringLiteral("A2")); 1627 1628 // second numeric (double) column 1629 auto* col4 = sheet->column(3); 1630 col4->setName(QStringLiteral("double")); 1631 col4->setColumnMode(AbstractColumn::ColumnMode::Double); 1632 col4->setValueAt(0, 4); 1633 col4->setValueAt(1, 3); 1634 col4->setValueAt(2, 2); 1635 col4->setValueAt(3, 1); 1636 1637 return sheet; 1638 } 1639 1640 /*! 1641 * simple search ignoring data types, column-major order 1642 */ 1643 void SpreadsheetTest::testSearchSimple00() { 1644 Project project; 1645 auto* sheet = createSearchReplaceSpreadsheet(); 1646 project.addChild(sheet); 1647 1648 // navigate to the (0,0) cell having the text value "A" 1649 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 1650 view->goToCell(0, 0); 1651 1652 // check the initial selection 1653 auto indexes = view->selectionModel()->selectedIndexes(); 1654 QCOMPARE(indexes.count(), 1); 1655 auto curIndex = indexes.constFirst(); 1656 QCOMPARE(curIndex.row(), 0); 1657 QCOMPARE(curIndex.column(), 0); 1658 1659 // initialize the search&replace widget 1660 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 1661 searchReplaceWidget->setReplaceEnabled(false); 1662 const auto& firstIndex = indexes.constFirst(); 1663 const auto* column = sheet->column(firstIndex.column()); 1664 const int row = firstIndex.row(); 1665 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 1666 1667 // checks: the initial cell text is "A", we navigate with 'next' 1668 // and then back with 'prev' in the column-major order looking for "A" 1669 1670 // next 1671 searchReplaceWidget->findNextSimple(true); 1672 indexes = view->selectionModel()->selectedIndexes(); 1673 QCOMPARE(indexes.count(), 1); 1674 curIndex = indexes.constFirst(); 1675 QCOMPARE(curIndex.row(), 2); 1676 QCOMPARE(curIndex.column(), 0); 1677 1678 // next 1679 searchReplaceWidget->findNextSimple(true); 1680 indexes = view->selectionModel()->selectedIndexes(); 1681 QCOMPARE(indexes.count(), 1); 1682 curIndex = indexes.constFirst(); 1683 QCOMPARE(curIndex.row(), 1); 1684 QCOMPARE(curIndex.column(), 2); 1685 1686 // next 1687 searchReplaceWidget->findNextSimple(true); 1688 indexes = view->selectionModel()->selectedIndexes(); 1689 QCOMPARE(indexes.count(), 1); 1690 curIndex = indexes.constFirst(); 1691 QCOMPARE(curIndex.row(), 3); 1692 QCOMPARE(curIndex.column(), 2); 1693 1694 // next, last matching cell reached 1695 searchReplaceWidget->findNextSimple(true); 1696 indexes = view->selectionModel()->selectedIndexes(); 1697 QCOMPARE(indexes.count(), 1); 1698 curIndex = indexes.constFirst(); 1699 QCOMPARE(curIndex.row(), 3); 1700 QCOMPARE(curIndex.column(), 2); 1701 1702 // previous 1703 searchReplaceWidget->findPreviousSimple(true); 1704 indexes = view->selectionModel()->selectedIndexes(); 1705 QCOMPARE(indexes.count(), 1); 1706 curIndex = indexes.constFirst(); 1707 QCOMPARE(curIndex.row(), 1); 1708 QCOMPARE(curIndex.column(), 2); 1709 1710 // previous 1711 searchReplaceWidget->findPreviousSimple(true); 1712 indexes = view->selectionModel()->selectedIndexes(); 1713 QCOMPARE(indexes.count(), 1); 1714 curIndex = indexes.constFirst(); 1715 QCOMPARE(curIndex.row(), 2); 1716 QCOMPARE(curIndex.column(), 0); 1717 1718 // previous 1719 searchReplaceWidget->findPreviousSimple(true); 1720 indexes = view->selectionModel()->selectedIndexes(); 1721 QCOMPARE(indexes.count(), 1); 1722 curIndex = indexes.constFirst(); 1723 QCOMPARE(curIndex.row(), 0); 1724 QCOMPARE(curIndex.column(), 0); 1725 1726 // previous, last matching cell reached 1727 searchReplaceWidget->findPreviousSimple(true); 1728 indexes = view->selectionModel()->selectedIndexes(); 1729 QCOMPARE(indexes.count(), 1); 1730 curIndex = indexes.constFirst(); 1731 QCOMPARE(curIndex.row(), 0); 1732 QCOMPARE(curIndex.column(), 0); 1733 } 1734 1735 /*! 1736 * extended search for Text, column-major order 1737 */ 1738 void SpreadsheetTest::testSearchExtended00() { 1739 Project project; 1740 auto* sheet = createSearchReplaceSpreadsheet(); 1741 project.addChild(sheet); 1742 1743 // navigate to the (0,0) cell having the text value "A" 1744 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 1745 view->goToCell(0, 0); 1746 1747 // check the initial selection 1748 auto indexes = view->selectionModel()->selectedIndexes(); 1749 QCOMPARE(indexes.count(), 1); 1750 auto curIndex = indexes.constFirst(); 1751 QCOMPARE(curIndex.row(), 0); 1752 QCOMPARE(curIndex.column(), 0); 1753 1754 // initialize the search&replace widget 1755 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 1756 searchReplaceWidget->setReplaceEnabled(true); 1757 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Text); 1758 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::ColumnMajor); 1759 const auto& firstIndex = indexes.constFirst(); 1760 const auto* column = sheet->column(firstIndex.column()); 1761 const int row = firstIndex.row(); 1762 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 1763 1764 // checks: the initial cell text is "A", we navigate with 'next' 1765 // and then back with 'prev' in the column-major order looking for "A" 1766 1767 // next 1768 searchReplaceWidget->findNext(true); 1769 indexes = view->selectionModel()->selectedIndexes(); 1770 QCOMPARE(indexes.count(), 1); 1771 curIndex = indexes.constFirst(); 1772 QCOMPARE(curIndex.row(), 2); 1773 QCOMPARE(curIndex.column(), 0); 1774 1775 // next 1776 searchReplaceWidget->findNext(true); 1777 indexes = view->selectionModel()->selectedIndexes(); 1778 QCOMPARE(indexes.count(), 1); 1779 curIndex = indexes.constFirst(); 1780 QCOMPARE(curIndex.row(), 1); 1781 QCOMPARE(curIndex.column(), 2); 1782 1783 // next, last matching cell reached 1784 searchReplaceWidget->findNext(true); 1785 indexes = view->selectionModel()->selectedIndexes(); 1786 QCOMPARE(indexes.count(), 1); 1787 curIndex = indexes.constFirst(); 1788 QCOMPARE(curIndex.row(), 1); 1789 QCOMPARE(curIndex.column(), 2); 1790 1791 // previous 1792 searchReplaceWidget->findPrevious(true); 1793 indexes = view->selectionModel()->selectedIndexes(); 1794 QCOMPARE(indexes.count(), 1); 1795 curIndex = indexes.constFirst(); 1796 QCOMPARE(curIndex.row(), 2); 1797 QCOMPARE(curIndex.column(), 0); 1798 1799 // previous 1800 searchReplaceWidget->findPrevious(true); 1801 indexes = view->selectionModel()->selectedIndexes(); 1802 QCOMPARE(indexes.count(), 1); 1803 curIndex = indexes.constFirst(); 1804 QCOMPARE(curIndex.row(), 0); 1805 QCOMPARE(curIndex.column(), 0); 1806 1807 // previous, last matching cell reached 1808 searchReplaceWidget->findPrevious(true); 1809 indexes = view->selectionModel()->selectedIndexes(); 1810 QCOMPARE(indexes.count(), 1); 1811 curIndex = indexes.constFirst(); 1812 QCOMPARE(curIndex.row(), 0); 1813 QCOMPARE(curIndex.column(), 0); 1814 } 1815 1816 /*! 1817 * extended search for Text, row-major order 1818 */ 1819 void SpreadsheetTest::testSearchExtended01() { 1820 Project project; 1821 auto* sheet = createSearchReplaceSpreadsheet(); 1822 project.addChild(sheet); 1823 1824 // navigate to the (0,0) cell having the text value "A" 1825 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 1826 view->goToCell(0, 0); 1827 1828 // check the initial selection 1829 auto indexes = view->selectionModel()->selectedIndexes(); 1830 QCOMPARE(indexes.count(), 1); 1831 auto curIndex = indexes.constFirst(); 1832 QCOMPARE(curIndex.row(), 0); 1833 QCOMPARE(curIndex.column(), 0); 1834 1835 // initialize the search&replace widget 1836 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 1837 searchReplaceWidget->setReplaceEnabled(true); 1838 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Text); 1839 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::RowMajor); 1840 const auto& firstIndex = indexes.constFirst(); 1841 const auto* column = sheet->column(firstIndex.column()); 1842 const int row = firstIndex.row(); 1843 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 1844 1845 // checks: the initial cell text is "A", we navigate with 'next' 1846 // and then back with 'prev' in the row-major order looking for "A" 1847 1848 // next 1849 searchReplaceWidget->findNext(true); 1850 indexes = view->selectionModel()->selectedIndexes(); 1851 QCOMPARE(indexes.count(), 1); 1852 curIndex = indexes.constFirst(); 1853 QCOMPARE(curIndex.row(), 1); 1854 QCOMPARE(curIndex.column(), 2); 1855 1856 // next 1857 searchReplaceWidget->findNext(true); 1858 indexes = view->selectionModel()->selectedIndexes(); 1859 QCOMPARE(indexes.count(), 1); 1860 curIndex = indexes.constFirst(); 1861 QCOMPARE(curIndex.row(), 2); 1862 QCOMPARE(curIndex.column(), 0); 1863 1864 // next, last matching cell reached 1865 searchReplaceWidget->findNext(true); 1866 indexes = view->selectionModel()->selectedIndexes(); 1867 QCOMPARE(indexes.count(), 1); 1868 curIndex = indexes.constFirst(); 1869 QCOMPARE(curIndex.row(), 2); 1870 QCOMPARE(curIndex.column(), 0); 1871 1872 // previous 1873 searchReplaceWidget->findPrevious(true); 1874 indexes = view->selectionModel()->selectedIndexes(); 1875 QCOMPARE(indexes.count(), 1); 1876 curIndex = indexes.constFirst(); 1877 QCOMPARE(curIndex.row(), 1); 1878 QCOMPARE(curIndex.column(), 2); 1879 1880 // previous 1881 searchReplaceWidget->findPrevious(true); 1882 indexes = view->selectionModel()->selectedIndexes(); 1883 QCOMPARE(indexes.count(), 1); 1884 curIndex = indexes.constFirst(); 1885 QCOMPARE(curIndex.row(), 0); 1886 QCOMPARE(curIndex.column(), 0); 1887 1888 // previous, last matching cell reached 1889 searchReplaceWidget->findPrevious(true); 1890 indexes = view->selectionModel()->selectedIndexes(); 1891 QCOMPARE(indexes.count(), 1); 1892 curIndex = indexes.constFirst(); 1893 QCOMPARE(curIndex.row(), 0); 1894 QCOMPARE(curIndex.column(), 0); 1895 } 1896 1897 /*! 1898 * search for Numeric, column-major order 1899 */ 1900 void SpreadsheetTest::testSearchExtended02() { 1901 Project project; 1902 auto* sheet = createSearchReplaceSpreadsheet(); 1903 project.addChild(sheet); 1904 1905 // navigate to the (1,1) cell having the numeric value "2" 1906 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 1907 view->goToCell(1, 1); 1908 1909 // check the initial selection 1910 auto indexes = view->selectionModel()->selectedIndexes(); 1911 QCOMPARE(indexes.count(), 1); 1912 auto curIndex = indexes.constFirst(); 1913 QCOMPARE(curIndex.row(), 1); 1914 QCOMPARE(curIndex.column(), 1); 1915 1916 // initialize the search&replace widget 1917 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 1918 searchReplaceWidget->setReplaceEnabled(true); 1919 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Numeric); 1920 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::ColumnMajor); 1921 const auto& firstIndex = indexes.constFirst(); 1922 const auto* column = sheet->column(firstIndex.column()); 1923 const int row = firstIndex.row(); 1924 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 1925 1926 // checks: the initial cell text is "2", we navigate with 'next' 1927 // and then back with 'prev' in the column-major order looking for "2" 1928 1929 // next 1930 searchReplaceWidget->findNext(true); 1931 indexes = view->selectionModel()->selectedIndexes(); 1932 QCOMPARE(indexes.count(), 1); 1933 curIndex = indexes.constFirst(); 1934 QCOMPARE(curIndex.row(), 3); 1935 QCOMPARE(curIndex.column(), 1); 1936 1937 // next 1938 searchReplaceWidget->findNext(true); 1939 indexes = view->selectionModel()->selectedIndexes(); 1940 QCOMPARE(indexes.count(), 1); 1941 curIndex = indexes.constFirst(); 1942 QCOMPARE(curIndex.row(), 2); 1943 QCOMPARE(curIndex.column(), 3); 1944 1945 // next, last matching cell reached 1946 searchReplaceWidget->findNext(true); 1947 indexes = view->selectionModel()->selectedIndexes(); 1948 QCOMPARE(indexes.count(), 1); 1949 curIndex = indexes.constFirst(); 1950 QCOMPARE(curIndex.row(), 2); 1951 QCOMPARE(curIndex.column(), 3); 1952 1953 // previous 1954 searchReplaceWidget->findPrevious(true); 1955 indexes = view->selectionModel()->selectedIndexes(); 1956 QCOMPARE(indexes.count(), 1); 1957 curIndex = indexes.constFirst(); 1958 QCOMPARE(curIndex.row(), 3); 1959 QCOMPARE(curIndex.column(), 1); 1960 1961 // previous 1962 searchReplaceWidget->findPrevious(true); 1963 indexes = view->selectionModel()->selectedIndexes(); 1964 QCOMPARE(indexes.count(), 1); 1965 curIndex = indexes.constFirst(); 1966 QCOMPARE(curIndex.row(), 1); 1967 QCOMPARE(curIndex.column(), 1); 1968 1969 // previous, last matching cell reached 1970 searchReplaceWidget->findPrevious(true); 1971 indexes = view->selectionModel()->selectedIndexes(); 1972 QCOMPARE(indexes.count(), 1); 1973 curIndex = indexes.constFirst(); 1974 QCOMPARE(curIndex.row(), 1); 1975 QCOMPARE(curIndex.column(), 1); 1976 } 1977 1978 /*! 1979 * search for Numeric, row major 1980 */ 1981 void SpreadsheetTest::testSearchExtended03() { 1982 Project project; 1983 auto* sheet = createSearchReplaceSpreadsheet(); 1984 project.addChild(sheet); 1985 1986 // navigate to the (1,1) cell having the numeric value "2" 1987 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 1988 view->goToCell(1, 1); 1989 1990 // check the initial selection 1991 auto indexes = view->selectionModel()->selectedIndexes(); 1992 QCOMPARE(indexes.count(), 1); 1993 auto curIndex = indexes.constFirst(); 1994 QCOMPARE(curIndex.row(), 1); 1995 QCOMPARE(curIndex.column(), 1); 1996 1997 // initialize the search&replace widget 1998 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 1999 searchReplaceWidget->setReplaceEnabled(true); 2000 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Numeric); 2001 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::RowMajor); 2002 const auto& firstIndex = indexes.constFirst(); 2003 const auto* column = sheet->column(firstIndex.column()); 2004 const int row = firstIndex.row(); 2005 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 2006 2007 // checks: the initial cell text is "2", we navigate with 'next' 2008 // and then back with 'prev' in the column-major order looking for "2" 2009 2010 // next 2011 searchReplaceWidget->findNext(true); 2012 indexes = view->selectionModel()->selectedIndexes(); 2013 QCOMPARE(indexes.count(), 1); 2014 curIndex = indexes.constFirst(); 2015 QCOMPARE(curIndex.row(), 2); 2016 QCOMPARE(curIndex.column(), 3); 2017 2018 // next 2019 searchReplaceWidget->findNext(true); 2020 indexes = view->selectionModel()->selectedIndexes(); 2021 QCOMPARE(indexes.count(), 1); 2022 curIndex = indexes.constFirst(); 2023 QCOMPARE(curIndex.row(), 3); 2024 QCOMPARE(curIndex.column(), 1); 2025 2026 // next, last matching cell reached 2027 searchReplaceWidget->findNext(true); 2028 indexes = view->selectionModel()->selectedIndexes(); 2029 QCOMPARE(indexes.count(), 1); 2030 curIndex = indexes.constFirst(); 2031 QCOMPARE(curIndex.row(), 3); 2032 QCOMPARE(curIndex.column(), 1); 2033 2034 // previous 2035 searchReplaceWidget->findPrevious(true); 2036 indexes = view->selectionModel()->selectedIndexes(); 2037 QCOMPARE(indexes.count(), 1); 2038 curIndex = indexes.constFirst(); 2039 QCOMPARE(curIndex.row(), 2); 2040 QCOMPARE(curIndex.column(), 3); 2041 2042 // previous 2043 searchReplaceWidget->findPrevious(true); 2044 indexes = view->selectionModel()->selectedIndexes(); 2045 QCOMPARE(indexes.count(), 1); 2046 curIndex = indexes.constFirst(); 2047 QCOMPARE(curIndex.row(), 1); 2048 QCOMPARE(curIndex.column(), 1); 2049 2050 // previous, last matching cell reached 2051 searchReplaceWidget->findPrevious(true); 2052 indexes = view->selectionModel()->selectedIndexes(); 2053 QCOMPARE(indexes.count(), 1); 2054 curIndex = indexes.constFirst(); 2055 QCOMPARE(curIndex.row(), 1); 2056 QCOMPARE(curIndex.column(), 1); 2057 } 2058 2059 void SpreadsheetTest::testSearchFindAll() { 2060 Project project; 2061 auto* sheet = createSearchReplaceSpreadsheet(); 2062 project.addChild(sheet); 2063 2064 // initialize the search&replace widget 2065 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, nullptr); 2066 searchReplaceWidget->setReplaceEnabled(true); 2067 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Text); 2068 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::ColumnMajor); 2069 searchReplaceWidget->setTextOperator(SearchReplaceWidget::OperatorText::StartsWith); 2070 2071 // search for cells starting with "B" and highlight them ("find") 2072 searchReplaceWidget->setInitialPattern(AbstractColumn::ColumnMode::Text, QLatin1String("B")); 2073 searchReplaceWidget->findAll(); 2074 2075 // checks 2076 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 2077 auto indexes = view->selectionModel()->selectedIndexes(); 2078 QCOMPARE(indexes.size(), 2); 2079 QCOMPARE(indexes.at(0).row(), 1); 2080 QCOMPARE(indexes.at(0).column(), 0); 2081 QCOMPARE(indexes.at(1).row(), 0); 2082 QCOMPARE(indexes.at(1).column(), 2); 2083 } 2084 2085 /*! 2086 * replace a numeric value in int and double columns via "replace next" 2087 */ 2088 void SpreadsheetTest::testSearchReplaceNumeric() { 2089 Project project; 2090 auto* sheet = createSearchReplaceSpreadsheet(); 2091 project.addChild(sheet); 2092 2093 // navigate to the (1,1) cell having the numeric value 2 2094 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 2095 view->goToCell(1, 1); 2096 2097 // check the initial selection 2098 auto indexes = view->selectionModel()->selectedIndexes(); 2099 QCOMPARE(indexes.count(), 1); 2100 auto curIndex = indexes.constFirst(); 2101 QCOMPARE(curIndex.row(), 1); 2102 QCOMPARE(curIndex.column(), 1); 2103 2104 // initialize the search&replace widget 2105 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 2106 searchReplaceWidget->setReplaceEnabled(true); 2107 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Numeric); 2108 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::ColumnMajor); 2109 searchReplaceWidget->setReplaceText(QLatin1String("5")); 2110 const auto& firstIndex = indexes.constFirst(); 2111 const auto* column = sheet->column(firstIndex.column()); 2112 const int row = firstIndex.row(); 2113 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 2114 2115 // checks: the initial cell text is (1,1) with the value 2, we replace this value with 5 via replaceNext, 2116 // and proceed to other cells with further replaceNext calls. 2117 const auto& columns = sheet->children<Column>(); 2118 2119 // replace next - the value in the currently selected cell should be replaced first 2120 searchReplaceWidget->replaceNext(); 2121 indexes = view->selectionModel()->selectedIndexes(); 2122 QCOMPARE(indexes.count(), 1); 2123 curIndex = indexes.constFirst(); 2124 QCOMPARE(curIndex.row(), 1); 2125 QCOMPARE(curIndex.column(), 1); 2126 QCOMPARE(columns.at(1)->integerAt(1), 5); 2127 2128 // replace next 2129 searchReplaceWidget->replaceNext(); 2130 indexes = view->selectionModel()->selectedIndexes(); 2131 QCOMPARE(indexes.count(), 1); 2132 curIndex = indexes.constFirst(); 2133 QCOMPARE(curIndex.row(), 3); 2134 QCOMPARE(curIndex.column(), 1); 2135 QCOMPARE(columns.at(1)->integerAt(1), 5); 2136 2137 // replace next 2138 searchReplaceWidget->replaceNext(); 2139 indexes = view->selectionModel()->selectedIndexes(); 2140 QCOMPARE(indexes.count(), 1); 2141 curIndex = indexes.constFirst(); 2142 QCOMPARE(curIndex.row(), 2); 2143 QCOMPARE(curIndex.column(), 3); 2144 QCOMPARE(columns.at(3)->valueAt(2), 5); 2145 } 2146 2147 /*! 2148 * replace a text value in text columns via "replace next" 2149 */ 2150 void SpreadsheetTest::testSearchReplaceText() { 2151 Project project; 2152 auto* sheet = createSearchReplaceSpreadsheet(); 2153 project.addChild(sheet); 2154 2155 // navigate to the (0,0) cell having the text value "A" 2156 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 2157 view->goToCell(0, 0); 2158 2159 // check the initial selection 2160 auto indexes = view->selectionModel()->selectedIndexes(); 2161 QCOMPARE(indexes.count(), 1); 2162 auto curIndex = indexes.constFirst(); 2163 QCOMPARE(curIndex.row(), 0); 2164 QCOMPARE(curIndex.column(), 0); 2165 2166 // initialize the search&replace widget 2167 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, view); 2168 searchReplaceWidget->setReplaceEnabled(true); 2169 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Text); 2170 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::ColumnMajor); 2171 searchReplaceWidget->setReplaceText(QLatin1String("AAA")); 2172 const auto& firstIndex = indexes.constFirst(); 2173 const auto* column = sheet->column(firstIndex.column()); 2174 const int row = firstIndex.row(); 2175 searchReplaceWidget->setInitialPattern(column->columnMode(), column->asStringColumn()->textAt(row)); 2176 2177 // checks: the initial cell text is (0,0) with the value "A", we replace this value with "AAA" via replaceNext, 2178 // and proceed to other cells with further replaceNext calls. 2179 const auto& columns = sheet->children<Column>(); 2180 2181 // replace next - the value in the currently selected cell should be replaced first 2182 searchReplaceWidget->replaceNext(); 2183 indexes = view->selectionModel()->selectedIndexes(); 2184 QCOMPARE(indexes.count(), 1); 2185 curIndex = indexes.constFirst(); 2186 QCOMPARE(curIndex.row(), 0); 2187 QCOMPARE(curIndex.column(), 0); 2188 QCOMPARE(columns.at(0)->textAt(0), QLatin1String("AAA")); 2189 2190 // replace next 2191 searchReplaceWidget->replaceNext(); 2192 indexes = view->selectionModel()->selectedIndexes(); 2193 QCOMPARE(indexes.count(), 1); 2194 curIndex = indexes.constFirst(); 2195 QCOMPARE(curIndex.row(), 2); 2196 QCOMPARE(curIndex.column(), 0); 2197 QCOMPARE(columns.at(0)->textAt(2), QLatin1String("AAA")); 2198 2199 // replace next 2200 searchReplaceWidget->replaceNext(); 2201 indexes = view->selectionModel()->selectedIndexes(); 2202 QCOMPARE(indexes.count(), 1); 2203 curIndex = indexes.constFirst(); 2204 QCOMPARE(curIndex.row(), 1); 2205 QCOMPARE(curIndex.column(), 2); 2206 QCOMPARE(columns.at(2)->textAt(1), QLatin1String("AAA")); 2207 } 2208 2209 void SpreadsheetTest::testSearchReplaceAll() { 2210 Project project; 2211 auto* sheet = createSearchReplaceSpreadsheet(); 2212 project.addChild(sheet); 2213 2214 // initialize the search&replace widget 2215 auto* searchReplaceWidget = new SearchReplaceWidget(sheet, nullptr); 2216 searchReplaceWidget->setReplaceEnabled(true); 2217 searchReplaceWidget->setDataType(SearchReplaceWidget::DataType::Text); 2218 searchReplaceWidget->setOrder(SearchReplaceWidget::Order::ColumnMajor); 2219 searchReplaceWidget->setTextOperator(SearchReplaceWidget::OperatorText::RegEx); 2220 2221 // search for "A" or "C" and replace with "test" 2222 searchReplaceWidget->setInitialPattern(AbstractColumn::ColumnMode::Text, QLatin1String("[A.C]")); 2223 searchReplaceWidget->setReplaceText(QLatin1String("test")); 2224 searchReplaceWidget->replaceAll(); 2225 2226 // checks 2227 const auto& columns = sheet->children<Column>(); 2228 QCOMPARE(columns.at(0)->textAt(0), QLatin1String("test")); 2229 QCOMPARE(columns.at(0)->textAt(2), QLatin1String("test")); 2230 QCOMPARE(columns.at(2)->textAt(1), QLatin1String("test")); 2231 QCOMPARE(columns.at(2)->textAt(2), QLatin1String("test")); 2232 } 2233 2234 // ********************************************************** 2235 // ********************** size changes ********************* 2236 // ********************************************************** 2237 void SpreadsheetTest::testInsertRows() { 2238 Project project; 2239 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2240 project.addChild(sheet); 2241 2242 auto* model = new SpreadsheetModel(sheet); 2243 int rowsAboutToBeInsertedCounter = 0; 2244 connect(model, &SpreadsheetModel::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter]() { 2245 rowsAboutToBeInsertedCounter++; 2246 }); 2247 int rowsInsertedCounter = 0; 2248 connect(model, &SpreadsheetModel::rowsInserted, [&rowsInsertedCounter]() { 2249 rowsInsertedCounter++; 2250 }); 2251 int rowsAboutToBeRemovedCounter = 0; 2252 connect(model, &SpreadsheetModel::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter]() { 2253 rowsAboutToBeRemovedCounter++; 2254 }); 2255 int rowsRemovedCounter = 0; 2256 connect(model, &SpreadsheetModel::rowsRemoved, [&rowsRemovedCounter]() { 2257 rowsRemovedCounter++; 2258 }); 2259 2260 QCOMPARE(sheet->rowCount(), 100); 2261 QCOMPARE(model->rowCount(), 100); 2262 sheet->setRowCount(101); // No crash shall happen 2263 QCOMPARE(sheet->rowCount(), 101); 2264 QCOMPARE(model->rowCount(), 101); 2265 2266 sheet->undoStack()->undo(); 2267 QCOMPARE(sheet->rowCount(), 100); 2268 QCOMPARE(model->rowCount(), 100); 2269 sheet->undoStack()->redo(); 2270 QCOMPARE(sheet->rowCount(), 101); 2271 QCOMPARE(model->rowCount(), 101); 2272 2273 QCOMPARE(rowsAboutToBeInsertedCounter, 2); // set and redo() 2274 QCOMPARE(rowsInsertedCounter, 2); // set and redo() 2275 QCOMPARE(rowsAboutToBeRemovedCounter, 1); // undo() 2276 QCOMPARE(rowsRemovedCounter, 1); // undo() 2277 } 2278 2279 void SpreadsheetTest::testRemoveRows() { 2280 Project project; 2281 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2282 project.addChild(sheet); 2283 2284 auto* model = new SpreadsheetModel(sheet); 2285 int rowsAboutToBeInsertedCounter = 0; 2286 connect(model, &SpreadsheetModel::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter]() { 2287 rowsAboutToBeInsertedCounter++; 2288 }); 2289 int rowsInsertedCounter = 0; 2290 connect(model, &SpreadsheetModel::rowsInserted, [&rowsInsertedCounter]() { 2291 rowsInsertedCounter++; 2292 }); 2293 int rowsAboutToBeRemovedCounter = 0; 2294 connect(model, &SpreadsheetModel::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter]() { 2295 rowsAboutToBeRemovedCounter++; 2296 }); 2297 int rowsRemovedCounter = 0; 2298 connect(model, &SpreadsheetModel::rowsRemoved, [&rowsRemovedCounter]() { 2299 rowsRemovedCounter++; 2300 }); 2301 2302 QCOMPARE(sheet->rowCount(), 100); 2303 QCOMPARE(model->rowCount(), 100); 2304 sheet->setRowCount(10); // No crash shall happen 2305 QCOMPARE(sheet->rowCount(), 10); 2306 QCOMPARE(model->rowCount(), 10); 2307 2308 sheet->undoStack()->undo(); 2309 QCOMPARE(sheet->rowCount(), 100); 2310 QCOMPARE(model->rowCount(), 100); 2311 sheet->undoStack()->redo(); 2312 QCOMPARE(sheet->rowCount(), 10); 2313 QCOMPARE(model->rowCount(), 10); 2314 2315 QCOMPARE(rowsAboutToBeInsertedCounter, 1); // undo 2316 QCOMPARE(rowsInsertedCounter, 1); // undo 2317 QCOMPARE(rowsAboutToBeRemovedCounter, 2); // set and redo() 2318 QCOMPARE(rowsRemovedCounter, 2); // set and redo() 2319 } 2320 2321 void SpreadsheetTest::testInsertRowsBegin() { 2322 Project project; 2323 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2324 project.addChild(sheet); 2325 2326 auto* model = new SpreadsheetModel(sheet); 2327 int rowsAboutToBeInsertedCounter = 0; 2328 connect(model, &SpreadsheetModel::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter]() { 2329 rowsAboutToBeInsertedCounter++; 2330 }); 2331 int rowsInsertedCounter = 0; 2332 connect(model, &SpreadsheetModel::rowsInserted, [&rowsInsertedCounter]() { 2333 rowsInsertedCounter++; 2334 }); 2335 int rowsAboutToBeRemovedCounter = 0; 2336 connect(model, &SpreadsheetModel::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter]() { 2337 rowsAboutToBeRemovedCounter++; 2338 }); 2339 int rowsRemovedCounter = 0; 2340 connect(model, &SpreadsheetModel::rowsRemoved, [&rowsRemovedCounter]() { 2341 rowsRemovedCounter++; 2342 }); 2343 2344 QCOMPARE(sheet->rowCount(), 100); 2345 QCOMPARE(model->rowCount(), 100); 2346 sheet->insertRows(0, 1); // No crash shall happen 2347 QCOMPARE(sheet->rowCount(), 101); 2348 QCOMPARE(model->rowCount(), 101); 2349 2350 sheet->undoStack()->undo(); 2351 QCOMPARE(sheet->rowCount(), 100); 2352 QCOMPARE(model->rowCount(), 100); 2353 sheet->undoStack()->redo(); 2354 QCOMPARE(sheet->rowCount(), 101); 2355 QCOMPARE(model->rowCount(), 101); 2356 2357 QCOMPARE(rowsAboutToBeInsertedCounter, 2); // set and redo() 2358 QCOMPARE(rowsInsertedCounter, 2); // set and redo() 2359 QCOMPARE(rowsAboutToBeRemovedCounter, 1); // undo() 2360 QCOMPARE(rowsRemovedCounter, 1); // undo() 2361 } 2362 void SpreadsheetTest::testRemoveRowsBegin() { 2363 Project project; 2364 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2365 project.addChild(sheet); 2366 2367 auto* model = new SpreadsheetModel(sheet); 2368 int rowsAboutToBeInsertedCounter = 0; 2369 connect(model, &SpreadsheetModel::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter]() { 2370 rowsAboutToBeInsertedCounter++; 2371 }); 2372 int rowsInsertedCounter = 0; 2373 connect(model, &SpreadsheetModel::rowsInserted, [&rowsInsertedCounter]() { 2374 rowsInsertedCounter++; 2375 }); 2376 int rowsAboutToBeRemovedCounter = 0; 2377 connect(model, &SpreadsheetModel::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter]() { 2378 rowsAboutToBeRemovedCounter++; 2379 }); 2380 int rowsRemovedCounter = 0; 2381 connect(model, &SpreadsheetModel::rowsRemoved, [&rowsRemovedCounter]() { 2382 rowsRemovedCounter++; 2383 }); 2384 2385 QCOMPARE(sheet->rowCount(), 100); 2386 QCOMPARE(model->rowCount(), 100); 2387 sheet->removeRows(0, 1); 2388 QCOMPARE(sheet->rowCount(), 99); 2389 QCOMPARE(model->rowCount(), 99); 2390 2391 sheet->undoStack()->undo(); 2392 QCOMPARE(sheet->rowCount(), 100); 2393 QCOMPARE(model->rowCount(), 100); 2394 sheet->undoStack()->redo(); 2395 QCOMPARE(sheet->rowCount(), 99); 2396 QCOMPARE(model->rowCount(), 99); 2397 2398 QCOMPARE(rowsAboutToBeInsertedCounter, 1); // undo 2399 QCOMPARE(rowsInsertedCounter, 1); // undo 2400 QCOMPARE(rowsAboutToBeRemovedCounter, 2); // set and redo() 2401 QCOMPARE(rowsRemovedCounter, 2); // set and redo() 2402 } 2403 2404 void SpreadsheetTest::testInsertColumns() { 2405 Project project; 2406 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2407 project.addChild(sheet); 2408 2409 auto* model = new SpreadsheetModel(sheet); 2410 2411 int columnsAboutToBeInsertedCounter = 0; 2412 connect(model, &SpreadsheetModel::columnsAboutToBeInserted, [&columnsAboutToBeInsertedCounter]() { 2413 columnsAboutToBeInsertedCounter++; 2414 }); 2415 int columnsInsertedCounter = 0; 2416 connect(model, &SpreadsheetModel::columnsInserted, [&columnsInsertedCounter]() { 2417 columnsInsertedCounter++; 2418 }); 2419 int columnsAboutToBeRemovedCounter = 0; 2420 connect(model, &SpreadsheetModel::columnsAboutToBeRemoved, [&columnsAboutToBeRemovedCounter]() { 2421 columnsAboutToBeRemovedCounter++; 2422 }); 2423 int columnsRemovedCounter = 0; 2424 connect(model, &SpreadsheetModel::columnsRemoved, [&columnsRemovedCounter]() { 2425 columnsRemovedCounter++; 2426 }); 2427 2428 QCOMPARE(sheet->columnCount(), 2); 2429 QCOMPARE(model->columnCount(), 2); 2430 sheet->setColumnCount(5); // No crash shall happen 2431 QCOMPARE(sheet->columnCount(), 5); 2432 QCOMPARE(model->columnCount(), 5); 2433 2434 sheet->undoStack()->undo(); 2435 QCOMPARE(sheet->columnCount(), 2); 2436 QCOMPARE(model->columnCount(), 2); 2437 sheet->undoStack()->redo(); 2438 QCOMPARE(sheet->columnCount(), 5); 2439 QCOMPARE(model->columnCount(), 5); 2440 2441 QCOMPARE(columnsAboutToBeInsertedCounter, 2); // set and redo() 2442 QCOMPARE(columnsInsertedCounter, 2); // set and redo() 2443 QCOMPARE(columnsRemovedCounter, 1); // undo() 2444 QCOMPARE(columnsAboutToBeRemovedCounter, 1); // undo() 2445 } 2446 2447 void SpreadsheetTest::testRemoveColumns() { 2448 Project project; 2449 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2450 project.addChild(sheet); 2451 2452 auto* model = new SpreadsheetModel(sheet); 2453 2454 int columnsAboutToBeInsertedCounter = 0; 2455 connect(model, &SpreadsheetModel::columnsAboutToBeInserted, [&columnsAboutToBeInsertedCounter]() { 2456 columnsAboutToBeInsertedCounter++; 2457 }); 2458 int columnsInsertedCounter = 0; 2459 connect(model, &SpreadsheetModel::columnsInserted, [&columnsInsertedCounter]() { 2460 columnsInsertedCounter++; 2461 }); 2462 int columnsAboutToBeRemovedCounter = 0; 2463 connect(model, &SpreadsheetModel::columnsAboutToBeRemoved, [&columnsAboutToBeRemovedCounter]() { 2464 columnsAboutToBeRemovedCounter++; 2465 }); 2466 int columnsRemovedCounter = 0; 2467 connect(model, &SpreadsheetModel::columnsRemoved, [&columnsRemovedCounter]() { 2468 columnsRemovedCounter++; 2469 }); 2470 2471 QCOMPARE(sheet->columnCount(), 2); 2472 QCOMPARE(model->columnCount(), 2); 2473 sheet->setColumnCount(1); // No crash shall happen 2474 QCOMPARE(sheet->columnCount(), 1); 2475 QCOMPARE(model->columnCount(), 1); 2476 2477 sheet->undoStack()->undo(); 2478 QCOMPARE(sheet->columnCount(), 2); 2479 QCOMPARE(model->columnCount(), 2); 2480 sheet->undoStack()->redo(); 2481 QCOMPARE(sheet->columnCount(), 1); 2482 QCOMPARE(model->columnCount(), 1); 2483 2484 QCOMPARE(columnsAboutToBeInsertedCounter, 1); // undo() 2485 QCOMPARE(columnsInsertedCounter, 1); // undo() 2486 QCOMPARE(columnsRemovedCounter, 2); // set and redo() 2487 QCOMPARE(columnsAboutToBeRemovedCounter, 2); // set and redo() 2488 } 2489 2490 /*! 2491 * \brief testInsertRowsSuppressUpdate 2492 * It shall not crash 2493 * Testing if in the model begin and end are used properly 2494 */ 2495 void SpreadsheetTest::testInsertRowsSuppressUpdate() { 2496 Project project; 2497 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2498 project.addChild(sheet); 2499 2500 auto* model = new SpreadsheetModel(sheet); 2501 2502 int rowsAboutToBeInsertedCounter = 0; 2503 connect(model, &SpreadsheetModel::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter]() { 2504 rowsAboutToBeInsertedCounter++; 2505 }); 2506 int rowsInsertedCounter = 0; 2507 connect(model, &SpreadsheetModel::rowsInserted, [&rowsInsertedCounter]() { 2508 rowsInsertedCounter++; 2509 }); 2510 int rowsAboutToBeRemovedCounter = 0; 2511 connect(model, &SpreadsheetModel::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter]() { 2512 rowsAboutToBeRemovedCounter++; 2513 }); 2514 int rowsRemovedCounter = 0; 2515 connect(model, &SpreadsheetModel::rowsRemoved, [&rowsRemovedCounter]() { 2516 rowsRemovedCounter++; 2517 }); 2518 2519 int modelResetCounter = 0; 2520 connect(model, &SpreadsheetModel::modelReset, [&modelResetCounter]() { 2521 modelResetCounter++; 2522 }); 2523 int modelAboutToResetCounter = 0; 2524 connect(model, &SpreadsheetModel::modelAboutToBeReset, [&modelAboutToResetCounter]() { 2525 modelAboutToResetCounter++; 2526 }); 2527 2528 model->suppressSignals(true); 2529 2530 QCOMPARE(sheet->rowCount(), 100); 2531 sheet->setRowCount(101); // No crash shall happen 2532 QCOMPARE(sheet->rowCount(), 101); 2533 2534 sheet->undoStack()->undo(); 2535 QCOMPARE(sheet->rowCount(), 100); 2536 sheet->undoStack()->redo(); 2537 QCOMPARE(sheet->rowCount(), 101); 2538 2539 model->suppressSignals(false); 2540 2541 QCOMPARE(rowsAboutToBeInsertedCounter, 0); 2542 QCOMPARE(rowsInsertedCounter, 0); 2543 QCOMPARE(rowsAboutToBeRemovedCounter, 0); 2544 QCOMPARE(rowsRemovedCounter, 0); 2545 QCOMPARE(modelResetCounter, 1); 2546 QCOMPARE(modelAboutToResetCounter, 1); 2547 } 2548 2549 /*! 2550 * \brief testInsertColumnsSuppressUpdate 2551 * It shall not crash 2552 * Testing if in the model begin and end are used properly 2553 */ 2554 void SpreadsheetTest::testInsertColumnsSuppressUpdate() { 2555 Project project; 2556 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 2557 project.addChild(sheet); 2558 2559 auto* model = new SpreadsheetModel(sheet); 2560 2561 int columnsAboutToBeInsertedCounter = 0; 2562 connect(model, &SpreadsheetModel::columnsAboutToBeInserted, [&columnsAboutToBeInsertedCounter]() { 2563 columnsAboutToBeInsertedCounter++; 2564 }); 2565 int columnsInsertedCounter = 0; 2566 connect(model, &SpreadsheetModel::columnsInserted, [&columnsInsertedCounter]() { 2567 columnsInsertedCounter++; 2568 }); 2569 int columnsAboutToBeRemovedCounter = 0; 2570 connect(model, &SpreadsheetModel::columnsAboutToBeRemoved, [&columnsAboutToBeRemovedCounter]() { 2571 columnsAboutToBeRemovedCounter++; 2572 }); 2573 int columnsRemovedCounter = 0; 2574 connect(model, &SpreadsheetModel::columnsRemoved, [&columnsRemovedCounter]() { 2575 columnsRemovedCounter++; 2576 }); 2577 2578 int modelResetCounter = 0; 2579 connect(model, &SpreadsheetModel::modelReset, [&modelResetCounter]() { 2580 modelResetCounter++; 2581 }); 2582 int modelAboutToResetCounter = 0; 2583 connect(model, &SpreadsheetModel::modelAboutToBeReset, [&modelAboutToResetCounter]() { 2584 modelAboutToResetCounter++; 2585 }); 2586 2587 model->suppressSignals(true); 2588 2589 QCOMPARE(sheet->columnCount(), 2); 2590 sheet->setColumnCount(5); // No crash shall happen 2591 QCOMPARE(sheet->columnCount(), 5); 2592 2593 sheet->undoStack()->undo(); 2594 QCOMPARE(sheet->columnCount(), 2); 2595 sheet->undoStack()->redo(); 2596 QCOMPARE(sheet->columnCount(), 5); 2597 2598 model->suppressSignals(false); 2599 2600 QCOMPARE(columnsAboutToBeInsertedCounter, 0); 2601 QCOMPARE(columnsInsertedCounter, 0); 2602 QCOMPARE(columnsRemovedCounter, 0); 2603 QCOMPARE(columnsAboutToBeRemovedCounter, 0); 2604 QCOMPARE(modelResetCounter, 1); 2605 QCOMPARE(modelAboutToResetCounter, 1); 2606 } 2607 2608 void SpreadsheetTest::testLinkSpreadsheetsUndoRedo() { 2609 #ifdef __FreeBSD__ 2610 return; 2611 #endif 2612 Project project; 2613 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2614 project.addChild(sheetData); 2615 sheetData->setColumnCount(3); 2616 sheetData->setRowCount(10); 2617 2618 auto* sheetData2 = new Spreadsheet(QStringLiteral("data2"), false); 2619 project.addChild(sheetData2); 2620 sheetData2->setColumnCount(3); 2621 sheetData2->setRowCount(100); 2622 2623 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2624 project.addChild(sheetCalculations); 2625 sheetCalculations->setColumnCount(3); 2626 sheetCalculations->setRowCount(2); 2627 2628 SpreadsheetDock dock(nullptr); 2629 dock.setSpreadsheets({sheetCalculations}); 2630 auto* modelSheetCalculations = new SpreadsheetModel(sheetCalculations); 2631 2632 QCOMPARE(dock.ui.cbLinkingEnabled->isChecked(), false); 2633 QCOMPARE(dock.ui.cbLinkedSpreadsheet->isVisible(), false); 2634 QCOMPARE(dock.ui.sbRowCount->isEnabled(), true); 2635 QCOMPARE(dock.m_spreadsheet->linking(), false); 2636 2637 dock.ui.cbLinkingEnabled->toggled(true); 2638 2639 // QCOMPARE(dock.ui.cbLinkingEnabled->isChecked(), true); // does not work here. Don't know why 2640 // QCOMPARE(dock.ui.cbLinkedSpreadsheet->isVisible(), true); 2641 // QCOMPARE(dock.ui.sbRowCount->isEnabled(), false); 2642 QCOMPARE(sheetCalculations->linking(), true); 2643 QCOMPARE(sheetCalculations->linkedSpreadsheet(), nullptr); 2644 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), QLatin1String()); 2645 QCOMPARE(sheetCalculations->rowCount(), 2); 2646 QCOMPARE(modelSheetCalculations->rowCount(), 2); 2647 2648 const auto index = dock.aspectModel()->modelIndexOfAspect(sheetData); 2649 QCOMPARE(index.isValid(), true); 2650 // dock.ui.cbLinkedSpreadsheet->setCurrentModelIndex(index); // Does not trigger the slot 2651 sheetCalculations->setLinkedSpreadsheet(sheetData); 2652 2653 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2654 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2655 QCOMPARE(sheetCalculations->rowCount(), 10); 2656 QCOMPARE(modelSheetCalculations->rowCount(), 10); 2657 2658 sheetCalculations->setLinkedSpreadsheet(sheetData2); 2659 2660 QCOMPARE(sheetCalculations->linking(), true); 2661 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData2); 2662 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData2->path()); 2663 QCOMPARE(sheetCalculations->rowCount(), 100); 2664 QCOMPARE(modelSheetCalculations->rowCount(), 100); 2665 2666 sheetCalculations->undoStack()->undo(); 2667 2668 QCOMPARE(sheetCalculations->linking(), true); 2669 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2670 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2671 QCOMPARE(sheetCalculations->rowCount(), 10); 2672 QCOMPARE(modelSheetCalculations->rowCount(), 10); 2673 2674 sheetCalculations->undoStack()->redo(); 2675 2676 QCOMPARE(sheetCalculations->linking(), true); 2677 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData2); 2678 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData2->path()); 2679 QCOMPARE(sheetCalculations->rowCount(), 100); 2680 QCOMPARE(modelSheetCalculations->rowCount(), 100); 2681 2682 sheetCalculations->undoStack()->undo(); // first undo 2683 QCOMPARE(sheetCalculations->linking(), true); 2684 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2685 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2686 QCOMPARE(sheetCalculations->rowCount(), 10); 2687 QCOMPARE(modelSheetCalculations->rowCount(), 10); 2688 2689 sheetCalculations->undoStack()->undo(); 2690 2691 QCOMPARE(sheetCalculations->linking(), true); 2692 QCOMPARE(sheetCalculations->linkedSpreadsheet(), nullptr); // No linked spreadsheet anymore 2693 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), QLatin1String()); 2694 QCOMPARE(sheetCalculations->rowCount(), 2); // Go back to original row count 2695 QCOMPARE(modelSheetCalculations->rowCount(), 2); 2696 2697 sheetCalculations->undoStack()->undo(); 2698 QCOMPARE(sheetCalculations->linking(), false); 2699 QCOMPARE(sheetCalculations->linkedSpreadsheet(), nullptr); 2700 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), QLatin1String()); 2701 QCOMPARE(sheetCalculations->rowCount(), 2); 2702 QCOMPARE(modelSheetCalculations->rowCount(), 2); 2703 } 2704 2705 void SpreadsheetTest::testLinkSpreadsheetDeleteAdd() { 2706 #ifdef __FreeBSD__ 2707 return; 2708 #endif 2709 Project project; 2710 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2711 project.addChild(sheetData); 2712 sheetData->setColumnCount(3); 2713 sheetData->setRowCount(10); 2714 2715 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2716 project.addChild(sheetCalculations); 2717 sheetCalculations->setColumnCount(3); 2718 sheetCalculations->setRowCount(2); 2719 2720 SpreadsheetDock dock(nullptr); 2721 dock.setSpreadsheets({sheetCalculations}); 2722 2723 QCOMPARE(dock.ui.cbLinkingEnabled->isChecked(), false); 2724 QCOMPARE(dock.ui.cbLinkedSpreadsheet->isVisible(), false); 2725 QCOMPARE(dock.ui.sbRowCount->isEnabled(), true); 2726 QCOMPARE(sheetCalculations->linking(), false); 2727 2728 Q_EMIT dock.ui.cbLinkingEnabled->toggled(true); 2729 2730 sheetCalculations->setLinkedSpreadsheet(sheetData); 2731 2732 QCOMPARE(sheetCalculations->linking(), true); 2733 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2734 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2735 QCOMPARE(sheetCalculations->rowCount(), 10); 2736 2737 sheetData->remove(); 2738 sheetData->setRowCount(100); 2739 2740 QCOMPARE(sheetCalculations->linking(), true); 2741 QCOMPARE(sheetCalculations->linkedSpreadsheet(), nullptr); 2742 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), QStringLiteral("Project/data")); 2743 QCOMPARE(sheetCalculations->rowCount(), 10); // does not change 2744 2745 auto* sheetDataNew = new Spreadsheet(QStringLiteral("data"), false); 2746 sheetDataNew->setColumnCount(3); 2747 sheetDataNew->setRowCount(12); 2748 project.addChild(sheetDataNew); 2749 2750 QCOMPARE(sheetCalculations->linking(), true); 2751 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetDataNew); 2752 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetDataNew->path()); 2753 QCOMPARE(sheetCalculations->rowCount(), 12); 2754 } 2755 2756 void SpreadsheetTest::testLinkSpreadsheetAddRow() { 2757 #ifdef __FreeBSD__ 2758 return; 2759 #endif 2760 Project project; 2761 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2762 project.addChild(sheetData); 2763 sheetData->setColumnCount(3); 2764 sheetData->setRowCount(10); 2765 2766 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2767 project.addChild(sheetCalculations); 2768 sheetCalculations->setColumnCount(3); 2769 sheetCalculations->setRowCount(2); 2770 2771 SpreadsheetDock dock(nullptr); 2772 dock.setSpreadsheets({sheetCalculations}); 2773 2774 QCOMPARE(dock.ui.cbLinkingEnabled->isChecked(), false); 2775 QCOMPARE(dock.ui.cbLinkedSpreadsheet->isVisible(), false); 2776 QCOMPARE(dock.ui.sbRowCount->isEnabled(), true); 2777 QCOMPARE(sheetCalculations->linking(), false); 2778 2779 Q_EMIT dock.ui.cbLinkingEnabled->toggled(true); 2780 2781 sheetCalculations->setLinkedSpreadsheet(sheetData); 2782 2783 QCOMPARE(sheetCalculations->linking(), true); 2784 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2785 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2786 QCOMPARE(sheetCalculations->rowCount(), 10); 2787 2788 new SpreadsheetModel(sheetData); // otherwise emitRowCountChanged will not be called 2789 sheetData->setRowCount(13); 2790 2791 QCOMPARE(sheetCalculations->linking(), true); 2792 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2793 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2794 QCOMPARE(sheetCalculations->rowCount(), 13); 2795 } 2796 2797 void SpreadsheetTest::testLinkSpreadsheetRemoveRow() { 2798 #ifdef __FreeBSD__ 2799 return; 2800 #endif 2801 Project project; 2802 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2803 project.addChild(sheetData); 2804 sheetData->setColumnCount(3); 2805 sheetData->setRowCount(10); 2806 2807 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2808 project.addChild(sheetCalculations); 2809 sheetCalculations->setColumnCount(3); 2810 sheetCalculations->setRowCount(2); 2811 2812 SpreadsheetDock dock(nullptr); 2813 dock.setSpreadsheets({sheetCalculations}); 2814 2815 QCOMPARE(dock.ui.cbLinkingEnabled->isChecked(), false); 2816 QCOMPARE(dock.ui.cbLinkedSpreadsheet->isVisible(), false); 2817 QCOMPARE(dock.ui.sbRowCount->isEnabled(), true); 2818 QCOMPARE(sheetCalculations->linking(), false); 2819 2820 dock.ui.cbLinkingEnabled->toggled(true); 2821 2822 sheetCalculations->setLinkedSpreadsheet(sheetData); 2823 2824 QCOMPARE(sheetCalculations->linking(), true); 2825 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2826 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2827 QCOMPARE(sheetCalculations->rowCount(), 10); 2828 2829 new SpreadsheetModel(sheetData); // otherwise emitRowCountChanged will not be called 2830 sheetData->setRowCount(7); 2831 2832 QCOMPARE(sheetCalculations->linking(), true); 2833 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2834 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2835 QCOMPARE(sheetCalculations->rowCount(), 7); 2836 } 2837 2838 void SpreadsheetTest::testLinkSpreadsheetRecalculate() { 2839 #ifdef __FreeBSD__ 2840 return; 2841 #endif 2842 Project project; 2843 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2844 project.addChild(sheetData); 2845 sheetData->setColumnCount(2); 2846 sheetData->setRowCount(10); 2847 auto* sheetDataColumn0 = sheetData->child<Column>(0); 2848 sheetDataColumn0->replaceValues(0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 2849 QVERIFY(sheetDataColumn0); 2850 auto* sheetDataColumn1 = sheetData->child<Column>(1); 2851 QVERIFY(sheetDataColumn1); 2852 sheetDataColumn1->replaceValues(0, {1, 2, 1, 2, 1, 2, 1, 2, 1, 3}); 2853 2854 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2855 project.addChild(sheetCalculations); 2856 sheetCalculations->setColumnCount(1); 2857 sheetCalculations->setRowCount(2); 2858 auto* sheetCalculationsColumn0 = sheetCalculations->child<Column>(0); 2859 QVERIFY(sheetCalculationsColumn0); 2860 sheetCalculationsColumn0->setFormula(QStringLiteral("x + y"), {QStringLiteral("x"), QStringLiteral("y")}, {sheetDataColumn0, sheetDataColumn1}, true); 2861 sheetCalculationsColumn0->updateFormula(); 2862 2863 { 2864 QVector<double> ref{2, 4, 4, 6, 6, 8, 8, 10, 10, 13}; 2865 QCOMPARE(sheetCalculationsColumn0->rowCount(), 10); // currently the update() triggers a resize 2866 for (int i = 0; i < 10; i++) 2867 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), ref.at(i)); 2868 } 2869 sheetCalculations->setLinking(true); 2870 sheetCalculations->setLinkedSpreadsheet(sheetData); 2871 2872 QCOMPARE(sheetCalculations->linking(), true); 2873 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2874 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2875 QCOMPARE(sheetCalculations->rowCount(), 10); 2876 2877 new SpreadsheetModel(sheetData); // otherwise emitRowCountChanged will not be called 2878 sheetData->setRowCount(7); 2879 sheetDataColumn0->replaceValues(0, {3, 4, 6, 2, 1, 8, 5}); 2880 QCOMPARE(sheetDataColumn0->rowCount(), 7); 2881 2882 { 2883 QVector<double> ref{4, 6, 7, 4, 2, 10, 6}; 2884 QCOMPARE(sheetCalculationsColumn0->rowCount(), ref.count()); 2885 for (int i = 0; i < ref.count(); i++) 2886 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), ref.at(i)); 2887 } 2888 } 2889 2890 void SpreadsheetTest::testLinkSpreadsheetRecalculateRowCountChange() { 2891 #ifdef __FreeBSD__ 2892 return; 2893 #endif 2894 Project project; 2895 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2896 project.addChild(sheetData); 2897 sheetData->setColumnCount(2); 2898 sheetData->setRowCount(10); 2899 auto* sheetDataColumn0 = sheetData->child<Column>(0); 2900 sheetDataColumn0->setColumnMode(AbstractColumn::ColumnMode::Integer); 2901 sheetDataColumn0->replaceInteger(0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 2902 QVERIFY(sheetDataColumn0); 2903 2904 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2905 project.addChild(sheetCalculations); 2906 sheetCalculations->setColumnCount(1); 2907 sheetCalculations->setRowCount(2); 2908 auto* sheetCalculationsColumn0 = sheetCalculations->child<Column>(0); 2909 QVERIFY(sheetCalculationsColumn0); 2910 sheetCalculationsColumn0->setFormula(QStringLiteral("x"), {QStringLiteral("x")}, {sheetDataColumn0}, true, false); 2911 sheetCalculationsColumn0->updateFormula(); 2912 2913 { 2914 QVector<double> ref{1, 2}; 2915 QCOMPARE(sheetCalculationsColumn0->rowCount(), 2); 2916 for (int i = 0; i < 2; i++) 2917 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), ref.at(i)); 2918 } 2919 sheetCalculations->setLinking(true); 2920 sheetCalculations->setLinkedSpreadsheet(sheetData); 2921 2922 QCOMPARE(sheetCalculations->linking(), true); 2923 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2924 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2925 QCOMPARE(sheetCalculations->rowCount(), 10); 2926 2927 new SpreadsheetModel(sheetData); // otherwise emitRowCountChanged will not be called 2928 sheetData->setRowCount(7); 2929 sheetDataColumn0->replaceInteger(0, {1, 2, 3, 4, 5, 6, 7}); 2930 QCOMPARE(sheetDataColumn0->rowCount(), 7); 2931 2932 { 2933 QCOMPARE(sheetCalculationsColumn0->rowCount(), 7); 2934 for (int i = 0; i < 7; i++) 2935 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), i + 1.); 2936 } 2937 2938 sheetData->setRowCount(8); 2939 2940 { 2941 QCOMPARE(sheetCalculationsColumn0->rowCount(), 8); 2942 for (int i = 0; i < 8; i++) { 2943 if (i < 7) 2944 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), i + 1.); 2945 else 2946 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), 0.); // When inserting a row for an integer column, the initial value is zero 2947 } 2948 } 2949 2950 sheetData->setRowCount(7); 2951 2952 { 2953 QCOMPARE(sheetCalculationsColumn0->rowCount(), 7); 2954 for (int i = 0; i < 7; i++) 2955 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), i + 1.); 2956 } 2957 } 2958 2959 void SpreadsheetTest::testLinkSpreadsheetSaveLoad() { 2960 #ifdef __FreeBSD__ 2961 return; 2962 #endif 2963 QString savePath; 2964 { 2965 Project project; 2966 auto model = new AspectTreeModel(&project, this); 2967 ProjectExplorer pe; // Needed otherwise the state key is missing in the file and then no restorePointers will be called 2968 pe.setProject(&project); 2969 pe.setModel(model); 2970 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 2971 project.addChild(sheetData); 2972 sheetData->setColumnCount(3); 2973 sheetData->setRowCount(10); 2974 2975 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 2976 project.addChild(sheetCalculations); 2977 sheetCalculations->setColumnCount(3); 2978 sheetCalculations->setRowCount(2); 2979 2980 SpreadsheetDock dock(nullptr); 2981 dock.setSpreadsheets({sheetCalculations}); 2982 2983 QCOMPARE(dock.ui.cbLinkingEnabled->isChecked(), false); 2984 QCOMPARE(dock.ui.cbLinkedSpreadsheet->isVisible(), false); 2985 QCOMPARE(dock.ui.sbRowCount->isEnabled(), true); 2986 QCOMPARE(sheetCalculations->linking(), false); 2987 2988 Q_EMIT dock.ui.cbLinkingEnabled->toggled(true); 2989 2990 sheetCalculations->setLinkedSpreadsheet(sheetData); 2991 2992 QCOMPARE(sheetCalculations->linking(), true); 2993 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 2994 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 2995 QCOMPARE(sheetCalculations->rowCount(), 10); 2996 2997 SAVE_PROJECT("testLinkSpreadsheetSaveLoad") 2998 } 2999 3000 { 3001 Project project; 3002 QCOMPARE(project.load(savePath), true); 3003 3004 auto sheetData = project.child<Spreadsheet>(0); 3005 QVERIFY(sheetData); 3006 auto sheetCalculations = project.child<Spreadsheet>(1); 3007 QVERIFY(sheetCalculations); 3008 3009 QCOMPARE(sheetCalculations->linking(), true); 3010 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 3011 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 3012 QCOMPARE(sheetCalculations->rowCount(), 10); 3013 3014 new SpreadsheetModel(sheetData); // otherwise emitRowCountChanged will not be called 3015 sheetData->setRowCount(11); // Changing shall also update sheetCalculations also after loading 3016 3017 QCOMPARE(sheetCalculations->linking(), true); 3018 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 3019 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 3020 QCOMPARE(sheetCalculations->rowCount(), 11); 3021 } 3022 } 3023 3024 void SpreadsheetTest::testLinkSpreadsheetsModelDockUpdateCheckRemoveRows() { 3025 #ifdef __FreeBSD__ 3026 return; 3027 #endif 3028 Project project; 3029 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 3030 project.addChild(sheetData); 3031 sheetData->setRowCount(20); // smaller 3032 3033 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 3034 project.addChild(sheetCalculations); 3035 sheetCalculations->setRowCount(100); 3036 3037 const auto* model = new SpreadsheetModel(sheetCalculations); 3038 SpreadsheetDock dock(nullptr); 3039 dock.setSpreadsheets({sheetCalculations}); 3040 3041 sheetCalculations->setLinking(true); 3042 sheetCalculations->setLinkedSpreadsheet(sheetData); 3043 3044 QCOMPARE(sheetCalculations->linking(), true); 3045 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 3046 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 3047 QCOMPARE(sheetCalculations->rowCount(), 20); 3048 QCOMPARE(model->rowCount(), 20); 3049 QCOMPARE(dock.ui.sbRowCount->value(), 20); 3050 } 3051 3052 void SpreadsheetTest::testLinkSpreadsheetsModelDockUpdateCheckInsertRows() { 3053 #ifdef __FreeBSD__ 3054 return; 3055 #endif 3056 Project project; 3057 auto* sheetData = new Spreadsheet(QStringLiteral("data"), false); 3058 project.addChild(sheetData); 3059 sheetData->setRowCount(100); // larger 3060 3061 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 3062 project.addChild(sheetCalculations); 3063 sheetCalculations->setRowCount(20); 3064 3065 const auto* model = new SpreadsheetModel(sheetCalculations); 3066 SpreadsheetDock dock(nullptr); 3067 dock.setSpreadsheets({sheetCalculations}); 3068 3069 sheetCalculations->setLinking(true); 3070 sheetCalculations->setLinkedSpreadsheet(sheetData); 3071 3072 QCOMPARE(sheetCalculations->linking(), true); 3073 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 3074 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 3075 QCOMPARE(sheetCalculations->rowCount(), 100); 3076 QCOMPARE(model->rowCount(), 100); 3077 QCOMPARE(dock.ui.sbRowCount->value(), 100); 3078 } 3079 3080 // ********************************************************** 3081 // **************** statistics spreadsheet ***************** 3082 // ********************************************************** 3083 /*! 3084 * toggle on and off the statistics spreadsheet and check its presence 3085 */ 3086 void SpreadsheetTest::testStatisticsSpreadsheetToggle() { 3087 Project project; 3088 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 3089 project.addChild(sheet); 3090 3091 // initial 3092 auto children = sheet->children<StatisticsSpreadsheet>(); 3093 QCOMPARE(children.size(), 0); 3094 3095 // toggle on 3096 sheet->toggleStatisticsSpreadsheet(true); 3097 children = sheet->children<StatisticsSpreadsheet>(); 3098 QCOMPARE(children.size(), 1); 3099 3100 // toggle off 3101 sheet->toggleStatisticsSpreadsheet(false); 3102 children = sheet->children<StatisticsSpreadsheet>(); 3103 QCOMPARE(children.size(), 0); 3104 } 3105 3106 /*! 3107 * change the statistics metrics and check the presense of the corresponding columns 3108 */ 3109 void SpreadsheetTest::testStatisticsSpreadsheetChangeMetrics() { 3110 Project project; 3111 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 3112 project.addChild(sheet); 3113 3114 new SpreadsheetModel(sheet); 3115 3116 // toggle on the statistics spreadsheet and count the available columns 3117 sheet->toggleStatisticsSpreadsheet(true); 3118 auto* statisticsSpreadsheet = sheet->children<StatisticsSpreadsheet>().constFirst(); 3119 auto metrics = statisticsSpreadsheet->metrics(); 3120 int colCount = 1; // column "Column Name" is always available 3121 auto it = statisticsSpreadsheet->m_metricNames.constBegin(); 3122 while (it != statisticsSpreadsheet->m_metricNames.constEnd()) { 3123 if (metrics.testFlag(it.key())) 3124 ++colCount; 3125 ++it; 3126 } 3127 QCOMPARE(statisticsSpreadsheet->children<Column>().size(), colCount); 3128 3129 // deactivate Count and check the available columns again 3130 metrics.setFlag(StatisticsSpreadsheet::Metric::Count, false); 3131 statisticsSpreadsheet->setMetrics(metrics); 3132 QCOMPARE(statisticsSpreadsheet->children<Column>().size(), colCount - 1); 3133 } 3134 3135 /*! 3136 * check the position of the statistics spreadsheet in the list of children of the parent 3137 * spreadsheet, it should always be put at the last position. 3138 */ 3139 void SpreadsheetTest::testStatisticsSpreadsheetChildIndex() { 3140 Project project; 3141 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 3142 project.addChild(sheet); 3143 3144 // toggle on the statistics spreadsheet and count the available columns 3145 sheet->toggleStatisticsSpreadsheet(true); 3146 auto* statisticsSpreadsheet = sheet->children<StatisticsSpreadsheet>().constFirst(); 3147 3148 // set the column count and check the position of the statistics spreadsheet 3149 sheet->setColumnCount(2); 3150 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 2); 3151 3152 // change the column count and check the position again 3153 sheet->setColumnCount(3); 3154 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 3); 3155 3156 // append one more column and check the position again 3157 sheet->appendColumn(); 3158 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 4); 3159 3160 // check also the position when adding new columns via the actions in the view that also 3161 // take the current selected column into account and have more logic for "append left to" 3162 // and "append right to" - ensure no columns are added after the statistics spreadhseet 3163 auto* view = static_cast<SpreadsheetView*>(sheet->view()); 3164 3165 // insert a new column right to the last selected column, it should be placed in front of the statistics spreadsheet 3166 view->selectColumn(3); // select the last 4th column 3167 view->insertColumnsRight(1); 3168 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 5); 3169 } 3170 3171 /*! 3172 * check the position of the statistics spreadsheet in the list of children of the parent 3173 * spreadsheet after an undo and redo of a column appen, it should always be put at the last position, 3174 * and also check the handling of added and removed childrend in the model in the presense of the 3175 * statistics spreadsheet. 3176 */ 3177 void SpreadsheetTest::testStatisticsSpreadsheetChildIndexAfterUndoRedo() { 3178 Project project; 3179 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 3180 project.addChild(sheet); 3181 3182 new SpreadsheetModel(sheet); 3183 3184 // toggle on the statistics spreadsheet and count the available columns 3185 sheet->toggleStatisticsSpreadsheet(true); 3186 auto* statisticsSpreadsheet = sheet->children<StatisticsSpreadsheet>().constFirst(); 3187 3188 // set the column count , append a column, undo and redo this change and check the position of the statistics spreadsheet 3189 sheet->setColumnCount(2); 3190 sheet->appendColumn(); 3191 QCOMPARE(sheet->model()->columnCount(), 3); 3192 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 3); 3193 3194 project.undoStack()->undo(); 3195 QCOMPARE(sheet->model()->columnCount(), 2); 3196 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 2); 3197 3198 project.undoStack()->redo(); 3199 QCOMPARE(sheet->model()->columnCount(), 3); 3200 QCOMPARE(sheet->indexOfChild<AbstractAspect>(statisticsSpreadsheet), 3); 3201 } 3202 3203 #ifdef HAVE_VECTOR_BLF 3204 // Copied from BLFFilterTest.cpp 3205 namespace { 3206 static const std::string PRIMITIVE_DBC = 3207 R"(VERSION "1.0.0" 3208 3209 NS_ : 3210 3211 BS_: 3212 3213 BU_: DBG DRIVER IO MOTOR SENSOR 3214 3215 )"; 3216 3217 void createDBCFile(const QString& filename, const std::string& content) { 3218 auto* file = std::fopen(filename.toStdString().c_str(), "w"); 3219 QVERIFY(file); 3220 std::fputs(PRIMITIVE_DBC.c_str(), file); 3221 std::fputs(content.c_str(), file); 3222 std::fclose(file); 3223 } 3224 3225 Vector::BLF::CanMessage2* createCANMessage(uint32_t id, uint64_t timestamp, const std::vector<uint8_t>& data) { 3226 auto* canMessage = new Vector::BLF::CanMessage2(); 3227 canMessage->channel = 1; 3228 canMessage->flags = 1; // TX 3229 canMessage->dlc = std::min<uint8_t>(data.size(), 8); 3230 canMessage->id = id; 3231 canMessage->objectTimeStamp = timestamp; 3232 canMessage->objectFlags = Vector::BLF::ObjectHeader::ObjectFlags::TimeOneNans; 3233 if (canMessage->data.size() < canMessage->dlc) 3234 canMessage->data.resize(canMessage->dlc); 3235 3236 for (int i = 0; i < canMessage->dlc; i++) { 3237 canMessage->data[i] = data.at(i); 3238 } 3239 return canMessage; 3240 } 3241 3242 void createBLFFile(const QString& filename, QVector<Vector::BLF::CanMessage2*> messages) { 3243 Vector::BLF::File blfFile; 3244 blfFile.open(filename.toStdString().c_str(), std::ios_base::out); 3245 QVERIFY(blfFile.is_open()); 3246 3247 for (auto msg : messages) { 3248 blfFile.write(msg); 3249 } 3250 // Finish creating files 3251 blfFile.close(); 3252 } 3253 } 3254 3255 void SpreadsheetTest::testLinkSpreadSheetImportBLF() { 3256 QTemporaryFile blfFileName(QStringLiteral("XXXXXX.blf")); 3257 QVERIFY(blfFileName.open()); 3258 QVector<Vector::BLF::CanMessage2*> messages{ 3259 createCANMessage(337, 5, {0, 4, 252, 19, 0, 0, 0, 0}), 3260 createCANMessage(541, 10, {7, 39, 118, 33, 250, 30, 76, 24}), // 99.91, 85.66, 79.3, 22.2 3261 createCANMessage(337, 15, {47, 4, 60, 29, 0, 0, 0, 0}), 3262 createCANMessage(337, 20, {57, 4, 250, 29, 0, 0, 0, 0}), 3263 createCANMessage(541, 25, {7, 39, 118, 33, 250, 30, 76, 24}), // 99.91, 85.66, 79.3, 22.2 3264 }; // time is in nanoseconds 3265 createBLFFile(blfFileName.fileName(), messages); 3266 3267 QTemporaryFile dbcFile(QStringLiteral("XXXXXX.dbc")); 3268 QVERIFY(dbcFile.open()); 3269 const auto dbcContent = R"(BO_ 337 STATUS: 8 Vector__XXX 3270 SG_ Value6 : 27|3@1+ (1,0) [0|7] "" Vector__XXX 3271 SG_ Value5 : 16|11@1+ (0.1,-102) [-102|102] "%" Vector__XXX 3272 SG_ Value2 : 8|2@1+ (1,0) [0|2] "" Vector__XXX 3273 SG_ Value3 : 10|1@1+ (1,0) [0|1] "" Vector__XXX 3274 SG_ Value7 : 30|2@1+ (1,0) [0|3] "" Vector__XXX 3275 SG_ Value4 : 11|4@1+ (1,0) [0|3] "" Vector__XXX 3276 SG_ Value1 : 0|8@1+ (1,0) [0|204] "Km/h" Vector__XXX" 3277 BO_ 541 MSG2: 8 Vector__XXX 3278 SG_ MSG2Value4 : 48|16@1+ (0.01,-40) [-40|125] "C" Vector__XXX 3279 SG_ MSG2Value1 : 0|16@1+ (0.01,0) [0|100] "%" Vector__XXX 3280 SG_ MSG2Value3 : 32|16@1+ (0.01,0) [0|100] "%" Vector__XXX 3281 SG_ MSG2Value2 : 16|16@1+ (0.01,0) [0|100] "%" Vector__XXX 3282 )"; 3283 createDBCFile(dbcFile.fileName(), dbcContent); 3284 3285 //------------------------------------------------------------------------------------------ 3286 Project project; 3287 const auto spreadsheetName = blfFileName.fileName().replace(QStringLiteral(".blf"), QStringLiteral("")); 3288 auto* sheetData = new Spreadsheet(spreadsheetName, false); 3289 project.addChild(sheetData); 3290 sheetData->setColumnCount(2); 3291 sheetData->setRowCount(10); 3292 auto* sheetDataColumn0 = sheetData->child<Column>(0); 3293 sheetDataColumn0->setName(QStringLiteral("Value6_")); 3294 sheetDataColumn0->replaceValues(0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 3295 QVERIFY(sheetDataColumn0); 3296 auto* sheetDataColumn1 = sheetData->child<Column>(1); 3297 sheetDataColumn1->setName(QStringLiteral("Value5_%")); 3298 QVERIFY(sheetDataColumn1); 3299 sheetDataColumn1->replaceValues(0, {1, 2, 1, 2, 1, 2, 1, 2, 1, 3}); 3300 3301 auto* sheetCalculations = new Spreadsheet(QStringLiteral("calculations"), false); 3302 project.addChild(sheetCalculations); 3303 sheetCalculations->setColumnCount(2); 3304 sheetCalculations->setRowCount(2); 3305 3306 auto* sheetCalculationsColumn0 = sheetCalculations->child<Column>(0); 3307 QVERIFY(sheetCalculationsColumn0); 3308 sheetCalculationsColumn0->setFormula(QStringLiteral("2*x"), {QStringLiteral("x")}, {sheetDataColumn0}, true); 3309 sheetCalculationsColumn0->updateFormula(); 3310 3311 auto* sheetCalculationsColumn1 = sheetCalculations->child<Column>(1); 3312 QVERIFY(sheetCalculationsColumn1); 3313 sheetCalculationsColumn1->setFormula(QStringLiteral("2*x"), {QStringLiteral("x")}, {sheetDataColumn1}, true); 3314 sheetCalculationsColumn1->updateFormula(); 3315 3316 { 3317 QVector<double> ref{2, 4, 6, 8, 10, 12, 14, 16, 18, 20}; 3318 QCOMPARE(sheetCalculationsColumn0->rowCount(), 10); 3319 for (int i = 0; i < 10; i++) 3320 VALUES_EQUAL(sheetCalculationsColumn0->doubleAt(i), ref.at(i)); 3321 } 3322 3323 { 3324 QVector<double> ref{2, 4, 2, 4, 2, 4, 2, 4, 2, 6}; 3325 QCOMPARE(sheetCalculationsColumn1->rowCount(), 10); 3326 for (int i = 0; i < 10; i++) 3327 VALUES_EQUAL(sheetCalculationsColumn1->doubleAt(i), ref.at(i)); 3328 } 3329 3330 sheetCalculations->setLinking(true); 3331 sheetCalculations->setLinkedSpreadsheet(sheetData); 3332 3333 QCOMPARE(sheetCalculations->linking(), true); 3334 QCOMPARE(sheetCalculations->linkedSpreadsheet(), sheetData); 3335 QCOMPARE(sheetCalculations->linkedSpreadsheetPath(), sheetData->path()); 3336 QCOMPARE(sheetCalculations->rowCount(), 10); 3337 3338 new SpreadsheetModel(sheetData); // otherwise emitRowCountChanged will not be called 3339 3340 VectorBLFFilter filter; 3341 filter.setConvertTimeToSeconds(true); 3342 filter.setTimeHandlingMode(CANFilter::TimeHandling::ConcatPrevious); 3343 QCOMPARE(filter.isValid(blfFileName.fileName()), true); 3344 3345 // Valid blf and valid dbc 3346 filter.setDBCFile(dbcFile.fileName()); 3347 filter.readDataFromFile(blfFileName.fileName(), sheetData); 3348 QCOMPARE(sheetData->columnCount(), 12); 3349 3350 QCOMPARE(sheetData->rowCount(), 5); 3351 QCOMPARE(sheetDataColumn0->rowCount(), 5); 3352 QCOMPARE(sheetDataColumn1->rowCount(), 5); 3353 3354 const auto* sheetDataColumn6 = sheetData->child<Column>(1); 3355 QCOMPARE(sheetDataColumn6->name(), QStringLiteral("Value6_")); 3356 const auto* sheetDataColumn5 = sheetData->child<Column>(2); 3357 QCOMPARE(sheetDataColumn5->name(), QStringLiteral("Value5_%")); 3358 3359 { 3360 QVector<double> ref{4., 4., 6., 6., 6.}; 3361 const auto* sheetCalculationsColumn = sheetCalculations->child<Column>(0); 3362 QCOMPARE(sheetCalculationsColumn->formulaData().at(0).column(), sheetDataColumn6); 3363 QCOMPARE(sheetCalculationsColumn->rowCount(), ref.count()); 3364 for (int i = 0; i < ref.count(); i++) 3365 VALUES_EQUAL(sheetCalculationsColumn->doubleAt(i), ref.at(i)); 3366 } 3367 3368 { 3369 QVector<double> ref{0., 0., 64., 102., 102.}; 3370 const auto* sheetCalculationsColumn = sheetCalculations->child<Column>(1); 3371 QCOMPARE(sheetCalculationsColumn->formulaData().at(0).column(), sheetDataColumn5); 3372 QCOMPARE(sheetCalculationsColumn->rowCount(), ref.count()); 3373 for (int i = 0; i < ref.count(); i++) 3374 VALUES_EQUAL(sheetCalculationsColumn->doubleAt(i), ref.at(i)); 3375 } 3376 } 3377 #endif // HAVE_VECTOR_BLF 3378 3379 /*! 3380 * Columns are named in the correct number order 3381 */ 3382 void SpreadsheetTest::testNaming() { 3383 Project project; 3384 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 3385 project.addChild(sheet); 3386 3387 new SpreadsheetModel(sheet); 3388 3389 QCOMPARE(sheet->columnCount(), 2); 3390 QCOMPARE(sheet->column(0)->name(), QStringLiteral("1")); 3391 QCOMPARE(sheet->column(1)->name(), QStringLiteral("2")); 3392 3393 sheet->setColumnCount(10); 3394 QCOMPARE(sheet->columnCount(), 10); 3395 for (int i = 0; i < 10; i++) { 3396 QCOMPARE(sheet->column(i)->name(), QString::number(i + 1)); 3397 } 3398 } 3399 3400 void SpreadsheetTest::testClearColumns() { 3401 Project project; 3402 auto* sheet = new Spreadsheet(QStringLiteral("test"), false); 3403 project.addChild(sheet); 3404 3405 new SpreadsheetModel(sheet); 3406 3407 sheet->setColumnCount(3); 3408 QCOMPARE(sheet->columnCount(), 3); 3409 sheet->setRowCount(10); 3410 3411 auto* c0 = sheet->column(0); 3412 auto* c1 = sheet->column(1); 3413 auto* c2 = sheet->column(2); 3414 3415 for (int i = 0; i < 10; i++) { 3416 c0->setValueAt(i, i); 3417 c1->setValueAt(i, 2. * i + 3.); 3418 c2->setValueAt(i, pow(i, 3.)); 3419 } 3420 3421 for (int i = 0; i < 10; i++) { 3422 QCOMPARE(c0->valueAt(i), i); 3423 QCOMPARE(c1->valueAt(i), 2. * i + 3.); 3424 QCOMPARE(c2->valueAt(i), pow(i, 3.)); 3425 } 3426 3427 sheet->clear({c0, c2}); 3428 3429 for (int i = 0; i < 10; i++) { 3430 QCOMPARE(c0->valueAt(i), NAN); 3431 QCOMPARE(c1->valueAt(i), 2. * i + 3.); 3432 QCOMPARE(c2->valueAt(i), NAN); 3433 } 3434 3435 sheet->undoStack()->undo(); 3436 3437 for (int i = 0; i < 10; i++) { 3438 QCOMPARE(c0->valueAt(i), i); 3439 QCOMPARE(c1->valueAt(i), 2. * i + 3.); 3440 QCOMPARE(c2->valueAt(i), pow(i, 3.)); 3441 } 3442 } 3443 3444 QTEST_MAIN(SpreadsheetTest)