From 2281b01f49e5ff67d64962ae84bc823b12b74ca6 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 24 Aug 2026 01:04:00 +0200 Subject: [PATCH] feat: email grouping by date + stability fixes - EmailTreeModel: hierarchical model for grouped email view (QTreeView) - DateGroupProxyModel: proxy model for date-based grouping logic - MailListView: migrated from QTableView to QTreeView with grouping support - ReaderView: subject frame with shadow, avatar moved to header, body inside header - MainWindow: frameless with rounded corners, 1px border, edge resize, no status bar - Stability fixes for grouping mode switch: * Proper tree cleanup (no double-delete of root) * Bounds checking on all array accesses * Null pointer checks in index/parent/data methods * Date validity checks before grouping * Index validation before accessing m_emails --- CMakeLists.txt | 2 + PLAN.md | 66 ---- src/ui/maillistview.cpp | 175 ++++++---- src/ui/maillistview.h | 13 +- src/ui/mainmainwindow.cpp | 2 +- src/ui/models/DateGroupProxyModel.cpp | 173 ++++++++++ src/ui/models/DateGroupProxyModel.h | 47 +++ src/ui/models/EmailListModel.h | 3 + src/ui/models/EmailTreeModel.cpp | 325 ++++++++++++++++++ src/ui/models/EmailTreeModel.h | 78 +++++ src/ui/readerview.cpp | 79 +++-- tests/unit/.qmake.stash | 23 ++ tests/unit/Makefile | 400 ++++++++++++++++++++++ tests/unit/moc_predefs.h | 475 ++++++++++++++++++++++++++ tests/unit/test_accountdao | Bin 0 -> 93720 bytes tests/unit/test_accountdao.cpp | 161 ++++++--- tests/unit/test_accountdao.moc | 124 +++++++ tests/unit/test_accountdao.pro | 7 + tests/unit/test_mimestorage.cpp | 64 ++++ 19 files changed, 1998 insertions(+), 219 deletions(-) delete mode 100644 PLAN.md create mode 100644 src/ui/models/DateGroupProxyModel.cpp create mode 100644 src/ui/models/DateGroupProxyModel.h create mode 100644 src/ui/models/EmailTreeModel.cpp create mode 100644 src/ui/models/EmailTreeModel.h create mode 100644 tests/unit/.qmake.stash create mode 100644 tests/unit/Makefile create mode 100644 tests/unit/moc_predefs.h create mode 100644 tests/unit/test_accountdao create mode 100644 tests/unit/test_accountdao.moc create mode 100644 tests/unit/test_accountdao.pro create mode 100644 tests/unit/test_mimestorage.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index be32738..b1c220f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,8 @@ set(SRC_FILES src/ui/accountsetupdialog.h src/ui/models/FolderListModel.cpp src/ui/models/EmailListModel.cpp + src/ui/models/EmailTreeModel.cpp + src/ui/models/DateGroupProxyModel.cpp src/ui/readerview.cpp src/ui/readerview.h src/ui/maillistview.cpp diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index e131375..0000000 --- a/PLAN.md +++ /dev/null @@ -1,66 +0,0 @@ -# Plan de Acción: wino-mail-dtkqt - -## Decisión Arquitectónica: Qt6 Widgets (no QML) - -**Motivación**: El cliente de correo es una aplicación de escritorio orientada a PC. -QML es excelente para dashboards animados y apps móviles, pero un cliente de correo -con árboles de carpetas, tablas de correos, formularios complejos y paneles divididos -se beneficia más de la madurez y eficiencia de Qt6 Widgets. - -Beneficios clave del cambio: -- Integración directa con DTK (Deepin Tool Kit) para el tema nativo -- Menos dependencias: solo qt6-base-dev -- Modelos C++ (QAbstractItemModel, QSqlQueryModel) funcionan nativamente con QTreeView/QTableView -- Sin bridges QML → C++, todo es C++ puro -- Más eficiente con listas grandes de correos (miles de items) - -El plan contempla descartar el QML existente y reconstruir la UI con QWidgets -reaprovechando toda la lógica C++ ya implementada (DAOs, servicios, sincronizadores, -modelos, EventBus, etc.). - ---- - -## Fase 0 — Arreglar Build (base C++) -- [x] Arreglar includes relativos en synchronizers -- [x] Migrar main.cpp: eliminar QQmlApplicationEngine, usar QApplication + MainWindow widget -- [x] Verificar compilación completa del target wino-mail-qt -- [x] Revisar que todos los .h existentes tengan guards y #include correctos - -## Fase 1 — Backend Core (completar lo que falta del C# original) -- [x] Implementar sistema de HTTP requests (request.cpp, concreterequests.cpp, requestprocessor.cpp) -- [x] Implementar ChangeProcessor completo (changetype.cpp, changprocessor.cpp) -- [x] Implementar EmailComposerBridge (preparar correos para envío) -- [x] Implementar SynchronizerProvider (fábrica de sincronizadores por tipo de cuenta) -- [x] Implementar AccountSetupDialogLauncher (lanzar diálogo de configuración inicial) - -## Fase 2 — Autenticación y Servicios (port desde C#) -- [x] Portar IAuthenticator → GmailAuthenticator, OutlookAuthenticator (OAuth2 con Qt Network) -- [x] Portar AccountService: CRUD de cuentas de correo (IMAP, Gmail, Outlook) -- [x] Portar MailService: envío (SMTP) y recepción real de correos -- [x] Portar MimeStorageService: almacenamiento y gestión de adjuntos -- [x] Portar MimeFileService: exportar/importar .eml y adjuntos -- [x] Portar FolderService: gestión de carpetas (INBOX, Sent, Drafts, etc.) - -## Fase 3 — UI con Qt6 Widgets (descartar QML) -- [x] **Diseñar estructura de navegación**: QMainWindow + QSplitter (panel izquierdo: árbol de carpetas, panel derecho: lista de correos + lector) -- [x] **Implementar MainWindow**: menú, toolbar, barra de estado, system tray -- [x] **AccountSetupDialog**: QDialog con wizard para configurar cuenta IMAP/Gmail/Outlook (OAuth2 o credenciales) -- [x] **FolderTreeView**: QTreeView con QStandardItemModel o modelo propio para mostrar jerarquía de carpetas -- [x] **MailListView**: QTableView o QTreeView con delegados personalizados -- [x] **ReaderPanel**: QTextBrowser o QWebEngineView para mostrar el cuerpo del correo -- [x] **ComposeDialog**: QDialog para redactar correos (To, CC, BCC, asunto, cuerpo HTML/plain, adjuntos) -- [x] **SearchBar**: QLineEdit con filtrado en tiempo real sobre el modelo de correos -- [x] **Ventanas independientes**: Botón en toolbar y ReaderView + doble clic en lista para abrir correo en ventana independiente -- [x] **AccountSetupDialog (Wizard)**: QDialog con wizard de 3 pasos para configurar cuenta Gmail/Outlook (OAuth2) o IMAP (credenciales manuales) - -## Fase 4 — DTK Integration (tema Deepin) -- [ ] Habilitar DTK en CMakeLists.txt (detectar dtkwidget) -- [ ] Aplicar DMainWindow, DApplication, DTitlebar para el look nativo Deepin -- [ ] Adaptar QSS/DStyle para mantener coherencia visual -- [ ] Fallback a Qt widgets estándar si DTK no está disponible - -## Fase 5 — Testing y QA -- [ ] Re-activar y arreglar tests unitarios (DAO, Translator, EventBus) -- [ ] Tests de integración (SyncScheduler con mock de sincronizadores) -- [ ] Tests de UI (verificar navegación, apertura de correos, composición) -- [ ] Benchmark con cargas grandes de correos (>10.000) diff --git a/src/ui/maillistview.cpp b/src/ui/maillistview.cpp index 5ca0162..b494f49 100644 --- a/src/ui/maillistview.cpp +++ b/src/ui/maillistview.cpp @@ -1,7 +1,18 @@ #include "ui/maillistview.h" #include +#include "ui/models/EmailListModel.h" +#include "ui/models/EmailTreeModel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include -MailListView::MailListView(QWidget *parent) : QWidget(parent) { +MailListView::MailListView(QWidget *parent) : QWidget(parent), m_sourceModel(nullptr) { setupUI(); } @@ -10,29 +21,6 @@ void MailListView::setupUI() { layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - // Header bar - QWidget *headerBar = new QWidget(); - headerBar->setFixedHeight(48); - headerBar->setStyleSheet("background-color: #1976D2;"); - QHBoxLayout *headerLayout = new QHBoxLayout(headerBar); - headerLayout->setContentsMargins(16, 0, 10, 0); - - QLabel *title = new QLabel("Wino Mail"); - title->setStyleSheet("color: white; font-size: 18px; font-weight: bold;"); - headerLayout->addWidget(title); - headerLayout->addStretch(); - - m_composeButton = new QPushButton("✎"); - m_composeButton->setFixedSize(36, 36); - m_composeButton->setStyleSheet( - "QPushButton { background-color: #e0e0e0; border-radius: 4px; font-size: 18px; color: #333; }" - "QPushButton:hover { background-color: #d0d0d0; }" - ); - headerLayout->addWidget(m_composeButton); - connect(m_composeButton, &QPushButton::clicked, this, &MailListView::composeRequested); - - layout->addWidget(headerBar); - // Search bar QWidget *searchBar = new QWidget(); searchBar->setFixedHeight(44); @@ -76,83 +64,122 @@ void MailListView::setupUI() { filterLayout->addStretch(); - connect(m_unreadOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); - connect(m_flaggedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); - connect(m_hasAttachmentsCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); + // Group by combo + m_groupByCombo = new QComboBox(); + m_groupByCombo->addItem("Sin agrupación"); + m_groupByCombo->addItem("Agrupar por fecha"); + m_groupByCombo->setFixedWidth(180); + m_groupByCombo->setStyleSheet( + "QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 4px 8px; " + "background: white; min-height: 20px; }" + "QComboBox:hover { border-color: #bdbdbd; }" + "QComboBox:focus { border-color: #1976D2; }" + ); + connect(m_groupByCombo, QOverload::of(&QComboBox::currentIndexChanged), + this, &MailListView::onGroupByChanged); + filterLayout->addWidget(m_groupByCombo); layout->addWidget(filterBar); - // Table - m_tableView = new QTableView(); - m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); - m_tableView->setShowGrid(false); - m_tableView->setAlternatingRowColors(true); - m_tableView->verticalHeader()->hide(); - m_tableView->horizontalHeader()->setStretchLastSection(true); - m_tableView->horizontalHeader()->setSectionsClickable(true); - m_tableView->setSortingEnabled(true); - m_tableView->setFrameShape(QFrame::NoFrame); - m_tableView->setStyleSheet( - "QTableView { background-color: #ffffff; alternate-background-color: #f9f9fb; border: none; }" - "QTableView::item { padding: 8px; border-bottom: 1px solid #e8e8ed; }" - "QTableView::item:selected { background-color: #e3f2fd; color: #1a1a2e; }" + // Tree View + m_treeView = new QTreeView(); + m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_treeView->setSelectionMode(QAbstractItemView::SingleSelection); + m_treeView->setAlternatingRowColors(true); + m_treeView->header()->setStretchLastSection(false); + m_treeView->header()->setSectionsClickable(true); + m_treeView->setSortingEnabled(false); // We handle sorting in the model + m_treeView->setFrameShape(QFrame::NoFrame); + m_treeView->setRootIsDecorated(true); + m_treeView->setItemsExpandable(true); + m_treeView->setUniformRowHeights(true); + m_treeView->setStyleSheet( + "QTreeView { background-color: #ffffff; alternate-background-color: #f9f9fb; border: none; }" + "QTreeView::item { padding: 8px; border-bottom: 1px solid #e8e8ed; }" + "QTreeView::item:selected { background-color: #e3f2fd; color: #1a1a2e; }" + "QTreeView::branch { background: transparent; }" "QHeaderView::section { background-color: #f5f5f7; padding: 8px; border: none; border-bottom: 1px solid #d1d1d6; font-weight: 600; color: #555; }" ); + // Tree model + m_treeModel = new EmailTreeModel(this); m_proxyModel = new QSortFilterProxyModel(this); - m_proxyModel->setSortRole(EmailListModel::DateRole); - m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - m_proxyModel->setDynamicSortFilter(true); + m_proxyModel->setSourceModel(m_treeModel); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_proxyModel->setFilterKeyColumn(-1); // Search all columns - m_tableView->setModel(m_proxyModel); + m_treeView->setModel(m_proxyModel); - connect(m_tableView, &QTableView::clicked, this, &MailListView::onRowSelected); - connect(m_tableView, &QTableView::doubleClicked, this, [this](const QModelIndex &index) { + // Column widths + m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSubject, QHeaderView::Stretch); + m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSender, QHeaderView::Stretch); + m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColDate, QHeaderView::ResizeToContents); + + connect(m_treeView, &QTreeView::clicked, this, &MailListView::onRowSelected); + connect(m_treeView, &QTreeView::doubleClicked, this, [this](const QModelIndex &index) { if (!index.isValid()) return; - QModelIndex sourceIndex = m_proxyModel->mapToSource(index); - int mailId = sourceIndex.data(EmailListModel::IdRole).toInt(); - emit emailOpenRequested(mailId); + QModelIndex proxyIdx = m_proxyModel->mapToSource(index); + if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) { + int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt(); + emit emailOpenRequested(mailId); + } }); - layout->addWidget(m_tableView); + // Expand all groups by default + m_treeView->expandAll(); + + layout->addWidget(m_treeView); + + // Connect filter checkboxes + connect(m_unreadOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); + connect(m_flaggedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); + connect(m_hasAttachmentsCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); } void MailListView::setModel(EmailListModel *model) { - m_proxyModel->setSourceModel(model); - - // Hide columns we don't want to show (by index now, not by role) - // Columns: 0=Subject, 1=Sender, 2=Date - // All visible for now - we can hide via header data + m_sourceModel = model; - // Default sort by date descending (column 2) - m_tableView->sortByColumn(EmailListModel::ColDate, Qt::DescendingOrder); + // Connect to source model changes to refresh tree + connect(m_sourceModel, &QAbstractItemModel::modelReset, this, &MailListView::refreshTreeModel); + connect(m_sourceModel, &QAbstractItemModel::layoutChanged, this, &MailListView::refreshTreeModel); + connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, &MailListView::refreshTreeModel); + connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &MailListView::refreshTreeModel); + + // Initial population + refreshTreeModel(); +} - // Set column resize modes - m_tableView->horizontalHeader()->setSectionResizeMode(EmailListModel::ColSubject, QHeaderView::Stretch); - m_tableView->horizontalHeader()->setSectionResizeMode(EmailListModel::ColSender, QHeaderView::Stretch); - m_tableView->horizontalHeader()->setSectionResizeMode(EmailListModel::ColDate, QHeaderView::ResizeToContents); +void MailListView::refreshTreeModel() { + if (!m_sourceModel) return; + + const QVector &emails = m_sourceModel->emails(); + m_treeModel->setEmails(emails); + m_treeView->expandAll(); } void MailListView::onRowSelected(const QModelIndex &index) { if (!index.isValid()) return; - QModelIndex sourceIndex = m_proxyModel->mapToSource(index); - int mailId = sourceIndex.data(EmailListModel::IdRole).toInt(); - emit emailSelected(mailId); + QModelIndex proxyIdx = m_proxyModel->mapToSource(index); + if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) { + int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt(); + emit emailSelected(mailId); + } } void MailListView::onSearchTextChanged(const QString &text) { - if (auto *model = qobject_cast(m_proxyModel->sourceModel())) { - model->setSearchFilter(text); - } + m_proxyModel->setFilterFixedString(text); } void MailListView::onFilterChanged() { - if (auto *model = qobject_cast(m_proxyModel->sourceModel())) { - model->setShowUnreadOnly(m_unreadOnlyCheck->isChecked()); - model->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked()); - model->setShowHasAttachments(m_hasAttachmentsCheck->isChecked()); - } + if (!m_sourceModel) return; + m_sourceModel->setShowUnreadOnly(m_unreadOnlyCheck->isChecked()); + m_sourceModel->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked()); + m_sourceModel->setShowHasAttachments(m_hasAttachmentsCheck->isChecked()); + // refreshTreeModel will be called automatically via modelReset signal +} + +void MailListView::onGroupByChanged(int index) { + bool enabled = (index == 1); // 0 = Sin agrupación, 1 = Agrupar por fecha + m_treeModel->setGroupMode(enabled ? EmailTreeModel::GroupByDate : EmailTreeModel::NoGrouping); + m_treeView->expandAll(); } \ No newline at end of file diff --git a/src/ui/maillistview.h b/src/ui/maillistview.h index 97e476c..b88779d 100644 --- a/src/ui/maillistview.h +++ b/src/ui/maillistview.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -10,8 +10,10 @@ #include #include #include +#include #include "ui/models/EmailListModel.h" +#include "ui/models/EmailTreeModel.h" class MailListView : public QWidget { Q_OBJECT @@ -21,7 +23,7 @@ public: ~MailListView() override = default; void setModel(EmailListModel *model); - QTableView* tableView() const { return m_tableView; } + QTreeView* treeView() const { return m_treeView; } signals: void emailSelected(int mailId); @@ -32,15 +34,20 @@ private slots: void onRowSelected(const QModelIndex &index); void onSearchTextChanged(const QString &text); void onFilterChanged(); + void onGroupByChanged(int index); private: void setupUI(); + void refreshTreeModel(); - QTableView *m_tableView; + QTreeView *m_treeView; + EmailTreeModel *m_treeModel; QSortFilterProxyModel *m_proxyModel; + EmailListModel *m_sourceModel = nullptr; QPushButton *m_composeButton; QLineEdit *m_searchEdit; QCheckBox *m_unreadOnlyCheck; QCheckBox *m_flaggedOnlyCheck; QCheckBox *m_hasAttachmentsCheck; + QComboBox *m_groupByCombo; }; \ No newline at end of file diff --git a/src/ui/mainmainwindow.cpp b/src/ui/mainmainwindow.cpp index 757b1c0..2bc97b3 100644 --- a/src/ui/mainmainwindow.cpp +++ b/src/ui/mainmainwindow.cpp @@ -385,7 +385,7 @@ void MainMainWindow::onFolderSelected(const QModelIndex &index) m_emailModel->refresh(); // Clear selection and show placeholder m_currentMailId = -1; - m_mailListView->tableView()->clearSelection(); + m_mailListView->treeView()->clearSelection(); m_viewerStack->setCurrentIndex(0); // placeholder } } diff --git a/src/ui/models/DateGroupProxyModel.cpp b/src/ui/models/DateGroupProxyModel.cpp new file mode 100644 index 0000000..e6628b3 --- /dev/null +++ b/src/ui/models/DateGroupProxyModel.cpp @@ -0,0 +1,173 @@ +#include "DateGroupProxyModel.h" +#include +#include +#include +#include "EmailListModel.h" + +DateGroupProxyModel::DateGroupProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + setDynamicSortFilter(true); +} + +void DateGroupProxyModel::setGroupByDate(bool enabled) +{ + if (m_groupByDate == enabled) return; + m_groupByDate = enabled; + invalidate(); +} + +bool DateGroupProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if (!m_groupByDate) { + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); + } + + // When grouping, we don't filter rows, we just organize them + return true; +} + +bool DateGroupProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + // First sort by group, then by date within group (descending) + if (m_groupByDate) { + int leftGroup = groupForRow(left.row()); + int rightGroup = groupForRow(right.row()); + + if (leftGroup != rightGroup) { + return leftGroup < rightGroup; + } + + // Same group - sort by date descending (newest first) + QVariant leftDate = sourceModel()->data(left, EmailListModel::DateRole); + QVariant rightDate = sourceModel()->data(right, EmailListModel::DateRole); + + if (leftDate.isValid() && rightDate.isValid()) { + return leftDate.toDateTime() > rightDate.toDateTime(); + } + } + + return QSortFilterProxyModel::lessThan(left, right); +} + +QString DateGroupProxyModel::groupNameForDate(const QDateTime &date) +{ + if (!date.isValid()) return "Sin fecha"; + + QDate emailDate = date.date(); + QDate today = QDate::currentDate(); + + int daysDiff = today.daysTo(emailDate); + + if (daysDiff == 0) { + return "Hoy"; + } else if (daysDiff == -1) { + return "Ayer"; + } else if (daysDiff > -7 && daysDiff < 0) { + // This week (Monday to Sunday) + return emailDate.dayOfWeek() == 1 ? "Esta semana (Lunes)" : "Esta semana"; + } else if (daysDiff >= -13 && daysDiff <= -7) { + return "Semana pasada"; + } else if (daysDiff >= -20 && daysDiff <= -14) { + return "Hace dos semanas"; + } else if (emailDate.month() == today.month() && emailDate.year() == today.year()) { + return "Este mes"; + } else if (emailDate.year() == today.year()) { + // Month name + static const QString months[] = { + "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", + "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" + }; + return months[emailDate.month() - 1]; + } else { + return QString("%1").arg(emailDate.year()); + } +} + +void DateGroupProxyModel::updateGroupCache() const +{ + m_groupRowCounts.clear(); + m_groupStartRows.clear(); + + int rowCount = this->rowCount(); + if (rowCount == 0) return; + + QString lastGroup = ""; + int currentGroup = -1; + + for (int i = 0; i < rowCount; ++i) { + QModelIndex idx = index(i, 0); + QString group = idx.data(GroupRole).toString(); + + if (group != lastGroup) { + currentGroup++; + lastGroup = group; + m_groupStartRows.append(i); + m_groupRowCounts.append(1); + } else { + m_groupRowCounts[currentGroup]++; + } + } +} + +int DateGroupProxyModel::groupForRow(int row) const +{ + if (row < 0 || row >= rowCount()) return -1; + + if (m_groupStartRows.isEmpty()) { + updateGroupCache(); + } + + // Binary search for group + int group = 0; + for (int i = m_groupStartRows.size() - 1; i >= 0; --i) { + if (row >= m_groupStartRows[i]) { + return i; + } + } + return 0; +} + +QString DateGroupProxyModel::groupNameForRow(int row) const +{ + if (row < 0 || row >= rowCount()) return ""; + + QModelIndex idx = index(row, 0); + return idx.data(GroupRole).toString(); +} + +QVariant DateGroupProxyModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) return QVariant(); + + if (role == GroupRole) { + // Return group name for this row + QModelIndex sourceIdx = mapToSource(index); + QVariant dateVar = sourceModel()->data(sourceIdx, EmailListModel::DateRole); + if (dateVar.isValid()) { + return groupNameForDate(dateVar.toDateTime()); + } + return "Sin fecha"; + } + + if (role == IsGroupHeaderRole) { + // This would be used if we inserted header rows - not implemented yet + return false; + } + + if (role == GroupSectionRole) { + return groupForRow(index.row()); + } + + return QSortFilterProxyModel::data(index, role); +} + +QVariant DateGroupProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == GroupRole && orientation == Qt::Horizontal && m_groupByDate) { + // For the date column, we might want to show group headers + // This is for column headers, not row grouping + } + + return QSortFilterProxyModel::headerData(section, orientation, role); +} \ No newline at end of file diff --git a/src/ui/models/DateGroupProxyModel.h b/src/ui/models/DateGroupProxyModel.h new file mode 100644 index 0000000..92ba765 --- /dev/null +++ b/src/ui/models/DateGroupProxyModel.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +class DateGroupProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + enum GroupBy { + GroupByDate = 0 + }; + + explicit DateGroupProxyModel(QObject *parent = nullptr); + + // Grouping methods + void setGroupByDate(bool enabled); + bool isGroupByDateEnabled() const { return m_groupByDate; } + + // Override to provide group headers + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + // For grouping - we'll use a custom role for group identification + static constexpr int GroupRole = Qt::UserRole + 100; + static constexpr int IsGroupHeaderRole = Qt::UserRole + 101; + static constexpr int GroupSectionRole = Qt::UserRole + 102; + + // Helper to get group name for a date + static QString groupNameForDate(const QDateTime &date); + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + +private: + bool m_groupByDate = false; + mutable QVector m_groupRowCounts; // cached group sizes + mutable QVector m_groupStartRows; // cached start rows for each group + + void updateGroupCache() const; + int groupForRow(int row) const; + QString groupNameForRow(int row) const; +}; \ No newline at end of file diff --git a/src/ui/models/EmailListModel.h b/src/ui/models/EmailListModel.h index 227285e..8441d7d 100644 --- a/src/ui/models/EmailListModel.h +++ b/src/ui/models/EmailListModel.h @@ -53,6 +53,9 @@ public: void setShowFlaggedOnly(bool show); void setShowHasAttachments(bool show); + // Get all emails currently in the model (after filtering) + const QVector& emails() const { return m_emails; } + private: QVector m_emails; int m_folderId{-1}; // -1 means all folders diff --git a/src/ui/models/EmailTreeModel.cpp b/src/ui/models/EmailTreeModel.cpp new file mode 100644 index 0000000..cbb4d52 --- /dev/null +++ b/src/ui/models/EmailTreeModel.cpp @@ -0,0 +1,325 @@ +#include "EmailTreeModel.h" +#include +#include +#include +#include + +EmailTreeModel::EmailTreeModel(QObject *parent) + : QAbstractItemModel(parent), m_rootItem(new TreeItem(TreeItem::Group)) +{ + m_rootItem->type = TreeItem::Group; + m_rootItem->groupName = "Root"; +} + +EmailTreeModel::~EmailTreeModel() +{ + deleteTree(m_rootItem); +} + +void EmailTreeModel::deleteTree(TreeItem* item) +{ + if (!item) return; + qDeleteAll(item->children); + delete item; +} + +QModelIndex EmailTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + TreeItem* parentItem; + if (!parent.isValid()) + parentItem = m_rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + // Verify parentItem is valid + if (!parentItem) + return QModelIndex(); + + if (row < 0 || row >= parentItem->children.size()) + return QModelIndex(); + + TreeItem* childItem = parentItem->children[row]; + if (!childItem) + return QModelIndex(); + + return createIndex(row, column, childItem); +} + +QModelIndex EmailTreeModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return QModelIndex(); + + TreeItem* childItem = static_cast(child.internalPointer()); + if (!childItem) + return QModelIndex(); + + TreeItem* parentItem = childItem->parent; + if (!parentItem || parentItem == m_rootItem) + return QModelIndex(); + + return createIndex(parentItem->row, 0, parentItem); +} + +int EmailTreeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + TreeItem* parentItem; + if (!parent.isValid()) + parentItem = m_rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + return parentItem->children.size(); +} + +int EmailTreeModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return ColCount; +} + +QVariant EmailTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + TreeItem* item = static_cast(index.internalPointer()); + + if (role == MailIdRole) { + if (item->type == TreeItem::Mail) { + const MailItem& mail = m_emails[item->mailIndex]; + return mail.id(); + } + return QVariant(); + } + + if (role == IsGroupRole) { + return item->type == TreeItem::Group; + } + + if (role == GroupNameRole && item->type == TreeItem::Group) { + return item->groupName; + } + + if (role == GroupIndexRole && item->type == TreeItem::Group) { + return item->groupIndex; + } + + if (role == MailIndexRole && item->type == TreeItem::Mail) { + return item->mailIndex; + } + + if (role != Qt::DisplayRole && role != Qt::ToolTipRole) + return QVariant(); + + if (item->type == TreeItem::Group) { + if (index.column() == ColSubject) { + return QString("%1 (%2)").arg(item->groupName).arg(item->children.size()); + } + return QVariant(); + } + + // Mail item + if (item->type == TreeItem::Mail) { + if (item->mailIndex < 0 || item->mailIndex >= m_emails.size()) { + return QVariant(); + } + const MailItem& mail = m_emails[item->mailIndex]; + + switch (index.column()) { + case ColSubject: + return mail.subject().isEmpty() ? "(Sin asunto)" : mail.subject(); + case ColSender: + return mail.sender(); + case ColDate: { + if (!mail.date().isValid()) return QVariant(); + QDateTime dt = mail.date(); + QDate today = QDate::currentDate(); + if (dt.date() == today) { + return dt.toString("HH:mm"); + } else if (dt.date() == today.addDays(-1)) { + return "Ayer " + dt.toString("HH:mm"); + } else if (dt.date().year() == today.year()) { + return dt.toString("ddd dd/MM HH:mm"); + } else { + return dt.toString("dd/MM/yyyy HH:mm"); + } + } + default: + return QVariant(); + } + } + return QVariant(); + } + +QVariant EmailTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case ColSubject: return "Asunto"; + case ColSender: return "De"; + case ColDate: return "Fecha"; + default: return QVariant(); + } + } + return QAbstractItemModel::headerData(section, orientation, role); +} + +Qt::ItemFlags EmailTreeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + TreeItem* item = static_cast(index.internalPointer()); + + Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + if (item->type == TreeItem::Group) { + // Groups don't need special flags for expand/collapse in Qt6 + } + return f; +} + +void EmailTreeModel::setEmails(const QVector &emails) +{ + beginResetModel(); + m_emails = emails; + // Clear tree properly without deleting root + qDeleteAll(m_rootItem->children); + m_rootItem->children.clear(); + setupModelData(); + endResetModel(); +} + +void EmailTreeModel::clear() +{ + beginResetModel(); + m_emails.clear(); + deleteTree(m_rootItem); + m_rootItem = new TreeItem(TreeItem::Group); + m_rootItem->type = TreeItem::Group; + m_rootItem->groupName = "Root"; + endResetModel(); +} + +void EmailTreeModel::setGroupMode(GroupMode mode) +{ + if (m_groupMode == mode) return; + + // Clear existing tree first + beginResetModel(); + m_groupMode = mode; + qDeleteAll(m_rootItem->children); + m_rootItem->children.clear(); + setupModelData(); + endResetModel(); +} + +void EmailTreeModel::setupModelData() +{ + if (m_groupMode == NoGrouping) { + // Flat list - all mails directly under root + for (int i = 0; i < m_emails.size(); ++i) { + if (i < 0 || i >= m_emails.size()) continue; + TreeItem* mailItem = new TreeItem(TreeItem::Mail, m_rootItem); + mailItem->mailIndex = i; + mailItem->row = m_rootItem->children.size(); + m_rootItem->children.append(mailItem); + } + } else { + // Grouped by date + buildGroups(); + } +} + +void EmailTreeModel::buildGroups() +{ + // Sort emails by date descending (newest first) + QVector indices(m_emails.size()); + std::iota(indices.begin(), indices.end(), 0); + std::sort(indices.begin(), indices.end(), [this](int a, int b) { + const QDateTime& dateA = m_emails[a].date(); + const QDateTime& dateB = m_emails[b].date(); + if (!dateA.isValid() && !dateB.isValid()) return false; + if (!dateA.isValid()) return false; + if (!dateB.isValid()) return true; + return dateA > dateB; + }); + + // Group by date + QMap> groups; // groupName -> mail indices + for (int idx : indices) { + if (idx < 0 || idx >= m_emails.size()) continue; + const QDateTime& dt = m_emails[idx].date(); + if (!dt.isValid()) continue; + + QString groupName = groupNameForDate(dt); + groups[groupName].append(idx); + } + + // Create tree items in group order + int groupIdx = 0; + for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) { + const QString& groupName = it.key(); + const QVector& mailIndices = it.value(); + if (mailIndices.isEmpty()) continue; + + TreeItem* groupItem = new TreeItem(TreeItem::Group, m_rootItem); + groupItem->groupName = groupName; + groupItem->groupIndex = groupIdx++; + groupItem->row = m_rootItem->children.size(); + m_rootItem->children.append(groupItem); + + for (int mailIdx : mailIndices) { + if (mailIdx < 0 || mailIdx >= m_emails.size()) continue; + TreeItem* mailItem = new TreeItem(TreeItem::Mail, groupItem); + mailItem->mailIndex = mailIdx; + mailItem->row = groupItem->children.size(); + groupItem->children.append(mailItem); + } + } +} + +QString EmailTreeModel::groupNameForDate(const QDateTime &date) const +{ + if (!date.isValid()) return "Sin fecha"; + + QDate emailDate = date.date(); + QDate today = QDate::currentDate(); + + int daysDiff = today.daysTo(emailDate); + + if (daysDiff == 0) { + return "Hoy"; + } else if (daysDiff == -1) { + return "Ayer"; + } else if (daysDiff > -7 && daysDiff < 0) { + // This week + return "Esta semana"; + } else if (daysDiff >= -13 && daysDiff <= -7) { + return "Semana pasada"; + } else if (daysDiff >= -20 && daysDiff <= -14) { + return "Hace dos semanas"; + } else if (emailDate.month() == today.month() && emailDate.year() == today.year()) { + return "Este mes"; + } else if (emailDate.year() == today.year()) { + static const QString months[] = { + "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", + "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre" + }; + return months[emailDate.month() - 1]; + } else { + return QString("%1").arg(emailDate.year()); + } +} + +int EmailTreeModel::groupIndexForDate(const QDateTime &date) const +{ + // Not used directly but kept for reference + return 0; +} \ No newline at end of file diff --git a/src/ui/models/EmailTreeModel.h b/src/ui/models/EmailTreeModel.h new file mode 100644 index 0000000..7272871 --- /dev/null +++ b/src/ui/models/EmailTreeModel.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "core/mailitem.h" + +class EmailTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + enum Columns { + ColSubject = 0, + ColSender = 1, + ColDate = 2, + ColCount = 3 + }; + + enum Roles { + MailIdRole = Qt::UserRole + 1, + IsGroupRole = Qt::UserRole + 2, + GroupNameRole = Qt::UserRole + 3, + GroupIndexRole = Qt::UserRole + 4, + MailIndexRole = Qt::UserRole + 5 + }; + + explicit EmailTreeModel(QObject *parent = nullptr); + ~EmailTreeModel() override; + + // Basic model interface + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + + // Email data management + void setEmails(const QVector &emails); + void clear(); + + // Grouping + enum GroupMode { + NoGrouping = 0, + GroupByDate = 1 + }; + void setGroupMode(GroupMode mode); + GroupMode groupMode() const { return m_groupMode; } + +private: + struct TreeItem { + enum Type { Group, Mail } type; + QString groupName; // for Group type + int groupIndex = -1; // for Group type + int mailIndex = -1; // for Mail type (index in m_emails) + QVector children; + TreeItem* parent = nullptr; + int row = -1; + + TreeItem(Type t, TreeItem* p = nullptr) : type(t), parent(p) {} + ~TreeItem() { qDeleteAll(children); } + }; + + GroupMode m_groupMode = NoGrouping; + QVector m_emails; + TreeItem* m_rootItem = nullptr; + + void setupModelData(); + void buildGroups(); + QString groupNameForDate(const QDateTime &date) const; + int groupIndexForDate(const QDateTime &date) const; + void deleteTree(TreeItem* item); +}; \ No newline at end of file diff --git a/src/ui/readerview.cpp b/src/ui/readerview.cpp index eba5bc3..f5fea97 100644 --- a/src/ui/readerview.cpp +++ b/src/ui/readerview.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "db/dao/mailitemdao.h" ReaderView::ReaderView(QWidget *parent) : QWidget(parent) { @@ -29,7 +30,7 @@ ReaderView::ReaderView(QWidget *parent) : QWidget(parent) { void ReaderView::setupUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setContentsMargins(12, 0, 12, 0); mainLayout->setSpacing(0); // ===== Toolbar (zoom, find, images) ===== @@ -182,23 +183,31 @@ void ReaderView::setupUI() { mainLayout->addWidget(m_toolbar); mainLayout->addWidget(m_findBar); - // ===== Header Section ===== - m_headerWidget = new QWidget(); - m_headerWidget->setStyleSheet("QWidget { background: white; border-bottom: 1px solid #e0e0e0; }"); - QVBoxLayout *headerLayout = new QVBoxLayout(m_headerWidget); - headerLayout->setContentsMargins(16, 12, 16, 12); - headerLayout->setSpacing(8); + // ===== Subject Section (independent, with shadow) ===== + QFrame *subjectFrame = new QFrame(); + subjectFrame->setObjectName("SubjectFrame"); + subjectFrame->setStyleSheet( + "QFrame#SubjectFrame {" + " background: white;" + " border: none;" + " border: 1px solid #e0e0e0;" + " border-radius: 10px;" + "}" + ); + // Add shadow effect + QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(this); + shadowEffect->setBlurRadius(8); + shadowEffect->setOffset(0, 3); + shadowEffect->setColor(QColor(0, 0, 0, 40)); + subjectFrame->setGraphicsEffect(shadowEffect); + + QVBoxLayout *subjectLayout = new QVBoxLayout(subjectFrame); + subjectLayout->setContentsMargins(16, 16, 16, 16); + subjectLayout->setSpacing(8); - // Subject row QHBoxLayout *subjectRow = new QHBoxLayout(); subjectRow->setSpacing(12); - m_avatarLabel = new QLabel(); - m_avatarLabel->setFixedSize(40, 40); - m_avatarLabel->setAlignment(Qt::AlignCenter); - m_avatarLabel->setStyleSheet("QLabel { background: #1976D2; color: white; border-radius: 20px; font-weight: bold; font-size: 14px; }"); - subjectRow->addWidget(m_avatarLabel); - m_subjectLabel = new QLabel(); m_subjectLabel->setText("(Sin asunto)"); QFont subjectFont = m_subjectLabel->font(); @@ -210,12 +219,33 @@ void ReaderView::setupUI() { m_subjectLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); subjectRow->addWidget(m_subjectLabel, 1); - headerLayout->addLayout(subjectRow); + subjectLayout->addLayout(subjectRow); + mainLayout->addWidget(subjectFrame); + // Add spacing after subject frame so shadow is visible + mainLayout->addSpacing(12); - // From / To / Date row + // ===== Header Section ===== + m_headerWidget = new QWidget(); + m_headerWidget->setStyleSheet("QWidget { background: white; border-bottom: 1px solid #e0e0e0; }"); + QVBoxLayout *headerLayout = new QVBoxLayout(m_headerWidget); + headerLayout->setContentsMargins(16, 12, 16, 12); + headerLayout->setSpacing(8); + + // Avatar + From / To / Date row QHBoxLayout *metaRow = new QHBoxLayout(); metaRow->setSpacing(16); + // Avatar on the left + m_avatarLabel = new QLabel(); + m_avatarLabel->setFixedSize(40, 40); + m_avatarLabel->setAlignment(Qt::AlignCenter); + m_avatarLabel->setStyleSheet("QLabel { background: #1976D2; color: white; border-radius: 20px; font-weight: bold; font-size: 14px; }"); + metaRow->addWidget(m_avatarLabel, 0, Qt::AlignTop); + + // From / To / Date on the right of avatar + QVBoxLayout *metaRightLayout = new QVBoxLayout(); + metaRightLayout->setSpacing(2); + m_fromLabel = new QLabel(); m_fromLabel->setStyleSheet("color: #333; font-size: 13px;"); m_fromLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); @@ -223,17 +253,19 @@ void ReaderView::setupUI() { connect(m_fromLabel, &QLabel::linkActivated, [this](const QString &link) { QDesktopServices::openUrl(QUrl(link)); }); - metaRow->addWidget(m_fromLabel, 1); + metaRightLayout->addWidget(m_fromLabel); m_toLabel = new QLabel(); m_toLabel->setStyleSheet("color: #666; font-size: 12px;"); m_toLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); m_toLabel->setWordWrap(true); - metaRow->addWidget(m_toLabel, 2); + metaRightLayout->addWidget(m_toLabel); m_dateLabel = new QLabel(); m_dateLabel->setStyleSheet("color: #888; font-size: 12px;"); - metaRow->addWidget(m_dateLabel); + metaRightLayout->addWidget(m_dateLabel); + + metaRow->addLayout(metaRightLayout, 1); headerLayout->addLayout(metaRow); @@ -310,16 +342,17 @@ void ReaderView::setupUI() { actionsLayout->addWidget(m_moreButton); headerLayout->addLayout(actionsLayout); + + // ===== Body Viewer (inside header widget) ===== + setupBodyViewer(); + headerLayout->addWidget(m_scrollArea, 1); + mainLayout->addWidget(m_headerWidget); // ===== Attachments Area ===== setupAttachmentsArea(); mainLayout->addWidget(m_attachmentsFrame); - // ===== Body Viewer ===== - setupBodyViewer(); - mainLayout->addWidget(m_scrollArea, 1); - // Shortcuts QShortcut *findShortcut = new QShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_F), this); connect(findShortcut, &QShortcut::activated, [this]() { diff --git a/tests/unit/.qmake.stash b/tests/unit/.qmake.stash new file mode 100644 index 0000000..42dcf68 --- /dev/null +++ b/tests/unit/.qmake.stash @@ -0,0 +1,23 @@ +QMAKE_CXX.QT_COMPILER_STDCXX = 201703L +QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 15 +QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 2 +QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 0 +QMAKE_CXX.COMPILER_MACROS = \ + QT_COMPILER_STDCXX \ + QMAKE_GCC_MAJOR_VERSION \ + QMAKE_GCC_MINOR_VERSION \ + QMAKE_GCC_PATCH_VERSION +QMAKE_CXX.INCDIRS = \ + /usr/include/c++/15 \ + /usr/include/x86_64-linux-gnu/c++/15 \ + /usr/include/c++/15/backward \ + /usr/lib/gcc/x86_64-linux-gnu/15/include \ + /usr/local/include \ + /usr/include/x86_64-linux-gnu \ + /usr/include +QMAKE_CXX.LIBDIRS = \ + /usr/lib/gcc/x86_64-linux-gnu/15 \ + /usr/lib/x86_64-linux-gnu \ + /usr/lib \ + /lib/x86_64-linux-gnu \ + /lib diff --git a/tests/unit/Makefile b/tests/unit/Makefile new file mode 100644 index 0000000..2158401 --- /dev/null +++ b/tests/unit/Makefile @@ -0,0 +1,400 @@ +############################################################################# +# Makefile for building: test_accountdao +# Generated by qmake (3.1) (Qt 6.10.2) +# Project: test_accountdao.pro +# Template: app +# Command: /usr/bin/qmake6 -o Makefile test_accountdao.pro +############################################################################# + +MAKEFILE = Makefile + +EQ = = + +####### Compiler, tools and options + +CC = gcc +CXX = g++ +DEFINES = -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_TESTLIB_LIB -DQT_SQL_LIB -DQT_CORE_LIB -DQT_TESTCASE_BUILDDIR='"/mnt/c/Users/javie/wino-mail-dtkqt/tests/unit"' +CFLAGS = -pipe -O2 -Wall -Wextra -D_REENTRANT $(DEFINES) +CXXFLAGS = -pipe -O2 -std=gnu++1z -Wall -Wextra -D_REENTRANT $(DEFINES) +INCPATH = -I. -I../../src -I../../src/services -I../../src/db -I../../src/db/dao -I../../src/core -I../../src/core/models -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtGui -I/usr/include/x86_64-linux-gnu/qt6/QtTest -I/usr/include/x86_64-linux-gnu/qt6/QtSql -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ +QMAKE = /usr/bin/qmake6 +DEL_FILE = rm -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p +COPY = cp -f +COPY_FILE = cp -f +COPY_DIR = cp -f -R +INSTALL_FILE = install -m 644 -p +INSTALL_PROGRAM = install -m 755 -p +INSTALL_DIR = cp -f -R +QINSTALL = /usr/bin/qmake6 -install qinstall +QINSTALL_PROGRAM = /usr/bin/qmake6 -install qinstall -exe +DEL_FILE = rm -f +SYMLINK = ln -f -s +DEL_DIR = rmdir +MOVE = mv -f +TAR = tar -cf +COMPRESS = gzip -9f +DISTNAME = test_accountdao1.0.0 +DISTDIR = /mnt/c/Users/javie/wino-mail-dtkqt/tests/unit/.tmp/test_accountdao1.0.0 +LINK = g++ +LFLAGS = -Wl,-O1 -Wl,-rpath-link,/usr/lib/x86_64-linux-gnu +LIBS = $(SUBLIBS) /usr/lib/x86_64-linux-gnu/libQt6Gui.so /usr/lib/x86_64-linux-gnu/libGLX.so /usr/lib/x86_64-linux-gnu/libOpenGL.so /usr/lib/x86_64-linux-gnu/libQt6Test.so /usr/lib/x86_64-linux-gnu/libQt6Sql.so /usr/lib/x86_64-linux-gnu/libQt6Core.so -lpthread -lGLX -lOpenGL +AR = ar cqs +RANLIB = +SED = sed +STRIP = strip + +####### Output directory + +OBJECTS_DIR = ./ + +####### Files + +SOURCES = ../../src/db/dao/accountdao.cpp \ + ../../src/db/databasemanager.cpp \ + ../../src/core/models/account.cpp \ + test_accountdao.cpp +OBJECTS = accountdao.o \ + databasemanager.o \ + account.o \ + test_accountdao.o +DIST = /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/sanitize.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base-unix.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-base.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-unix.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/qconfig.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_devicediscovery_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_gbm_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfsdeviceintegration_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_example_icons_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_examples_asset_downloader_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_fb_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_input_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_kms_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testinternals_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandclient.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandclient_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandglobal_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_wl_shell_integration_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xcb_qpa_lib_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_functions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_config.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_post.prf \ + .qmake.stash \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exclusive_builds.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/toolchain.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_pre.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resolve_config.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_post.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/warn_on.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/permissions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources_functions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/moc.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testlib_defines.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/opengl.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/thread.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qmake_use.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/file_copies.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testcase_targets.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exceptions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/yacc.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/lex.prf \ + test_accountdao.pro ../../src/db/dao/accountdao.cpp \ + ../../src/db/databasemanager.cpp \ + ../../src/core/models/account.cpp \ + test_accountdao.cpp +QMAKE_TARGET = test_accountdao +DESTDIR = +TARGET = test_accountdao + + +first: all +####### Build rules + +test_accountdao: $(OBJECTS) + $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) + +Makefile: test_accountdao.pro /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/sanitize.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base-unix.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-base.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-unix.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/qconfig.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_devicediscovery_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_gbm_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfsdeviceintegration_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_example_icons_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_examples_asset_downloader_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_fb_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_input_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_kms_support_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testinternals_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandclient.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandclient_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandglobal_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_wl_shell_integration_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xcb_qpa_lib_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml_private.pri \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_functions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_config.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_post.prf \ + .qmake.stash \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exclusive_builds.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/toolchain.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_pre.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resolve_config.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_post.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/warn_on.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/permissions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources_functions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/moc.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testlib_defines.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/opengl.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/thread.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qmake_use.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/file_copies.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testcase_targets.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exceptions.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/yacc.prf \ + /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/lex.prf \ + test_accountdao.pro \ + /usr/lib/x86_64-linux-gnu/libQt6Gui.prl \ + /usr/lib/x86_64-linux-gnu/libQt6Test.prl \ + /usr/lib/x86_64-linux-gnu/libQt6Sql.prl \ + /usr/lib/x86_64-linux-gnu/libQt6Core.prl + $(QMAKE) -o Makefile test_accountdao.pro +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/sanitize.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base-unix.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-base.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-unix.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/qconfig.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_devicediscovery_support_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_gbm_support_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_support_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfsdeviceintegration_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_example_icons_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_examples_asset_downloader_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_fb_support_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_input_support_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_kms_support_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testinternals_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandclient.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandclient_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_waylandglobal_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_wl_shell_integration_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xcb_qpa_lib_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml_private.pri: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_functions.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_config.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_post.prf: +.qmake.stash: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exclusive_builds.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/toolchain.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_pre.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resolve_config.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_post.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/warn_on.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/permissions.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources_functions.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/moc.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testlib_defines.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/opengl.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/thread.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qmake_use.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/file_copies.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testcase_targets.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exceptions.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/yacc.prf: +/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/lex.prf: +test_accountdao.pro: +/usr/lib/x86_64-linux-gnu/libQt6Gui.prl: +/usr/lib/x86_64-linux-gnu/libQt6Test.prl: +/usr/lib/x86_64-linux-gnu/libQt6Sql.prl: +/usr/lib/x86_64-linux-gnu/libQt6Core.prl: +qmake: FORCE + @$(QMAKE) -o Makefile test_accountdao.pro + +qmake_all: FORCE + + +all: Makefile test_accountdao + +dist: distdir FORCE + (cd `dirname $(DISTDIR)` && $(TAR) $(DISTNAME).tar $(DISTNAME) && $(COMPRESS) $(DISTNAME).tar) && $(MOVE) `dirname $(DISTDIR)`/$(DISTNAME).tar.gz . && $(DEL_FILE) -r $(DISTDIR) + +distdir: FORCE + @test -d $(DISTDIR) || mkdir -p $(DISTDIR) + $(COPY_FILE) --parents $(DIST) $(DISTDIR)/ + $(COPY_FILE) --parents /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp $(DISTDIR)/ + $(COPY_FILE) --parents ../../src/db/dao/accountdao.cpp ../../src/db/databasemanager.cpp ../../src/core/models/account.cpp test_accountdao.cpp $(DISTDIR)/ + + +clean: compiler_clean + -$(DEL_FILE) $(OBJECTS) + -$(DEL_FILE) *~ core *.core + + +distclean: clean + -$(DEL_FILE) $(TARGET) + -$(DEL_FILE) .qmake.stash + -$(DEL_FILE) Makefile + + +####### Sub-libraries + +mocclean: compiler_moc_header_clean compiler_moc_objc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_objc_header_make_all compiler_moc_source_make_all + +check: first + +benchmark: first + +compiler_rcc_make_all: +compiler_rcc_clean: +compiler_moc_predefs_make_all: moc_predefs.h +compiler_moc_predefs_clean: + -$(DEL_FILE) moc_predefs.h +moc_predefs.h: /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp + g++ -pipe -O2 -std=gnu++1z -Wall -Wextra -dM -E -o moc_predefs.h /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp + +compiler_moc_header_make_all: +compiler_moc_header_clean: +compiler_moc_objc_header_make_all: +compiler_moc_objc_header_clean: +compiler_moc_source_make_all: test_accountdao.moc +compiler_moc_source_clean: + -$(DEL_FILE) test_accountdao.moc +test_accountdao.moc: test_accountdao.cpp \ + ../../src/db/dao/accountdao.h \ + ../../src/db/databasemanager.h \ + ../../src/core/models/account.h \ + moc_predefs.h \ + /usr/lib/qt6/libexec/moc + /usr/lib/qt6/libexec/moc $(DEFINES) --include /mnt/c/Users/javie/wino-mail-dtkqt/tests/unit/moc_predefs.h -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -I/mnt/c/Users/javie/wino-mail-dtkqt/tests/unit -I/mnt/c/Users/javie/wino-mail-dtkqt/src -I/mnt/c/Users/javie/wino-mail-dtkqt/src/services -I/mnt/c/Users/javie/wino-mail-dtkqt/src/db -I/mnt/c/Users/javie/wino-mail-dtkqt/src/db/dao -I/mnt/c/Users/javie/wino-mail-dtkqt/src/core -I/mnt/c/Users/javie/wino-mail-dtkqt/src/core/models -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtGui -I/usr/include/x86_64-linux-gnu/qt6/QtTest -I/usr/include/x86_64-linux-gnu/qt6/QtSql -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I/usr/include/c++/15 -I/usr/include/x86_64-linux-gnu/c++/15 -I/usr/include/c++/15/backward -I/usr/lib/gcc/x86_64-linux-gnu/15/include -I/usr/local/include -I/usr/include/x86_64-linux-gnu -I/usr/include test_accountdao.cpp -o test_accountdao.moc + +compiler_yacc_decl_make_all: +compiler_yacc_decl_clean: +compiler_yacc_impl_make_all: +compiler_yacc_impl_clean: +compiler_lex_make_all: +compiler_lex_clean: +compiler_clean: compiler_moc_predefs_clean compiler_moc_source_clean + +####### Compile + +accountdao.o: ../../src/db/dao/accountdao.cpp ../../src/db/dao/accountdao.h \ + ../../src/db/databasemanager.h \ + ../../src/core/models/account.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o accountdao.o ../../src/db/dao/accountdao.cpp + +databasemanager.o: ../../src/db/databasemanager.cpp ../../src/db/databasemanager.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o databasemanager.o ../../src/db/databasemanager.cpp + +account.o: ../../src/core/models/account.cpp ../../src/core/models/account.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o account.o ../../src/core/models/account.cpp + +test_accountdao.o: test_accountdao.cpp ../../src/db/dao/accountdao.h \ + ../../src/db/databasemanager.h \ + ../../src/core/models/account.h \ + test_accountdao.moc + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o test_accountdao.o test_accountdao.cpp + +####### Install + +install: FORCE + +uninstall: FORCE + +FORCE: + +.SUFFIXES: + diff --git a/tests/unit/moc_predefs.h b/tests/unit/moc_predefs.h new file mode 100644 index 0000000..9a7f989 --- /dev/null +++ b/tests/unit/moc_predefs.h @@ -0,0 +1,475 @@ +#define __DBL_MIN_EXP__ (-1021) +#define __LDBL_MANT_DIG__ 64 +#define __cpp_nontype_template_parameter_auto 201606L +#define __UINT_LEAST16_MAX__ 0xffff +#define __FLT16_HAS_QUIET_NAN__ 1 +#define __ATOMIC_ACQUIRE 2 +#define __FLT128_MAX_10_EXP__ 4932 +#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F +#define __GCC_IEC_559_COMPLEX 2 +#define __cpp_aggregate_nsdmi 201304L +#define __UINT_LEAST8_TYPE__ unsigned char +#define __SIZEOF_FLOAT80__ 16 +#define __BFLT16_DENORM_MIN__ 9.18354961579912115600575419704879436e-41BF16 +#define __INTMAX_C(c) c ## L +#define __CHAR_BIT__ 8 +#define __UINT8_MAX__ 0xff +#define __SCHAR_WIDTH__ 8 +#define __WINT_MAX__ 0xffffffffU +#define __FLT32_MIN_EXP__ (-125) +#define __cpp_static_assert 201411L +#define __BFLT16_MIN_10_EXP__ (-37) +#define __cpp_inheriting_constructors 201511L +#define __ORDER_LITTLE_ENDIAN__ 1234 +#define __WCHAR_MAX__ 0x7fffffff +#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1 +#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1 +#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1 +#define __GCC_ATOMIC_CHAR_LOCK_FREE 2 +#define __GCC_IEC_559 2 +#define __FLT32X_DECIMAL_DIG__ 17 +#define __FLT_EVAL_METHOD__ 0 +#define __cpp_binary_literals 201304L +#define __FLT64_DECIMAL_DIG__ 17 +#define __CET__ 3 +#define __cpp_noexcept_function_type 201510L +#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2 +#define __cpp_variadic_templates 200704L +#define __UINT_FAST64_MAX__ 0xffffffffffffffffUL +#define __SIG_ATOMIC_TYPE__ int +#define __DBL_MIN_10_EXP__ (-307) +#define __FINITE_MATH_ONLY__ 0 +#define __cpp_variable_templates 201304L +#define __FLT32X_MAX_EXP__ 1024 +#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1 +#define __FLT32_HAS_DENORM__ 1 +#define __UINT_FAST8_MAX__ 0xff +#define __cpp_rvalue_reference 200610L +#define __cpp_nested_namespace_definitions 201411L +#define __DEC64_MAX_EXP__ 385 +#define __INT8_C(c) c +#define __LDBL_HAS_INFINITY__ 1 +#define __INT_LEAST8_WIDTH__ 8 +#define __cpp_variadic_using 201611L +#define __UINT_LEAST64_MAX__ 0xffffffffffffffffUL +#define __INT_LEAST8_MAX__ 0x7f +#define __cpp_attributes 200809L +#define __cpp_capture_star_this 201603L +#define __SHRT_MAX__ 0x7fff +#define __LDBL_MAX__ 1.18973149535723176502126385303097021e+4932L +#define __FLT64X_MAX_10_EXP__ 4932 +#define __cpp_if_constexpr 201606L +#define __BFLT16_MAX_10_EXP__ 38 +#define __BFLT16_MAX_EXP__ 128 +#define __LDBL_IS_IEC_60559__ 1 +#define __FLT64X_HAS_QUIET_NAN__ 1 +#define __UINT_LEAST8_MAX__ 0xff +#define __GCC_ATOMIC_BOOL_LOCK_FREE 2 +#define __FLT128_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966F128 +#define __UINTMAX_TYPE__ long unsigned int +#define __cpp_nsdmi 200809L +#define __BFLT16_DECIMAL_DIG__ 4 +#define __linux 1 +#define __DEC32_EPSILON__ 1E-6DF +#define __FLT_EVAL_METHOD_TS_18661_3__ 0 +#define __OPTIMIZE__ 1 +#define __UINT32_MAX__ 0xffffffffU +#define __GXX_EXPERIMENTAL_CXX0X__ 1 +#define __DBL_DENORM_MIN__ double(4.94065645841246544176568792868221372e-324L) +#define __FLT128_MIN_EXP__ (-16381) +#define __DEC64X_MAX_EXP__ 6145 +#define __WINT_MIN__ 0U +#define __FLT128_MIN_10_EXP__ (-4931) +#define __FLT32X_IS_IEC_60559__ 1 +#define __INT_LEAST16_WIDTH__ 16 +#define __SCHAR_MAX__ 0x7f +#define __FLT128_MANT_DIG__ 113 +#define __WCHAR_MIN__ (-__WCHAR_MAX__ - 1) +#define __INT64_C(c) c ## L +#define __SSP_STRONG__ 3 +#define __GCC_ATOMIC_POINTER_LOCK_FREE 2 +#define __ATOMIC_SEQ_CST 5 +#define _FORTIFY_SOURCE 3 +#define __unix 1 +#define __INT_LEAST64_MAX__ 0x7fffffffffffffffL +#define __FLT32X_MANT_DIG__ 53 +#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2 +#define __cpp_aligned_new 201606L +#define __FLT32_MAX_10_EXP__ 38 +#define __FLT64X_EPSILON__ 1.08420217248550443400745280086994171e-19F64x +#define __STDC_HOSTED__ 1 +#define __DEC64_MIN_EXP__ (-382) +#define __cpp_decltype_auto 201304L +#define __DBL_DIG__ 15 +#define __STDC_EMBED_EMPTY__ 2 +#define __FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F +#define __GXX_WEAK__ 1 +#define __SHRT_WIDTH__ 16 +#define __FLT32_IS_IEC_60559__ 1 +#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L +#define __DBL_IS_IEC_60559__ 1 +#define __DEC32_MAX__ 9.999999E96DF +#define __cpp_threadsafe_static_init 200806L +#define __cpp_enumerator_attributes 201411L +#define __FLT64X_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951F64x +#define __FLT32X_HAS_INFINITY__ 1 +#define __unix__ 1 +#define __INT_WIDTH__ 32 +#define __STDC_IEC_559__ 1 +#define __STDC_ISO_10646__ 201706L +#define __DECIMAL_DIG__ 21 +#define __STDC_IEC_559_COMPLEX__ 1 +#define __FLT64_EPSILON__ 2.22044604925031308084726333618164062e-16F64 +#define __gnu_linux__ 1 +#define __INT16_MAX__ 0x7fff +#define __FLT64_MIN_EXP__ (-1021) +#define __DEC64X_EPSILON__ 1E-33D64x +#define __FLT64X_MIN_10_EXP__ (-4931) +#define __LDBL_HAS_QUIET_NAN__ 1 +#define __FLT16_MIN_EXP__ (-13) +#define __FLT64_MANT_DIG__ 53 +#define __FLT64X_MANT_DIG__ 64 +#define __BFLT16_DIG__ 2 +#define __GNUC__ 15 +#define __GXX_RTTI 1 +#define __pie__ 2 +#define __MMX__ 1 +#define __FLT_HAS_DENORM__ 1 +#define __SIZEOF_LONG_DOUBLE__ 16 +#define __BIGGEST_ALIGNMENT__ 16 +#define __STDC_UTF_16__ 1 +#define __FLT64_MAX_10_EXP__ 308 +#define __BFLT16_IS_IEC_60559__ 0 +#define __FLT16_MAX_10_EXP__ 4 +#define __cpp_delegating_constructors 200604L +#define __DBL_MAX__ double(1.79769313486231570814527423731704357e+308L) +#define __cpp_raw_strings 200710L +#define __INT_FAST32_MAX__ 0x7fffffffffffffffL +#define __DBL_HAS_INFINITY__ 1 +#define __INT64_MAX__ 0x7fffffffffffffffL +#define __SIZEOF_FLOAT__ 4 +#define __HAVE_SPECULATION_SAFE_VALUE 1 +#define __cpp_fold_expressions 201603L +#define __DEC32_MIN_EXP__ (-94) +#define __INTPTR_WIDTH__ 64 +#define __UINT_LEAST32_MAX__ 0xffffffffU +#define __FLT32X_HAS_DENORM__ 1 +#define __INT_FAST16_TYPE__ long int +#define __MMX_WITH_SSE__ 1 +#define __LDBL_HAS_DENORM__ 1 +#define __SEG_GS 1 +#define __cplusplus 201703L +#define __cpp_ref_qualifiers 200710L +#define __DEC32_MIN__ 1E-95DF +#define __DEPRECATED 1 +#define __cpp_rvalue_references 200610L +#define __DBL_MAX_EXP__ 1024 +#define __WCHAR_WIDTH__ 32 +#define __FLT32_MAX__ 3.40282346638528859811704183484516925e+38F32 +#define __DEC128_EPSILON__ 1E-33DL +#define __FLT16_DECIMAL_DIG__ 5 +#define __SSE2_MATH__ 1 +#define __ATOMIC_HLE_RELEASE 131072 +#define __PTRDIFF_MAX__ 0x7fffffffffffffffL +#define __amd64 1 +#define __DEC64X_MAX__ 9.999999999999999999999999999999999E6144D64x +#define __ATOMIC_HLE_ACQUIRE 65536 +#define __GNUG__ 15 +#define __LONG_LONG_MAX__ 0x7fffffffffffffffLL +#define __SIZEOF_SIZE_T__ 8 +#define __BFLT16_HAS_INFINITY__ 1 +#define __FLT64X_MIN_EXP__ (-16381) +#define __SIZEOF_WINT_T__ 4 +#define __FLT32X_DIG__ 15 +#define __LONG_LONG_WIDTH__ 64 +#define __cpp_initializer_lists 200806L +#define __FLT32_MAX_EXP__ 128 +#define __cpp_hex_float 201603L +#define __GXX_ABI_VERSION 1020 +#define __FLT_MIN_EXP__ (-125) +#define __GCC_HAVE_DWARF2_CFI_ASM 1 +#define __x86_64 1 +#define __cpp_lambdas 200907L +#define __INT_FAST64_TYPE__ long int +#define __BFLT16_MAX__ 3.38953138925153547590470800371487867e+38BF16 +#define __FLT64_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F64 +#define __cpp_template_auto 201606L +#define __FLT16_DENORM_MIN__ 5.96046447753906250000000000000000000e-8F16 +#define __FLT128_EPSILON__ 1.92592994438723585305597794258492732e-34F128 +#define __FLT64X_NORM_MAX__ 1.18973149535723176502126385303097021e+4932F64x +#define __SIZEOF_POINTER__ 8 +#define __SIZE_TYPE__ long unsigned int +#define __LP64__ 1 +#define __DBL_HAS_QUIET_NAN__ 1 +#define __FLT32X_EPSILON__ 2.22044604925031308084726333618164062e-16F32x +#define __LDBL_MAX_EXP__ 16384 +#define __DECIMAL_BID_FORMAT__ 1 +#define __FLT64_MIN_10_EXP__ (-307) +#define __FLT16_MIN_10_EXP__ (-4) +#define __FLT64X_DECIMAL_DIG__ 21 +#define __DEC128_MIN__ 1E-6143DL +#define __REGISTER_PREFIX__ +#define __UINT16_MAX__ 0xffff +#define __FLT128_HAS_INFINITY__ 1 +#define __FLT32_MIN__ 1.17549435082228750796873653722224568e-38F32 +#define __UINT8_TYPE__ unsigned char +#define __FLT_DIG__ 6 +#define __DEC_EVAL_METHOD__ 2 +#define __FLT_MANT_DIG__ 24 +#define __LDBL_DECIMAL_DIG__ 21 +#define __VERSION__ "15.2.0" +#define __UINT64_C(c) c ## UL +#define __cpp_unicode_characters 201411L +#define __DEC64X_MIN__ 1E-6143D64x +#define _STDC_PREDEF_H 1 +#define __INT_LEAST32_MAX__ 0x7fffffff +#define __GCC_ATOMIC_INT_LOCK_FREE 2 +#define __FLT128_MAX_EXP__ 16384 +#define __FLT32_MANT_DIG__ 24 +#define __cpp_decltype 200707L +#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__ +#define __FLT32X_MIN_EXP__ (-1021) +#define __STDC_IEC_60559_COMPLEX__ 201404L +#define __cpp_aggregate_bases 201603L +#define __BFLT16_MIN__ 1.17549435082228750796873653722224568e-38BF16 +#define __FLT128_HAS_DENORM__ 1 +#define __FLT32_DECIMAL_DIG__ 9 +#define __FLT128_DIG__ 33 +#define __INT32_C(c) c +#define __DEC64_EPSILON__ 1E-15DD +#define __ORDER_PDP_ENDIAN__ 3412 +#define __DEC128_MIN_EXP__ (-6142) +#define __DEC128_MAX__ 9.999999999999999999999999999999999E6144DL +#define __INT_FAST32_TYPE__ long int +#define __UINT_LEAST16_TYPE__ short unsigned int +#define __DEC64X_MANT_DIG__ 34 +#define __DEC128_MAX_EXP__ 6145 +#define unix 1 +#define __DBL_HAS_DENORM__ 1 +#define __cpp_rtti 199711L +#define __UINT64_MAX__ 0xffffffffffffffffUL +#define __FLT_IS_IEC_60559__ 1 +#define __GNUC_WIDE_EXECUTION_CHARSET_NAME "UTF-32LE" +#define __FLT64X_DIG__ 18 +#define __INT8_TYPE__ signed char +#define __cpp_digit_separators 201309L +#define __ELF__ 1 +#define __GCC_ASM_FLAG_OUTPUTS__ 1 +#define __UINT32_TYPE__ unsigned int +#define __BFLT16_HAS_QUIET_NAN__ 1 +#define __FLT_RADIX__ 2 +#define __INT_LEAST16_TYPE__ short int +#define __LDBL_EPSILON__ 1.08420217248550443400745280086994171e-19L +#define __UINTMAX_C(c) c ## UL +#define __FLT16_DIG__ 3 +#define __k8 1 +#define __FLT32X_MIN__ 2.22507385850720138309023271733240406e-308F32x +#define __SIG_ATOMIC_MAX__ 0x7fffffff +#define __cpp_constexpr 201603L +#define __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2 +#define __USER_LABEL_PREFIX__ +#define __STDC_IEC_60559_BFP__ 201404L +#define __SIZEOF_PTRDIFF_T__ 8 +#define __FLT64X_HAS_INFINITY__ 1 +#define __SIZEOF_LONG__ 8 +#define __LDBL_DIG__ 18 +#define __FLT64_IS_IEC_60559__ 1 +#define __x86_64__ 1 +#define __FLT16_IS_IEC_60559__ 1 +#define __FLT16_MAX_EXP__ 16 +#define __DEC32_SUBNORMAL_MIN__ 0.000001E-95DF +#define __STDC_EMBED_FOUND__ 1 +#define __INT_FAST16_MAX__ 0x7fffffffffffffffL +#define __GCC_CONSTRUCTIVE_SIZE 64 +#define __FLT64_DIG__ 15 +#define __UINT_FAST32_MAX__ 0xffffffffffffffffUL +#define __UINT_LEAST64_TYPE__ long unsigned int +#define __FLT16_EPSILON__ 9.76562500000000000000000000000000000e-4F16 +#define __FLT_HAS_QUIET_NAN__ 1 +#define __FLT_MAX_10_EXP__ 38 +#define __FLT64X_HAS_DENORM__ 1 +#define __DEC128_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143DL +#define __FLT_HAS_INFINITY__ 1 +#define __GNUC_EXECUTION_CHARSET_NAME "UTF-8" +#define __cpp_unicode_literals 200710L +#define __UINT_FAST16_TYPE__ long unsigned int +#define __DEC64_MAX__ 9.999999999999999E384DD +#define __STDC_EMBED_NOT_FOUND__ 0 +#define __INT_FAST32_WIDTH__ 64 +#define __CHAR16_TYPE__ short unsigned int +#define __PRAGMA_REDEFINE_EXTNAME 1 +#define __DEC64X_SUBNORMAL_MIN__ 0.000000000000000000000000000000001E-6143D64x +#define __SIZE_WIDTH__ 64 +#define __SEG_FS 1 +#define __INT_LEAST16_MAX__ 0x7fff +#define __FLT16_NORM_MAX__ 6.55040000000000000000000000000000000e+4F16 +#define __DEC64_MANT_DIG__ 16 +#define __FLT32_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F32 +#define __SIG_ATOMIC_WIDTH__ 32 +#define __INT_LEAST64_TYPE__ long int +#define __INT16_TYPE__ short int +#define __INT_LEAST8_TYPE__ signed char +#define __FLT16_MAX__ 6.55040000000000000000000000000000000e+4F16 +#define __FLT128_MIN__ 3.36210314311209350626267781732175260e-4932F128 +#define __cpp_structured_bindings 201606L +#define __SIZEOF_INT__ 4 +#define __DEC32_MAX_EXP__ 97 +#define __BFLT16_EPSILON__ 7.81250000000000000000000000000000000e-3BF16 +#define __INT_FAST8_MAX__ 0x7f +#define __FLT128_MAX__ 1.18973149535723176508575932662800702e+4932F128 +#define __INTPTR_MAX__ 0x7fffffffffffffffL +#define __cpp_sized_deallocation 201309L +#define __cpp_guaranteed_copy_elision 201606L +#define linux 1 +#define __FLT64_HAS_QUIET_NAN__ 1 +#define __FLT32_MIN_10_EXP__ (-37) +#define __EXCEPTIONS 1 +#define __UINT16_C(c) c +#define __PTRDIFF_WIDTH__ 64 +#define __cpp_range_based_for 201603L +#define __INT_FAST16_WIDTH__ 64 +#define __FLT64_HAS_INFINITY__ 1 +#define __FLT64X_MAX__ 1.18973149535723176502126385303097021e+4932F64x +#define __FLT16_HAS_INFINITY__ 1 +#define __STDCPP_DEFAULT_NEW_ALIGNMENT__ 16 +#define __SIG_ATOMIC_MIN__ (-__SIG_ATOMIC_MAX__ - 1) +#define __code_model_small__ 1 +#define __GCC_ATOMIC_LONG_LOCK_FREE 2 +#define __cpp_nontype_template_args 201411L +#define __DEC32_MANT_DIG__ 7 +#define __k8__ 1 +#define __INTPTR_TYPE__ long int +#define __UINT16_TYPE__ short unsigned int +#define __WCHAR_TYPE__ int +#define __pic__ 2 +#define __UINTPTR_MAX__ 0xffffffffffffffffUL +#define __INT_FAST64_WIDTH__ 64 +#define __INT_FAST64_MAX__ 0x7fffffffffffffffL +#define __GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1 +#define __FLT_NORM_MAX__ 3.40282346638528859811704183484516925e+38F +#define __FLT32_HAS_INFINITY__ 1 +#define __FLT64X_MAX_EXP__ 16384 +#define __UINT_FAST64_TYPE__ long unsigned int +#define __cpp_inline_variables 201606L +#define __BFLT16_MIN_EXP__ (-125) +#define __INT_MAX__ 0x7fffffff +#define __linux__ 1 +#define __INT64_TYPE__ long int +#define __FLT_MAX_EXP__ 128 +#define __ORDER_BIG_ENDIAN__ 4321 +#define __DBL_MANT_DIG__ 53 +#define __SIZEOF_FLOAT128__ 16 +#define __BFLT16_MANT_DIG__ 8 +#define __DEC64_MIN__ 1E-383DD +#define __WINT_TYPE__ unsigned int +#define __UINT_LEAST32_TYPE__ unsigned int +#define __SIZEOF_SHORT__ 2 +#define __FLT32_NORM_MAX__ 3.40282346638528859811704183484516925e+38F32 +#define __SSE__ 1 +#define __LDBL_MIN_EXP__ (-16381) +#define __FLT64_MAX__ 1.79769313486231570814527423731704357e+308F64 +#define __DEC64X_MIN_EXP__ (-6142) +#define __amd64__ 1 +#define __WINT_WIDTH__ 32 +#define __INT_LEAST64_WIDTH__ 64 +#define __FLT32X_MAX_10_EXP__ 308 +#define __cpp_namespace_attributes 201411L +#define __SIZEOF_INT128__ 16 +#define __FLT16_MIN__ 6.10351562500000000000000000000000000e-5F16 +#define __FLT64X_IS_IEC_60559__ 1 +#define __GXX_CONSTEXPR_ASM__ 1 +#define __LDBL_MAX_10_EXP__ 4932 +#define __ATOMIC_RELAXED 0 +#define __DBL_EPSILON__ double(2.22044604925031308084726333618164062e-16L) +#define __INT_LEAST32_TYPE__ int +#define _LP64 1 +#define __UINT8_C(c) c +#define __FLT64_MAX_EXP__ 1024 +#define __cpp_return_type_deduction 201304L +#define __SIZEOF_WCHAR_T__ 4 +#define __GNUC_PATCHLEVEL__ 0 +#define __FLT128_NORM_MAX__ 1.18973149535723176508575932662800702e+4932F128 +#define __FLT64_NORM_MAX__ 1.79769313486231570814527423731704357e+308F64 +#define __FLT128_HAS_QUIET_NAN__ 1 +#define __INTMAX_MAX__ 0x7fffffffffffffffL +#define __INT_FAST8_TYPE__ signed char +#define __FLT64X_MIN__ 3.36210314311209350626267781732175260e-4932F64x +#define __STDCPP_THREADS__ 1 +#define __BFLT16_HAS_DENORM__ 1 +#define __GNUC_STDC_INLINE__ 1 +#define __FLT64_HAS_DENORM__ 1 +#define __FLT32_EPSILON__ 1.19209289550781250000000000000000000e-7F32 +#define __FLT16_HAS_DENORM__ 1 +#define __DBL_DECIMAL_DIG__ 17 +#define __STDC_UTF_32__ 1 +#define __INT_FAST8_WIDTH__ 8 +#define __FXSR__ 1 +#define __FLT32X_MAX__ 1.79769313486231570814527423731704357e+308F32x +#define __DBL_NORM_MAX__ double(1.79769313486231570814527423731704357e+308L) +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#define __GCC_DESTRUCTIVE_SIZE 64 +#define __INTMAX_WIDTH__ 64 +#define __cpp_runtime_arrays 198712L +#define __FLT32_DIG__ 6 +#define __UINT64_TYPE__ long unsigned int +#define __UINT32_C(c) c ## U +#define __cpp_alias_templates 200704L +#define __FLT_DENORM_MIN__ 1.40129846432481707092372958328991613e-45F +#define __FLT128_IS_IEC_60559__ 1 +#define __INT8_MAX__ 0x7f +#define __LONG_WIDTH__ 64 +#define __DBL_MIN__ double(2.22507385850720138309023271733240406e-308L) +#define __PIC__ 2 +#define __INT32_MAX__ 0x7fffffff +#define __UINT_FAST32_TYPE__ long unsigned int +#define __FLT16_MANT_DIG__ 11 +#define __FLT32X_NORM_MAX__ 1.79769313486231570814527423731704357e+308F32x +#define __CHAR32_TYPE__ unsigned int +#define __FLT_MAX__ 3.40282346638528859811704183484516925e+38F +#define __SSE2__ 1 +#define __cpp_deduction_guides 201703L +#define __BFLT16_NORM_MAX__ 3.38953138925153547590470800371487867e+38BF16 +#define __INT32_TYPE__ int +#define __SIZEOF_DOUBLE__ 8 +#define __cpp_exceptions 199711L +#define __FLT_MIN_10_EXP__ (-37) +#define __FLT64_MIN__ 2.22507385850720138309023271733240406e-308F64 +#define __INT_LEAST32_WIDTH__ 32 +#define __INTMAX_TYPE__ long int +#define __GLIBCXX_BITSIZE_INT_N_0 128 +#define __FLT32X_HAS_QUIET_NAN__ 1 +#define __ATOMIC_CONSUME 1 +#define __GNUC_MINOR__ 2 +#define __GLIBCXX_TYPE_INT_N_0 __int128 +#define __UINTMAX_MAX__ 0xffffffffffffffffUL +#define __PIE__ 2 +#define __FLT32X_DENORM_MIN__ 4.94065645841246544176568792868221372e-324F32x +#define __cpp_template_template_args 201611L +#define __DBL_MAX_10_EXP__ 308 +#define __LDBL_DENORM_MIN__ 3.64519953188247460252840593361941982e-4951L +#define __INT16_C(c) c +#define __STDC__ 1 +#define __PTRDIFF_TYPE__ long int +#define __LONG_MAX__ 0x7fffffffffffffffL +#define __FLT32X_MIN_10_EXP__ (-307) +#define __UINTPTR_TYPE__ long unsigned int +#define __DEC64_SUBNORMAL_MIN__ 0.000000000000001E-383DD +#define __DEC128_MANT_DIG__ 34 +#define __LDBL_MIN_10_EXP__ (-4931) +#define __cpp_generic_lambdas 201304L +#define __SSE_MATH__ 1 +#define __SIZEOF_LONG_LONG__ 8 +#define __cpp_user_defined_literals 200809L +#define __FLT128_DECIMAL_DIG__ 36 +#define __GCC_ATOMIC_LLONG_LOCK_FREE 2 +#define __FLT32_HAS_QUIET_NAN__ 1 +#define __FLT_DECIMAL_DIG__ 9 +#define __UINT_FAST16_MAX__ 0xffffffffffffffffUL +#define __LDBL_NORM_MAX__ 1.18973149535723176502126385303097021e+4932L +#define __GCC_ATOMIC_SHORT_LOCK_FREE 2 +#define __SIZE_MAX__ 0xffffffffffffffffUL +#define __UINT_FAST8_TYPE__ unsigned char +#define _GNU_SOURCE 1 +#define __cpp_init_captures 201304L +#define __ATOMIC_ACQ_REL 4 +#define __ATOMIC_RELEASE 3 diff --git a/tests/unit/test_accountdao b/tests/unit/test_accountdao new file mode 100644 index 0000000000000000000000000000000000000000..c3a18a4a2f87d134ffde924d92b73456517825f3 GIT binary patch literal 93720 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 +#include class TestAccountDao : public QObject { @@ -16,90 +18,145 @@ private slots: void TestAccountDao::initTestCase() { - DatabaseManager::instance().openDatabase(":memory:"); - DatabaseManager::instance().createTables(); + // Use in-memory SQLite database + // Ensure fresh connection by removing previous connection + QSqlDatabase::removeDatabase(QStringLiteral("wino_mail_connection")); + QVERIFY(DatabaseManager::instance().initialize(QStringLiteral(":memory:"))); + // Create tables (initialize will do it) } void TestAccountDao::cleanupTestCase() { - DatabaseManager::instance().closeDatabase(); + // Database will be closed automatically in destructor } void TestAccountDao::testInsertAndFind() { Account account; - account.setEmail("test@example.com"); - account.setProvider("IMAP"); - account.setHost("imap.example.com"); - account.setPort(993); - account.setUsername("testuser"); - account.setPassword("testpass"); - account.setUseSsl(true); + account.setEmail(QStringLiteral("test@example.com")); + account.setDisplayName(QStringLiteral("Test User")); + account.setSignature(QStringLiteral("Test sig")); + account.setType(AccountType::IMAP); + + Account::ConnectionSettings settings; + settings.type = QStringLiteral("imap"); + settings.incomingHost = QStringLiteral("imap.example.com"); + settings.incomingPort = 993; + settings.incomingSsl = true; + settings.outgoingHost = QStringLiteral("smtp.example.com"); + settings.outgoingPort = 465; + settings.outgoingSsl = true; + settings.username = QStringLiteral("testuser"); + settings.password = QStringLiteral("testpass"); + settings.authMethod = QStringLiteral("plain"); + account.setConnectionSettings(settings); - bool inserted = AccountDao::insert(account); - QVERIFY(inserted); - QVERIFY(account.id() > 0); + qint64 insertedId = AccountDao::insert(account); + QVERIFY(insertedId != -1); + // Optionally set id on account for later use (if needed) + // account.setId(insertedId); - Account found = AccountDao::findById(account.id()); - QVERIFY(found.isValid()); - QCOMPARE(found.email(), QString("test@example.com")); - QCOMPARE(found.provider(), QString("IMAP")); - QCOMPARE(found.host(), QString("imap.example.com")); - QCOMPARE(found.port(), 993); - QCOMPARE(found.username(), QString("testuser")); - QCOMPARE(found.password(), QString("testpass")); - QVERIFY(found.useSsl()); + Account* foundPtr = AccountDao::findById(insertedId); + QVERIFY(foundPtr != nullptr); + Account found = *foundPtr; + QCOMPARE(found.id(), insertedId); + QCOMPARE(found.email(), QStringLiteral("test@example.com")); + QCOMPARE(found.displayName(), QStringLiteral("Test User")); + QCOMPARE(found.signature(), QStringLiteral("Test sig")); + QCOMPARE(found.type(), AccountType::IMAP); + QCOMPARE(found.connectionSettings().incomingHost, QStringLiteral("imap.example.com")); + QCOMPARE(found.connectionSettings().incomingPort, 993); + QVERIFY(found.connectionSettings().incomingSsl); + QCOMPARE(found.connectionSettings().username, QStringLiteral("testuser")); + QCOMPARE(found.connectionSettings().password, QStringLiteral("testpass")); + QCOMPARE(found.connectionSettings().authMethod, QStringLiteral("plain")); } void TestAccountDao::testUpdate() { Account account; - account.setEmail("original@example.com"); - account.setProvider("IMAP"); - account.setHost("imap.example.com"); - account.setPort(993); - account.setUsername("user"); - account.setPassword("pass"); - account.setUseSsl(true); + account.setEmail(QStringLiteral("original@example.com")); + account.setDisplayName(QStringLiteral("Original User")); + account.setType(AccountType::IMAP); + Account::ConnectionSettings settings; + settings.type = QStringLiteral("imap"); + settings.incomingHost = QStringLiteral("imap.example.com"); + settings.incomingPort = 993; + settings.incomingSsl = true; + settings.outgoingHost = QStringLiteral("smtp.example.com"); + settings.outgoingPort = 465; + settings.outgoingSsl = true; + settings.username = QStringLiteral("user"); + settings.password = QStringLiteral("pass"); + settings.authMethod = QStringLiteral("plain"); + account.setConnectionSettings(settings); - bool inserted = AccountDao::insert(account); - QVERIFY(inserted); - qint64 id = account.id(); + qint64 insertedId = AccountDao::insert(account); + QVERIFY(insertedId != -1); + qint64 id = insertedId; - account.setEmail("updated@example.com"); - account.setProvider("Gmail"); - account.setUseSsl(false); + // Modify + account.setEmail(QStringLiteral("updated@example.com")); + account.setDisplayName(QStringLiteral("Updated User")); + account.setType(AccountType::Gmail); + Account::ConnectionSettings newSettings; + newSettings.type = QStringLiteral("imap"); + newSettings.incomingHost = QStringLiteral("imap.gmail.com"); + newSettings.incomingPort = 993; + newSettings.incomingSsl = true; + newSettings.outgoingHost = QStringLiteral("smtp.gmail.com"); + newSettings.outgoingPort = 465; + newSettings.outgoingSsl = true; + newSettings.username = QStringLiteral("user2"); + newSettings.password = QStringLiteral("newpass"); + newSettings.authMethod = QStringLiteral("plain"); + account.setConnectionSettings(newSettings); bool updated = AccountDao::update(account); QVERIFY(updated); - Account found = AccountDao::findById(id); - QVERIFY(found.isValid()); - QCOMPARE(found.email(), QString("updated@example.com")); - QCOMPARE(found.provider(), QString("Gmail")); - QVERIFY(!found.useSsl()); + Account* foundPtr = AccountDao::findById(id); + QVERIFY(foundPtr != nullptr); + Account found = *foundPtr; + QCOMPARE(found.id(), id); + QCOMPARE(found.email(), QStringLiteral("updated@example.com")); + QCOMPARE(found.displayName(), QStringLiteral("Updated User")); + QCOMPARE(found.type(), AccountType::Gmail); + QCOMPARE(found.connectionSettings().incomingHost, QStringLiteral("imap.gmail.com")); + QCOMPARE(found.connectionSettings().incomingPort, 993); + QVERIFY(found.connectionSettings().incomingSsl); + QCOMPARE(found.connectionSettings().username, QStringLiteral("user2")); + QCOMPARE(found.connectionSettings().password, QStringLiteral("newpass")); + QCOMPARE(found.connectionSettings().authMethod, QStringLiteral("plain")); } void TestAccountDao::testRemove() { Account account; - account.setEmail("todelete@example.com"); - account.setProvider("IMAP"); - account.setHost("imap.example.com"); - account.setPort(993); - account.setUsername("user"); - account.setPassword("pass"); - account.setUseSsl(true); + account.setEmail(QStringLiteral("todelete@example.com")); + account.setType(AccountType::IMAP); + Account::ConnectionSettings settings; + settings.type = QStringLiteral("imap"); + settings.incomingHost = QStringLiteral("imap.example.com"); + settings.incomingPort = 993; + settings.incomingSsl = true; + settings.outgoingHost = QStringLiteral("smtp.example.com"); + settings.outgoingPort = 465; + settings.outgoingSsl = true; + settings.username = QStringLiteral("user"); + settings.password = QStringLiteral("pass"); + settings.authMethod = QStringLiteral("plain"); + account.setConnectionSettings(settings); - bool inserted = AccountDao::insert(account); - QVERIFY(inserted); - qint64 id = account.id(); + qint64 insertedId = AccountDao::insert(account); + QVERIFY(insertedId != -1); + qint64 id = insertedId; bool removed = AccountDao::remove(id); QVERIFY(removed); - Account found = AccountDao::findById(id); - QVERIFY(!found.isValid()); + Account* foundPtr = AccountDao::findById(id); + QVERIFY(foundPtr == nullptr); } QTEST_MAIN(TestAccountDao) diff --git a/tests/unit/test_accountdao.moc b/tests/unit/test_accountdao.moc new file mode 100644 index 0000000..e5d3d3a --- /dev/null +++ b/tests/unit/test_accountdao.moc @@ -0,0 +1,124 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'test_accountdao.cpp' +** +** Created by: The Qt Meta Object Compiler version 69 (Qt 6.10.2) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include + +#include + +#include + + +#include +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'test_accountdao.cpp' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 69 +#error "This file was generated using the moc from 6.10.2. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +#ifndef Q_CONSTINIT +#define Q_CONSTINIT +#endif + +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +QT_WARNING_DISABLE_GCC("-Wuseless-cast") +namespace { +struct qt_meta_tag_ZN14TestAccountDaoE_t {}; +} // unnamed namespace + +template <> constexpr inline auto TestAccountDao::qt_create_metaobjectdata() +{ + namespace QMC = QtMocConstants; + QtMocHelpers::StringRefStorage qt_stringData { + "TestAccountDao", + "initTestCase", + "", + "cleanupTestCase", + "testInsertAndFind", + "testUpdate", + "testRemove" + }; + + QtMocHelpers::UintData qt_methods { + // Slot 'initTestCase' + QtMocHelpers::SlotData(1, 2, QMC::AccessPrivate, QMetaType::Void), + // Slot 'cleanupTestCase' + QtMocHelpers::SlotData(3, 2, QMC::AccessPrivate, QMetaType::Void), + // Slot 'testInsertAndFind' + QtMocHelpers::SlotData(4, 2, QMC::AccessPrivate, QMetaType::Void), + // Slot 'testUpdate' + QtMocHelpers::SlotData(5, 2, QMC::AccessPrivate, QMetaType::Void), + // Slot 'testRemove' + QtMocHelpers::SlotData(6, 2, QMC::AccessPrivate, QMetaType::Void), + }; + QtMocHelpers::UintData qt_properties { + }; + QtMocHelpers::UintData qt_enums { + }; + return QtMocHelpers::metaObjectData(QMC::MetaObjectFlag{}, qt_stringData, + qt_methods, qt_properties, qt_enums); +} +Q_CONSTINIT const QMetaObject TestAccountDao::staticMetaObject = { { + QMetaObject::SuperData::link(), + qt_staticMetaObjectStaticContent.stringdata, + qt_staticMetaObjectStaticContent.data, + qt_static_metacall, + nullptr, + qt_staticMetaObjectRelocatingContent.metaTypes, + nullptr +} }; + +void TestAccountDao::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + auto *_t = static_cast(_o); + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: _t->initTestCase(); break; + case 1: _t->cleanupTestCase(); break; + case 2: _t->testInsertAndFind(); break; + case 3: _t->testUpdate(); break; + case 4: _t->testRemove(); break; + default: ; + } + } + (void)_a; +} + +const QMetaObject *TestAccountDao::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *TestAccountDao::qt_metacast(const char *_clname) +{ + if (!_clname) return nullptr; + if (!strcmp(_clname, qt_staticMetaObjectStaticContent.strings)) + return static_cast(this); + return QObject::qt_metacast(_clname); +} + +int TestAccountDao::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 5) + qt_static_metacall(this, _c, _id, _a); + _id -= 5; + } + if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 5) + *reinterpret_cast(_a[0]) = QMetaType(); + _id -= 5; + } + return _id; +} +QT_WARNING_POP diff --git a/tests/unit/test_accountdao.pro b/tests/unit/test_accountdao.pro new file mode 100644 index 0000000..edba3d5 --- /dev/null +++ b/tests/unit/test_accountdao.pro @@ -0,0 +1,7 @@ +QT += core testlib sql +CONFIG += console c++17 +SOURCES += ../../src/db/dao/accountdao.cpp \ + ../../src/db/databasemanager.cpp \ + ../../src/core/models/account.cpp \ + test_accountdao.cpp +INCLUDEPATH += ../../src ../../src/services ../../src/db ../../src/db/dao ../../src/core ../../src/core/models \ No newline at end of file diff --git a/tests/unit/test_mimestorage.cpp b/tests/unit/test_mimestorage.cpp new file mode 100644 index 0000000..e1a0486 --- /dev/null +++ b/tests/unit/test_mimestorage.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +#include "services/mimestorage.h" + +class TestMimeStorage : public QObject +{ + Q_OBJECT +private slots: + void storesRawMessageAndDecodedAttachments(); +}; + +void TestMimeStorage::storesRawMessageAndDecodedAttachments() +{ + QStandardPaths::setTestModeEnabled(true); + const QByteArray rawMime = + "Message-ID: \r\n" + "From: =?UTF-8?B?Sm9zw6k=?= \r\n" + "To: receiver@example.com\r\n" + "Subject: =?UTF-8?Q?Informe_de_prueba?=\r\n" + "Date: Tue, 11 Aug 2026 12:00:00 +0000\r\n" + "MIME-Version: 1.0\r\n" + "Content-Type: multipart/mixed; boundary=\"wino-boundary\"\r\n" + "\r\n" + "--wino-boundary\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Content-Transfer-Encoding: 8bit\r\n" + "\r\n" + "

Contenido completo

\r\n" + "--wino-boundary\r\n" + "Content-Type: application/octet-stream; name=\"datos.bin\"\r\n" + "Content-Disposition: attachment; filename=\"datos.bin\"\r\n" + "Content-Transfer-Encoding: base64\r\n" + "\r\n" + "AAECAwQF\r\n" + "--wino-boundary--\r\n"; + + MimeStorageService storage; + ParsedMimeMessage parsed; + QVERIFY(storage.parseMessage(rawMime, parsed)); + QCOMPARE(parsed.subject, QStringLiteral("Informe de prueba")); + QCOMPARE(parsed.attachments.size(), 1); + QCOMPARE(parsed.attachments.first().fileName, QStringLiteral("datos.bin")); + QCOMPARE(parsed.attachments.first().data, QByteArray::fromHex("000102030405")); + + MailItem item; + item.setMessageId(QStringLiteral("mime-test@example.com")); + QVector attachmentPaths; + QVERIFY(storage.storeMessage(QStringLiteral("account-test"), QStringLiteral("inbox"), + item, rawMime, &attachmentPaths)); + QVERIFY(!item.fileId().isEmpty()); + QCOMPARE(storage.readEmlFile(item.fileId()), rawMime); + QCOMPARE(item.attachments(), QVector{QStringLiteral("datos.bin")}); + QCOMPARE(attachmentPaths.size(), 1); + + QFile attachment(attachmentPaths.first()); + QVERIFY(attachment.open(QIODevice::ReadOnly)); + QCOMPARE(attachment.readAll(), QByteArray::fromHex("000102030405")); + QVERIFY(storage.getEmlFilePath(item.fileId()).endsWith(QStringLiteral(".eml"))); +} + +QTEST_MAIN(TestMimeStorage) +#include "test_mimestorage.moc"