From dea65b637ca2369524b9b1864eff3b2457fb37b7 Mon Sep 17 00:00:00 2001 From: Vladimir Svyatski Date: Fri, 21 Apr 2017 13:49:32 +0300 Subject: [PATCH 1/5] Add context menu entry to clean the Recycle Bin in databases This implements the feature request (issue) #503. --- .../16x16/actions/group-empty-trash.png | Bin 0 -> 1115 bytes src/gui/DatabaseWidget.cpp | 35 ++++++++++++++++++ src/gui/DatabaseWidget.h | 2 + src/gui/MainWindow.cpp | 6 +++ src/gui/MainWindow.ui | 12 +++++- 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 share/icons/application/16x16/actions/group-empty-trash.png diff --git a/share/icons/application/16x16/actions/group-empty-trash.png b/share/icons/application/16x16/actions/group-empty-trash.png new file mode 100644 index 0000000000000000000000000000000000000000..aa9d7321f94a42ed44418c86e8a26a585de0ed7c GIT binary patch literal 1115 zcmYL}YfMvT7{}jpTG~nv6l9Zg2BTe^!!WWI8ih>+x?mVvA$-u6qoJ%qucz;-`>+qs^UwdwFVBZx zp2od-*;RgmJ-sm9PC8Jp8Se;%1bLM zOOC04L|HCZ3385#{mgXO>JX{=g+`Dl;;P;Gzzb626Ed%s_3|csnvh(HW zuM=J*_-pF`Jb1JLXy*#>t#1Ik6M#?#1RORSaIjF|a;{zQz&*h+7wAy+QC9dl66*<1FP*Jf;hHTx63-_BqU{~+Nrttgfbp8( z(qqdHzCnPD3UAUulg2b;>D~p;q{GJ-GA4}0 zSWI4*LKf*%DF148unyZpI06IGGEd^)E`CpZLhhGOABdE>opy$qX6uQC#RXzPc@b~9 zHH!~;Lhx61tJJBEPtVxKSGzGQS*u_!KLs>Hm^^SOSv0nGI4QP{?zT`NZWxxvc80Pj9oIipMY#((L~TBt*lIpLBhRST`w7nm=wCbu9m; z*7^9PsIWTlnfAT5k*d?7~QV4j-ze z@0u*`Q`$~qyQ};8va5n6QkNoycsKx9TgCN((9*Kws%!t_#2x*m)d846&z=z9+L|xZuokT)uwT$-@ zqQpFNr`|{u8SAG^!*`gE(Xkzvh7mMm1&zBf9?d5eRgR5a=C6cCyyzBl)VB(;mBcd4 zI0WTx@A582)U>KM6FQTxhzwT@6dKtZEuc&zbUYuXv0wAJ$d`28u!A^Xr&3qBmU-qL zp%fDu4We4;ZPm7&Ch$wOSVsE@r9;24&$L+uL9duQWm9MGpJek2J(ltD+a|75R1Ue~ zuDWewng?G-n6L8Xn6&l|hV`PD#n_iTKlq{E=K3+f$EZl$&^Zkd|^ho#E`AZJfr*5%h_4gUgKas{FQ literal 0 HcmV?d00001 diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index b4f62315..342f37f0 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1267,3 +1267,38 @@ void DatabaseWidget::hideMessage() m_messageWidget->animatedHide(); } } + +bool DatabaseWidget::isRecycleBinSelected() const +{ + return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin(); +} + +void DatabaseWidget::emptyTrash() +{ + Group* currentGroup = m_groupView->currentGroup(); + if (!currentGroup) { + Q_ASSERT(false); + return; + } + + if (currentGroup == m_db->metadata()->recycleBin()) { + QMessageBox::StandardButton result = MessageBox::question( + this, tr("Empty recycle bin?"), + tr("Are you sure you want to permanently delete everytning from your recycle bin?"), + QMessageBox::Yes | QMessageBox::No); + + if (result == QMessageBox::Yes) { + // destroying direct entries of the recycle bin + QList subEntries = currentGroup->entries(); + for (Entry* entry : subEntries) { + delete entry; + } + // destroying direct subgroups of the recycle bin + QList subGroups = currentGroup->children(); + for (Group* group : subGroups) { + delete group; + } + refreshSearch(); + } + } +} diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index d98cb072..5751aedb 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -102,6 +102,7 @@ public: void closeUnlockDialog(); void blockAutoReload(bool block = true); void refreshSearch(); + bool isRecycleBinSelected() const; signals: void closeRequest(); @@ -152,6 +153,7 @@ public slots: void switchToImportKeepass1(const QString& fileName); void databaseModified(); void databaseSaved(); + void emptyTrash(); // Search related slots void search(const QString& searchtext); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 814501f6..72a0f4c3 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -197,6 +197,7 @@ MainWindow::MainWindow() m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new", false)); m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit", false)); m_ui->actionGroupDelete->setIcon(filePath()->icon("actions", "group-delete", false)); + m_ui->actionGroupEmptyTrash->setIcon(filePath()->icon("actions", "group-empty-trash", false)); m_ui->actionSettings->setIcon(filePath()->icon("actions", "configure")); m_ui->actionSettings->setMenuRole(QAction::PreferencesRole); @@ -295,6 +296,8 @@ MainWindow::MainWindow() SLOT(switchToGroupEdit())); m_actionMultiplexer.connect(m_ui->actionGroupDelete, SIGNAL(triggered()), SLOT(deleteGroup())); + m_actionMultiplexer.connect(m_ui->actionGroupEmptyTrash, SIGNAL(triggered()), + SLOT(emptyTrash())); connect(m_ui->actionSettings, SIGNAL(triggered()), SLOT(switchToSettings())); connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(switchToPasswordGen(bool))); @@ -413,6 +416,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1; bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0; bool groupSelected = dbWidget->isGroupSelected(); + bool recycleBinSelected = dbWidget->isRecycleBinSelected(); m_ui->actionEntryNew->setEnabled(!inSearch); m_ui->actionEntryClone->setEnabled(singleEntrySelected); @@ -429,6 +433,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); + m_ui->actionGroupEmptyTrash->setVisible(recycleBinSelected); + m_ui->actionGroupEmptyTrash->setEnabled(recycleBinSelected); m_ui->actionChangeMasterKey->setEnabled(true); m_ui->actionChangeDatabaseSettings->setEnabled(true); m_ui->actionDatabaseSave->setEnabled(true); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index c44bee9e..adacff5c 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -162,7 +162,7 @@ 0 0 800 - 29 + 21 @@ -235,8 +235,10 @@ &Groups + + @@ -521,6 +523,14 @@ Re&pair database + + + Empty recycle bin + + + false + + From 75c16d1cbba7226fea7ada2a85a5b320bcdb06f9 Mon Sep 17 00:00:00 2001 From: Vladimir Svyatski Date: Fri, 21 Apr 2017 17:33:06 +0300 Subject: [PATCH 2/5] Add requested source code changes --- src/core/Database.cpp | 16 ++++++++++++++++ src/core/Database.h | 1 + src/gui/DatabaseWidget.cpp | 31 +++++++++---------------------- src/gui/DatabaseWidget.h | 2 +- src/gui/MainWindow.cpp | 10 +++++----- src/gui/MainWindow.ui | 4 ++-- 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 4aa9e3f5..5b5a707f 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -308,6 +308,22 @@ void Database::recycleGroup(Group* group) } } +void Database::emptyRecycleBin() +{ + if (m_metadata->recycleBinEnabled() && m_metadata->recycleBin()) { + // destroying direct entries of the recycle bin + QList subEntries = m_metadata->recycleBin()->entries(); + for (Entry* entry : subEntries) { + delete entry; + } + // destroying direct subgroups of the recycle bin + QList subGroups = m_metadata->recycleBin()->children(); + for (Group* group : subGroups) { + delete group; + } + } +} + void Database::merge(const Database* other) { m_rootGroup->merge(other->rootGroup()); diff --git a/src/core/Database.h b/src/core/Database.h index 16c14960..7728d14c 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -108,6 +108,7 @@ public: bool verifyKey(const CompositeKey& key) const; void recycleEntry(Entry* entry); void recycleGroup(Group* group); + void emptyRecycleBin(); void setEmitModified(bool value); void copyAttributesFrom(const Database* other); void merge(const Database* other); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 342f37f0..115c560f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1273,32 +1273,19 @@ bool DatabaseWidget::isRecycleBinSelected() const return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin(); } -void DatabaseWidget::emptyTrash() +void DatabaseWidget::emptyRecycleBin() { - Group* currentGroup = m_groupView->currentGroup(); - if (!currentGroup) { - Q_ASSERT(false); + if(!isRecycleBinSelected()) { return; } - if (currentGroup == m_db->metadata()->recycleBin()) { - QMessageBox::StandardButton result = MessageBox::question( - this, tr("Empty recycle bin?"), - tr("Are you sure you want to permanently delete everytning from your recycle bin?"), - QMessageBox::Yes | QMessageBox::No); + QMessageBox::StandardButton result = MessageBox::question( + this, tr("Empty recycle bin?"), + tr("Are you sure you want to permanently delete everything from your recycle bin?"), + QMessageBox::Yes | QMessageBox::No); - if (result == QMessageBox::Yes) { - // destroying direct entries of the recycle bin - QList subEntries = currentGroup->entries(); - for (Entry* entry : subEntries) { - delete entry; - } - // destroying direct subgroups of the recycle bin - QList subGroups = currentGroup->children(); - for (Group* group : subGroups) { - delete group; - } - refreshSearch(); - } + if (result == QMessageBox::Yes) { + m_db->emptyRecycleBin(); + refreshSearch(); } } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 5751aedb..3add336f 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -153,7 +153,7 @@ public slots: void switchToImportKeepass1(const QString& fileName); void databaseModified(); void databaseSaved(); - void emptyTrash(); + void emptyRecycleBin(); // Search related slots void search(const QString& searchtext); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 72a0f4c3..6883f8a7 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -197,7 +197,7 @@ MainWindow::MainWindow() m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new", false)); m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit", false)); m_ui->actionGroupDelete->setIcon(filePath()->icon("actions", "group-delete", false)); - m_ui->actionGroupEmptyTrash->setIcon(filePath()->icon("actions", "group-empty-trash", false)); + m_ui->actionGroupEmptyRecycleBin->setIcon(filePath()->icon("actions", "group-empty-trash", false)); m_ui->actionSettings->setIcon(filePath()->icon("actions", "configure")); m_ui->actionSettings->setMenuRole(QAction::PreferencesRole); @@ -296,8 +296,8 @@ MainWindow::MainWindow() SLOT(switchToGroupEdit())); m_actionMultiplexer.connect(m_ui->actionGroupDelete, SIGNAL(triggered()), SLOT(deleteGroup())); - m_actionMultiplexer.connect(m_ui->actionGroupEmptyTrash, SIGNAL(triggered()), - SLOT(emptyTrash())); + m_actionMultiplexer.connect(m_ui->actionGroupEmptyRecycleBin, SIGNAL(triggered()), + SLOT(emptyRecycleBin())); connect(m_ui->actionSettings, SIGNAL(triggered()), SLOT(switchToSettings())); connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(switchToPasswordGen(bool))); @@ -433,8 +433,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); - m_ui->actionGroupEmptyTrash->setVisible(recycleBinSelected); - m_ui->actionGroupEmptyTrash->setEnabled(recycleBinSelected); + m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected); + m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected); m_ui->actionChangeMasterKey->setEnabled(true); m_ui->actionChangeDatabaseSettings->setEnabled(true); m_ui->actionDatabaseSave->setEnabled(true); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index adacff5c..384586e8 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -238,7 +238,7 @@ - + @@ -523,7 +523,7 @@ Re&pair database - + Empty recycle bin From 5792bf1a858d66cf2f3b0953e08cae0650f217c4 Mon Sep 17 00:00:00 2001 From: Vladimir Svyatski Date: Sat, 22 Apr 2017 10:02:59 +0300 Subject: [PATCH 3/5] Add skeleton for TestDatabase.cpp and test data for unit tests for the "empty recycle bin" functionality --- tests/CMakeLists.txt | 3 ++ tests/TestDatabase.cpp | 50 ++++++++++++++++++++++++ tests/TestDatabase.h | 37 ++++++++++++++++++ tests/data/RecycleBinDisabled.kdbx | Bin 0 -> 2302 bytes tests/data/RecycleBinEmpty.kdbx | Bin 0 -> 2430 bytes tests/data/RecycleBinNotYetCreated.kdbx | Bin 0 -> 2350 bytes tests/data/RecycleBinWithData.kdbx | Bin 0 -> 3854 bytes 7 files changed, 90 insertions(+) create mode 100644 tests/TestDatabase.cpp create mode 100644 tests/TestDatabase.h create mode 100644 tests/data/RecycleBinDisabled.kdbx create mode 100644 tests/data/RecycleBinEmpty.kdbx create mode 100644 tests/data/RecycleBinNotYetCreated.kdbx create mode 100644 tests/data/RecycleBinWithData.kdbx diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2ea50424..1c9f1c0f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -177,6 +177,9 @@ add_unit_test(NAME testykchallengeresponsekey SOURCES TestYkChallengeResponseKey.cpp TestYkChallengeResponseKey.h LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp + LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestDatabase.cpp b/tests/TestDatabase.cpp new file mode 100644 index 00000000..584fde43 --- /dev/null +++ b/tests/TestDatabase.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Vladimir Svyatski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TestDatabase.h" + +#include + +#include "core/Database.h" +#include "crypto/Crypto.h" + +QTEST_GUILESS_MAIN(TestDatabase) + +void TestDatabase::initTestCase() +{ + QVERIFY(Crypto::init()); +} + +void TestDatabase::testEmptyRecycleBinOnDisabled() +{ + +} + +void TestDatabase::testEmptyRecycleBinOnNotCreated() +{ + +} + +void TestDatabase::testEmptyRecycleBinOnEmpty() +{ + +} + +void TestDatabase::testEmptyRecycleBinWithHierarchicalData() +{ + +} diff --git a/tests/TestDatabase.h b/tests/TestDatabase.h new file mode 100644 index 00000000..55015522 --- /dev/null +++ b/tests/TestDatabase.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Vladimir Svyatski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_TESTDATABASE_H +#define KEEPASSX_TESTDATABASE_H + +#include + +class Database; + +class TestDatabase : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void testEmptyRecycleBinOnDisabled(); + void testEmptyRecycleBinOnNotCreated(); + void testEmptyRecycleBinOnEmpty(); + void testEmptyRecycleBinWithHierarchicalData(); +}; + +#endif // KEEPASSX_TESTDATABASE_H diff --git a/tests/data/RecycleBinDisabled.kdbx b/tests/data/RecycleBinDisabled.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..0bbfb3efee2ceecebb86991548b659289d102799 GIT binary patch literal 2302 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 Q0Bj7l@RJbtWXS~2c!b@0 Date: Sat, 22 Apr 2017 14:35:01 +0300 Subject: [PATCH 4/5] Add recycle bin test cases body --- tests/TestDatabase.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- tests/TestDatabase.h | 2 -- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/tests/TestDatabase.cpp b/tests/TestDatabase.cpp index 584fde43..66eb870f 100644 --- a/tests/TestDatabase.cpp +++ b/tests/TestDatabase.cpp @@ -18,9 +18,12 @@ #include "TestDatabase.h" #include +#include +#include "config-keepassx-tests.h" #include "core/Database.h" #include "crypto/Crypto.h" +#include "keys/PasswordKey.h" QTEST_GUILESS_MAIN(TestDatabase) @@ -31,20 +34,56 @@ void TestDatabase::initTestCase() void TestDatabase::testEmptyRecycleBinOnDisabled() { + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinDisabled.kdbx"); + CompositeKey key; + key.addKey(PasswordKey("123")); + Database* db = Database::openDatabaseFile(filename, key); + QVERIFY(db); + QSignalSpy spyModified(db, SIGNAL(modifiedImmediate())); + + db->emptyRecycleBin(); + //The database must be unmodified in this test after emptying the recycle bin. + QCOMPARE(spyModified.count(), 0); + + delete db; } void TestDatabase::testEmptyRecycleBinOnNotCreated() { + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinNotYetCreated.kdbx"); + CompositeKey key; + key.addKey(PasswordKey("123")); + Database* db = Database::openDatabaseFile(filename, key); + QVERIFY(db); + QSignalSpy spyModified(db, SIGNAL(modifiedImmediate())); + + db->emptyRecycleBin(); + //The database must be unmodified in this test after emptying the recycle bin. + QCOMPARE(spyModified.count(), 0); + + delete db; } void TestDatabase::testEmptyRecycleBinOnEmpty() { + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinEmpty.kdbx"); + CompositeKey key; + key.addKey(PasswordKey("123")); + Database* db = Database::openDatabaseFile(filename, key); + QVERIFY(db); + QSignalSpy spyModified(db, SIGNAL(modifiedImmediate())); + + db->emptyRecycleBin(); + //The database must be unmodified in this test after emptying the recycle bin. + QCOMPARE(spyModified.count(), 0); + + delete db; } void TestDatabase::testEmptyRecycleBinWithHierarchicalData() { - +//TODO: implement } diff --git a/tests/TestDatabase.h b/tests/TestDatabase.h index 55015522..dc9609d7 100644 --- a/tests/TestDatabase.h +++ b/tests/TestDatabase.h @@ -20,8 +20,6 @@ #include -class Database; - class TestDatabase : public QObject { Q_OBJECT From c613f44991c52584121b8eeaeb26364a6ad91866 Mon Sep 17 00:00:00 2001 From: Vladimir Svyatski Date: Sun, 23 Apr 2017 00:50:26 +0300 Subject: [PATCH 5/5] Finish test cases for emptying recycle bin --- tests/TestDatabase.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/TestDatabase.cpp b/tests/TestDatabase.cpp index 66eb870f..a70ada19 100644 --- a/tests/TestDatabase.cpp +++ b/tests/TestDatabase.cpp @@ -19,11 +19,15 @@ #include #include +#include #include "config-keepassx-tests.h" #include "core/Database.h" #include "crypto/Crypto.h" #include "keys/PasswordKey.h" +#include "core/Metadata.h" +#include "core/Group.h" +#include "format/KeePass2Writer.h" QTEST_GUILESS_MAIN(TestDatabase) @@ -85,5 +89,24 @@ void TestDatabase::testEmptyRecycleBinOnEmpty() void TestDatabase::testEmptyRecycleBinWithHierarchicalData() { -//TODO: implement + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinWithData.kdbx"); + CompositeKey key; + key.addKey(PasswordKey("123")); + Database* db = Database::openDatabaseFile(filename, key); + QVERIFY(db); + + QFile originalFile(filename); + qint64 initialSize = originalFile.size(); + + db->emptyRecycleBin(); + QVERIFY(db->metadata()->recycleBin()); + QVERIFY(db->metadata()->recycleBin()->entries().empty()); + QVERIFY(db->metadata()->recycleBin()->children().empty()); + + QTemporaryFile afterCleanup; + KeePass2Writer writer; + writer.writeDatabase(&afterCleanup, db); + QVERIFY(afterCleanup.size() < initialSize); + + delete db; }