// // Unit tests for the LogViewer module: LogViewTableModel (log line storage, // column/role data access, standard level colors) and SortFilterModel // (string/date filtering and date-aware sorting). // // Both classes are the module's non-GUI logic (QAbstractTableModel / // QSortFilterProxyModel, no widgets needed); they are compiled from the // LogViewer executable sources. The other LogViewer classes are filter // widgets and the main window. // #include "../logviewtablemodel.h" #include "../sortfiltermodel.h" #include #include #include using LogInformation = LogViewTableModel::logInformation; namespace { LogInformation makeLine(const QDateTime &date, const QString &level, const QString &logger, const QString &data, const QString &location) { LogInformation line; line.date = date; line.level = level; line.logger = logger; line.data = data; line.location = location; return line; } QDateTime d(const QString &iso) { return QDateTime::fromString(iso, Qt::ISODate); } } // namespace class logviewer_unit_test : public QObject { Q_OBJECT private slots: void initTestCase(); // LogViewTableModel void tableModelEmptyDimensions(); void tableModelSetDataAndReadRoles(); void tableModelInvalidIndex(); void tableModelHeaderData(); void tableModelStandardLevelColors(); void tableModelAdjustColorsAndReset(); void tableModelAddDataAndLength(); void tableModelSetDataByIndex(); void tableModelStartEndDateTimeSignals(); // SortFilterModel void proxyStringFilter(); void proxyDateStringFilterEdgeCases(); void proxyDateFilter(); void proxyResetAllFilter(); void proxySortByDate(); void proxySortByLevel(); void proxyFigureFilterOutSignal(); }; void logviewer_unit_test::initTestCase() { // LogViewTableModel's constructor resolves its logger from the NGL root // logger, so it must exist before the model is instantiated. NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx.props", false); } // --------------------------------------------------------------------------- // LogViewTableModel // --------------------------------------------------------------------------- void logviewer_unit_test::tableModelEmptyDimensions() { LogViewTableModel model; // note: the overrides in LogViewTableModel redeclare the parameter without // the base class' default value QCOMPARE(model.rowCount(QModelIndex()), 0); QCOMPARE(model.columnCount(QModelIndex()), LogViewTableModel::colLast + 1); QCOMPARE(model.length(), 0); } void logviewer_unit_test::tableModelSetDataAndReadRoles() { LogViewTableModel model; const QDateTime date = d(QStringLiteral("2024-05-06T12:34:56")); QVector lines; lines << makeLine(date, QStringLiteral("INFO"), QStringLiteral("main"), QStringLiteral("hello"), QStringLiteral("a.cpp:42")) << makeLine(d(QStringLiteral("2024-05-07T08:00:00")), QStringLiteral("WARN"), QStringLiteral("net"), QStringLiteral("world"), QStringLiteral("b.cpp:7")); model.setData(lines); QCOMPARE(model.rowCount(QModelIndex()), 2); QCOMPARE(model.length(), 2); // DisplayRole per column QCOMPARE(model.data(model.index(0, LogViewTableModel::colDate), Qt::DisplayRole).toString(). startsWith(QStringLiteral("2024-05-06 12:34:56,")), true); QCOMPARE(model.data(model.index(0, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("INFO")); QCOMPARE(model.data(model.index(0, LogViewTableModel::colLogger), Qt::DisplayRole).toString(), QStringLiteral("main")); QCOMPARE(model.data(model.index(0, LogViewTableModel::colData), Qt::DisplayRole).toString(), QStringLiteral("hello")); QCOMPARE(model.data(model.index(0, LogViewTableModel::colLocation), Qt::DisplayRole).toString(), QStringLiteral("a.cpp:42")); // UserRole carries the raw QDateTime of the date column QCOMPARE(model.data(model.index(0, LogViewTableModel::colDate), Qt::UserRole).toDateTime(), date); // itemAt: note the date format uses dashes between the time parts QCOMPARE(model.itemAt(1, LogViewTableModel::colDate).startsWith(QStringLiteral("2024-05-07 08-00-00,")), true); QCOMPARE(model.itemAt(0, LogViewTableModel::colLevel), QStringLiteral("INFO")); QCOMPARE(model.itemAt(0, LogViewTableModel::colData), QStringLiteral("hello")); // unknown column index -> empty string QCOMPARE(model.itemAt(0, 99), QString()); // replacing all data model.setData(QVector()); QCOMPARE(model.rowCount(QModelIndex()), 0); } void logviewer_unit_test::tableModelInvalidIndex() { LogViewTableModel model; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("x.cpp:1")); model.setData(lines); // invalid index -> invalid QVariant for any role const QModelIndex invalid = QModelIndex(); QVERIFY(!model.data(invalid, Qt::DisplayRole).isValid()); QVERIFY(!model.data(invalid, Qt::UserRole).isValid()); QVERIFY(!model.data(invalid, Qt::BackgroundRole).isValid()); } void logviewer_unit_test::tableModelHeaderData() { LogViewTableModel model; QCOMPARE(model.headerData(LogViewTableModel::colDate, Qt::Horizontal, Qt::DisplayRole).toString(), QStringLiteral("Date")); QCOMPARE(model.headerData(LogViewTableModel::colLevel, Qt::Horizontal, Qt::DisplayRole).toString(), QStringLiteral("Level")); QCOMPARE(model.headerData(LogViewTableModel::colLogger, Qt::Horizontal, Qt::DisplayRole).toString(), QStringLiteral("Logger")); QCOMPARE(model.headerData(LogViewTableModel::colData, Qt::Horizontal, Qt::DisplayRole).toString(), QStringLiteral("Data")); QCOMPARE(model.headerData(LogViewTableModel::colLocation, Qt::Horizontal, Qt::DisplayRole).toString(), QStringLiteral("Location")); // vertical orientation and out-of-range section fall through to // QAbstractTableModel::headerData (1-based: section+1 horizontal, 1 vertical) QCOMPARE(model.headerData(0, Qt::Vertical, Qt::DisplayRole).toString(), QStringLiteral("1")); QCOMPARE(model.headerData(99, Qt::Horizontal, Qt::DisplayRole).toString(), QStringLiteral("100")); } void logviewer_unit_test::tableModelStandardLevelColors() { LogViewTableModel model; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("ERROR"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("FATAL"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("DEBUG"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("TRACE"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("CUSTOM"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); model.setData(lines); const int col = LogViewTableModel::colLevel; QCOMPARE(model.data(model.index(0, col), Qt::BackgroundRole).value(), Qt::blue); QCOMPARE(model.data(model.index(0, col), Qt::ForegroundRole).value(), Qt::white); QCOMPARE(model.data(model.index(1, col), Qt::BackgroundRole).value(), Qt::yellow); QCOMPARE(model.data(model.index(1, col), Qt::ForegroundRole).value(), Qt::black); QCOMPARE(model.data(model.index(2, col), Qt::BackgroundRole).value(), Qt::darkRed); QCOMPARE(model.data(model.index(2, col), Qt::ForegroundRole).value(), Qt::white); QCOMPARE(model.data(model.index(3, col), Qt::BackgroundRole).value(), Qt::red); QCOMPARE(model.data(model.index(3, col), Qt::ForegroundRole).value(), Qt::black); QCOMPARE(model.data(model.index(4, col), Qt::BackgroundRole).value(), Qt::green); QCOMPARE(model.data(model.index(4, col), Qt::ForegroundRole).value(), Qt::black); QCOMPARE(model.data(model.index(5, col), Qt::BackgroundRole).value(), Qt::lightGray); QCOMPARE(model.data(model.index(5, col), Qt::ForegroundRole).value(), Qt::black); // unknown level: no color registered -> invalid variants QVERIFY(!model.data(model.index(6, col), Qt::BackgroundRole).isValid()); QVERIFY(!model.data(model.index(6, col), Qt::ForegroundRole).isValid()); // other columns have no background/foreground color QVERIFY(!model.data(model.index(0, LogViewTableModel::colData), Qt::BackgroundRole).isValid()); } void logviewer_unit_test::tableModelAdjustColorsAndReset() { LogViewTableModel model; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); model.setData(lines); const int col = LogViewTableModel::colLevel; // adjustCellColor only affects levels that are already registered model.adjustCellColor(Qt::red, QStringLiteral("INFO")); QCOMPARE(model.data(model.index(0, col), Qt::BackgroundRole).value(), Qt::red); // unknown level: no effect model.adjustCellColor(Qt::green, QStringLiteral("NOPE")); QCOMPARE(model.data(model.index(1, col), Qt::BackgroundRole).value(), Qt::yellow); // adjustFontColor model.adjustFontColor(Qt::black, QStringLiteral("INFO")); QCOMPARE(model.data(model.index(0, col), Qt::ForegroundRole).value(), Qt::black); // reset() clears the data and restores the standard colors QVERIFY(model.reset()); QCOMPARE(model.length(), 0); QCOMPARE(model.rowCount(QModelIndex()), 0); } void logviewer_unit_test::tableModelAddDataAndLength() { LogViewTableModel model; QCOMPARE(model.length(), 0); model.addData(makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1"))); QCOMPARE(model.length(), 1); model.addData(makeLine(d(QStringLiteral("2024-01-02T00:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1"))); QCOMPARE(model.length(), 2); QCOMPARE(model.data(model.index(1, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("WARN")); } void logviewer_unit_test::tableModelSetDataByIndex() { LogViewTableModel model; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); model.setData(lines); // only Qt::EditRole is accepted QVERIFY(!model.setData(model.index(0, LogViewTableModel::colLevel), QStringLiteral("X"), Qt::DisplayRole)); QVERIFY(model.setData(model.index(0, LogViewTableModel::colLevel), QStringLiteral("ERROR"), Qt::EditRole)); QCOMPARE(model.data(model.index(0, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("ERROR")); QVERIFY(model.setData(model.index(0, LogViewTableModel::colLogger), QStringLiteral("other"), Qt::EditRole)); QCOMPARE(model.data(model.index(0, LogViewTableModel::colLogger), Qt::DisplayRole).toString(), QStringLiteral("other")); QVERIFY(model.setData(model.index(0, LogViewTableModel::colData), QStringLiteral("new data"), Qt::EditRole)); QCOMPARE(model.data(model.index(0, LogViewTableModel::colData), Qt::DisplayRole).toString(), QStringLiteral("new data")); QVERIFY(model.setData(model.index(0, LogViewTableModel::colLocation), QStringLiteral("m.cpp:9"), Qt::EditRole)); QCOMPARE(model.data(model.index(0, LogViewTableModel::colLocation), Qt::DisplayRole).toString(), QStringLiteral("m.cpp:9")); const QDateTime newDate = d(QStringLiteral("2024-02-02T02:02:02")); QVERIFY(model.setData(model.index(0, LogViewTableModel::colDate), newDate, Qt::EditRole)); QCOMPARE(model.data(model.index(0, LogViewTableModel::colDate), Qt::UserRole).toDateTime(), newDate); } void logviewer_unit_test::tableModelStartEndDateTimeSignals() { // empty model: no signals LogViewTableModel empty; bool started = false; bool ended = false; QObject::connect(&empty, &LogViewTableModel::startTimeSetted, [&started](const QDateTime &) { started = true; }); QObject::connect(&empty, &LogViewTableModel::endTimeSetted, [&ended](const QDateTime &) { ended = true; }); empty.startDateTime(); empty.endDateTime(); QVERIFY(!started); QVERIFY(!ended); // populated model: first/last line date LogViewTableModel model; QVector lines; lines << makeLine(d(QStringLiteral("2024-03-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-03-02T00:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-03-03T00:00:00")), QStringLiteral("ERROR"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); model.setData(lines); QDateTime start; QDateTime end; QObject::connect(&model, &LogViewTableModel::startTimeSetted, [&start](const QDateTime &t) { start = t; }); QObject::connect(&model, &LogViewTableModel::endTimeSetted, [&end](const QDateTime &t) { end = t; }); model.startDateTime(); model.endDateTime(); QCOMPARE(start, d(QStringLiteral("2024-03-01T00:00:00"))); QCOMPARE(end, d(QStringLiteral("2024-03-03T00:00:00"))); } // --------------------------------------------------------------------------- // SortFilterModel // --------------------------------------------------------------------------- void logviewer_unit_test::proxyStringFilter() { LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("main"), QStringLiteral("alpha"), QStringLiteral("a.cpp:1")) << makeLine(d(QStringLiteral("2024-01-02T00:00:00")), QStringLiteral("WARN"), QStringLiteral("net"), QStringLiteral("beta"), QStringLiteral("b.cpp:2")) << makeLine(d(QStringLiteral("2024-01-03T00:00:00")), QStringLiteral("INFO"), QStringLiteral("net"), QStringLiteral("gamma"), QStringLiteral("c.cpp:3")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); QCOMPARE(proxy.rowCount(), 3); QVERIFY(proxy.stringFilterIsEmpty()); // setStringFilter itself does not re-run the proxy filter (only the date // setters and resetAllFilter do); mainwindow.cpp calls // m_filterProxyModel->invalidate() after every setStringFilter, so the // tests do the same. // filter on the level column (col 1) proxy.setStringFilter(LogViewTableModel::colLevel, QStringLiteral("WARN")); proxy.invalidate(); QCOMPARE(proxy.rowCount(), 1); QCOMPARE(proxy.data(proxy.index(0, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("WARN")); // combined with the logger column (col 2) proxy.setStringFilter(LogViewTableModel::colLogger, QStringLiteral("net")); proxy.invalidate(); QCOMPARE(proxy.rowCount(), 1); QCOMPARE(proxy.data(proxy.index(0, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("WARN")); // replacing an existing filter: "net" does not match the new text "main" proxy.setStringFilter(LogViewTableModel::colLogger, QStringLiteral("main")); proxy.invalidate(); // WARN rows have logger "net" -> nothing left QCOMPARE(proxy.rowCount(), 0); // case sensitivity: a fresh column filter is stored as-is and the row data // "beta" does not match "BETA" (contains() is case sensitive) proxy.resetAllFilter(); QCOMPARE(proxy.rowCount(), 3); proxy.setStringFilter(LogViewTableModel::colData, QStringLiteral("BETA")); proxy.invalidate(); QCOMPARE(proxy.rowCount(), 0); // setStringFilter keeps the old filter when the new text matches the old // one case-insensitively, so the stored filter is still "BETA" proxy.setStringFilter(LogViewTableModel::colData, QStringLiteral("beta")); proxy.invalidate(); QCOMPARE(proxy.rowCount(), 0); } void logviewer_unit_test::proxyDateStringFilterEdgeCases() { LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T00:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); // column range is validated: out-of-range columns are ignored proxy.setStringFilter(-1, QStringLiteral("x")); proxy.setStringFilter(5, QStringLiteral("x")); proxy.setStringFilter(99, QStringLiteral("x")); QVERIFY(proxy.stringFilterIsEmpty()); QCOMPARE(proxy.rowCount(), 1); // an empty filter string still counts as "contained" for the activation // report (columns 3 and 4 only check presence, columns 1 and 2 check for // non-emptiness) proxy.setStringFilter(LogViewTableModel::colData, QString()); QVERIFY(!proxy.stringFilterIsEmpty()); QVector activated = proxy.activatedStringFilter(); QCOMPARE(activated.size(), 5); QVERIFY(!activated.at(0)); QVERIFY(!activated.at(1)); QVERIFY(!activated.at(2)); QVERIFY(activated.at(3)); QVERIFY(!activated.at(4)); // empty filter does not filter any row (contains("") is always true) QCOMPARE(proxy.rowCount(), 1); } void logviewer_unit_test::proxyDateFilter() { LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T10:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-02T10:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-03T10:00:00")), QStringLiteral("ERROR"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); // only start date: filter not active (both ends required) proxy.setStartDateFilter(d(QStringLiteral("2024-01-01T10:00:00"))); QCOMPARE(proxy.rowCount(), 3); QVector activated = proxy.activatedStringFilter(); QVERIFY(!activated.at(0)); // start and end: window is exclusive on both ends proxy.setEndDateFilter(d(QStringLiteral("2024-01-03T10:00:00"))); QCOMPARE(proxy.rowCount(), 1); QCOMPARE(proxy.data(proxy.index(0, LogViewTableModel::colDate), Qt::UserRole).toDateTime(), d(QStringLiteral("2024-01-02T10:00:00"))); activated = proxy.activatedStringFilter(); QVERIFY(activated.at(0)); // setting the same start date again is a no-op (and must not break the filter) proxy.setStartDateFilter(d(QStringLiteral("2024-01-01T10:00:00"))); QCOMPARE(proxy.rowCount(), 1); } void logviewer_unit_test::proxyResetAllFilter() { LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T10:00:00")), QStringLiteral("INFO"), QStringLiteral("main"), QStringLiteral("alpha"), QStringLiteral("a.cpp:1")) << makeLine(d(QStringLiteral("2024-01-02T10:00:00")), QStringLiteral("WARN"), QStringLiteral("net"), QStringLiteral("beta"), QStringLiteral("b.cpp:2")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); proxy.setStringFilter(LogViewTableModel::colLevel, QStringLiteral("WARN")); proxy.setStringFilter(LogViewTableModel::colLogger, QStringLiteral("net")); proxy.setStartDateFilter(d(QStringLiteral("2024-01-01T10:00:00"))); proxy.setEndDateFilter(d(QStringLiteral("2024-01-02T10:00:00"))); QCOMPARE(proxy.rowCount(), 0); proxy.resetAllFilter(); QVERIFY(proxy.stringFilterIsEmpty()); QCOMPARE(proxy.rowCount(), 2); QVector activated = proxy.activatedStringFilter(); for (const bool active : activated) QVERIFY(!active); } void logviewer_unit_test::proxySortByDate() { LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-03T10:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-01T10:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-02T10:00:00")), QStringLiteral("ERROR"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); proxy.sort(LogViewTableModel::colDate, Qt::AscendingOrder); QCOMPARE(proxy.rowCount(), 3); QCOMPARE(proxy.data(proxy.index(0, LogViewTableModel::colDate), Qt::UserRole).toDateTime(), d(QStringLiteral("2024-01-01T10:00:00"))); QCOMPARE(proxy.data(proxy.index(1, LogViewTableModel::colDate), Qt::UserRole).toDateTime(), d(QStringLiteral("2024-01-02T10:00:00"))); QCOMPARE(proxy.data(proxy.index(2, LogViewTableModel::colDate), Qt::UserRole).toDateTime(), d(QStringLiteral("2024-01-03T10:00:00"))); } void logviewer_unit_test::proxySortByLevel() { // non-date columns fall back to the default QVariant comparison LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T10:00:00")), QStringLiteral("WARN"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-02T10:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")) << makeLine(d(QStringLiteral("2024-01-03T10:00:00")), QStringLiteral("DEBUG"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); proxy.sort(LogViewTableModel::colLevel, Qt::AscendingOrder); QCOMPARE(proxy.data(proxy.index(0, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("DEBUG")); QCOMPARE(proxy.data(proxy.index(1, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("INFO")); QCOMPARE(proxy.data(proxy.index(2, LogViewTableModel::colLevel), Qt::DisplayRole).toString(), QStringLiteral("WARN")); } void logviewer_unit_test::proxyFigureFilterOutSignal() { LogViewTableModel source; QVector lines; lines << makeLine(d(QStringLiteral("2024-01-01T10:00:00")), QStringLiteral("INFO"), QStringLiteral("l"), QStringLiteral("d"), QStringLiteral("l.cpp:1")); source.setData(lines); SortFilterModel proxy; proxy.setSourceModel(&source); QVector lastFilter; int calls = 0; QObject::connect(&proxy, &SortFilterModel::theseFilterAreActivated, [&lastFilter, &calls](const QVector &filter) { lastFilter = filter; ++calls; }); proxy.figureFilterOut(); QCOMPARE(calls, 1); QCOMPARE(lastFilter, QVector({false, false, false, false, false})); proxy.setStringFilter(LogViewTableModel::colLevel, QStringLiteral("INFO")); proxy.figureFilterOut(); QCOMPARE(calls, 2); QCOMPARE(lastFilter, QVector({false, true, false, false, false})); } QTEST_MAIN(logviewer_unit_test) #include "logviewer_unit_test.moc"