Cockatrice/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h
BruebachL ce543896f5
[Tests] Use QtTest for model contracts and signal spies (#7630)
Qt6::Test was linked into every test but never used. Add a small RAII
QAbstractItemModelTester helper and apply it to the deck list model, the card
database model and display proxy, and the sets model. Add QSignalSpy coverage
for the card-added and download-settings signals, and assert explicitly that a
card row is a leaf with no child columns (the tester does not check that).

The contract checker immediately found four model bugs, fixed here:
- DeckListModel allowed children below non-zero columns.
- CardDatabaseModel reported every card as a child of every card row.
- CardDatabaseModel reported six columns for a valid (leaf) card index.
- CardDatabaseDisplayModel claimed more rows were fetchable while rowCount
  already exposed them all, so fetchMore() inserted duplicates.

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-02 07:27:43 +01:00

102 lines
2.9 KiB
C++

/**
* @file card_database_display_model.h
* @ingroup CardDatabaseModels
* @brief The CardDatabaseDisplayModel is a QSortFilterProxyModel that allows applying filters and sorting to a
* CardDatabaseModel.
*/
#ifndef COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H
#define COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H
#include "libcockatrice/card/card_info.h"
#include <QCollator>
#include <QModelIndex>
#include <QSet>
#include <QSortFilterProxyModel>
#include <QString>
#include <QStringView>
#include <QTimer>
#include <libcockatrice/card/card_localization.h>
#include <libcockatrice/filters/filter_string.h>
#include <qtmetamacros.h>
class FilterTree;
class QObject;
template <class Key, class T> class QMap;
class CardDatabaseDisplayModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
enum FilterBool
{
ShowTrue,
ShowFalse,
ShowAll
};
private:
FilterBool isToken;
QString cardName, cardText;
QSet<QString> cardNameSet, cardTypes, cardColors;
FilterTree *filterTree;
FilterString *filterString;
QTimer dirtyTimer;
CardSearchLanguage searchLanguage;
QString searchText;
/** The translation table that will be used for sanitizeCardName. */
static QMap<wchar_t, wchar_t> characterTranslation;
/** Natural, locale-aware comparison shared by every sort in this model. */
QCollator collator;
public:
explicit CardDatabaseDisplayModel(QObject *parent = nullptr);
void setSourceModel(QAbstractItemModel *model) override;
void setFilterTree(FilterTree *_filterTree);
void setIsToken(FilterBool _isToken)
{
isToken = _isToken;
dirty();
}
void setCardName(const QString &_cardName)
{
if (filterString != nullptr) {
delete filterString;
filterString = nullptr;
}
cardName = sanitizeCardName(_cardName, characterTranslation);
dirty();
}
void setStringFilter(const QString &_src);
void setCardNameSet(const QSet<QString> &_cardNameSet)
{
cardNameSet = _cardNameSet;
dirty();
}
void setSearchLanguage(const CardSearchLanguage &searchLang);
void dirty()
{
dirtyTimer.start(20);
}
void clearFilterAll();
signals:
void modelDirty();
protected:
[[nodiscard]] bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
int lessThanNumerically(QStringView left, QStringView right) const;
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
[[nodiscard]] bool rowMatchesCardName(CardInfoPtr info) const;
private slots:
void filterTreeChanged();
/** Will translate all undesirable characters in DIRTYNAME according to the TABLE. */
const QString sanitizeCardName(const QString &dirtyName, const QMap<wchar_t, wchar_t> &table);
};
#endif // COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H