Skip to content

Commit

Permalink
Improve Database and CLI tests
Browse files Browse the repository at this point in the history
  • Loading branch information
droidmonkey committed Oct 20, 2019
1 parent 744b4ab commit 1e69427
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 89 deletions.
4 changes: 2 additions & 2 deletions src/core/Database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Database::Database()

s_uuidMap.insert(m_uuid, this);

connect(m_metadata, SIGNAL(metadataModified()), this, SLOT(markAsModified()));
connect(m_metadata, SIGNAL(metadataModified()), SLOT(markAsModified()));
connect(m_timer, SIGNAL(timeout()), SIGNAL(databaseModified()));
connect(this, SIGNAL(databaseOpened()), SLOT(updateCommonUsernames()));
connect(this, SIGNAL(databaseSaved()), SLOT(updateCommonUsernames()));
Expand Down Expand Up @@ -189,7 +189,7 @@ bool Database::save(QString* error, bool atomic, bool backup)
/**
* Save the database to a specific file.
*
* If atmoic is false, this function uses QTemporaryFile instead of QSaveFile
* If atomic is false, this function uses QTemporaryFile instead of QSaveFile
* due to a bug in Qt (https://bugreports.qt.io/browse/QTBUG-57299) that may
* prevent the QSaveFile from renaming itself when using Dropbox, Google Drive,
* or OneDrive.
Expand Down
42 changes: 23 additions & 19 deletions tests/TestCli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,36 +144,32 @@ void TestCli::init()

m_stdinFile.reset(new TemporaryFile());
m_stdinFile->open();
m_stdinHandle = fdopen(m_stdinFile->handle(), "r+");
Utils::STDIN = m_stdinHandle;
Utils::STDIN = fdopen(m_stdinFile->handle(), "r+");

m_stdoutFile.reset(new TemporaryFile());
m_stdoutFile->open();
m_stdoutHandle = fdopen(m_stdoutFile->handle(), "r+");
Utils::STDOUT = m_stdoutHandle;
Utils::STDOUT = fdopen(m_stdoutFile->handle(), "r+");

m_stderrFile.reset(new TemporaryFile());
m_stderrFile->open();
m_stderrHandle = fdopen(m_stderrFile->handle(), "r+");
Utils::STDERR = m_stderrHandle;
Utils::STDERR = fdopen(m_stderrFile->handle(), "r+");
}

void TestCli::cleanup()
{
m_dbFile.reset();

m_dbFile2.reset();
m_keyFileProtectedDbFile.reset();
m_keyFileProtectedNoPasswordDbFile.reset();
m_yubiKeyProtectedDbFile.reset();

m_stdinFile.reset();
m_stdinHandle = stdin;
Utils::STDIN = stdin;

m_stdoutFile.reset();
Utils::STDOUT = stdout;
m_stdoutHandle = stdout;

m_stderrFile.reset();
m_stderrHandle = stderr;
Utils::STDERR = stderr;
}

Expand All @@ -184,8 +180,8 @@ void TestCli::cleanupTestCase()
QSharedPointer<Database> TestCli::readTestDatabase() const
{
Utils::Test::setNextPassword("a");
auto db = QSharedPointer<Database>(Utils::unlockDatabase(m_dbFile->fileName(), true, "", "", m_stdoutHandle));
m_stdoutFile->seek(ftell(m_stdoutHandle)); // re-synchronize handles
auto db = QSharedPointer<Database>(Utils::unlockDatabase(m_dbFile->fileName(), true, "", "", Utils::STDOUT));
m_stdoutFile->seek(ftell(Utils::STDOUT)); // re-synchronize handles
return db;
}

Expand Down Expand Up @@ -282,17 +278,25 @@ void TestCli::testAdd()
addCmd.execute({"add", "-q", "-u", "newuser", "-g", "-L", "20", m_dbFile->fileName(), "/newentry-quiet"});
m_stdoutFile->seek(pos);
m_stderrFile->seek(posErr);
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
db = readTestDatabase();
entry = db->rootGroup()->findEntryByPath("/newentry-quiet");
QVERIFY(entry);
QCOMPARE(entry->password().size(), 20);

pos = m_stdoutFile->pos();
posErr = m_stderrFile->pos();
Utils::Test::setNextPassword("a");
Utils::Test::setNextPassword("newpassword");
addCmd.execute(
{"add", "-u", "newuser2", "--url", "https://example.net/", "-p", m_dbFile->fileName(), "/newuser-entry2"});
m_stdoutFile->seek(pos);
m_stderrFile->seek(posErr);
m_stdoutFile->readLine(); // skip password prompt
m_stdoutFile->readLine(); // skip password input
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added entry newuser-entry2.\n"));

db = readTestDatabase();
entry = db->rootGroup()->findEntryByPath("/newuser-entry2");
Expand All @@ -309,8 +313,8 @@ void TestCli::testAdd()
m_stdoutFile->seek(pos);
m_stderrFile->seek(posErr);
m_stdoutFile->readLine(); // skip password prompt
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added entry newuser-entry3.\n"));
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added entry newuser-entry3.\n"));

db = readTestDatabase();
entry = db->rootGroup()->findEntryByPath("/newuser-entry3");
Expand Down Expand Up @@ -339,8 +343,8 @@ void TestCli::testAdd()
m_stdoutFile->seek(pos);
m_stderrFile->seek(posErr);
m_stdoutFile->readLine(); // skip password prompt
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added entry newuser-entry4.\n"));
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added entry newuser-entry4.\n"));

db = readTestDatabase();
entry = db->rootGroup()->findEntryByPath("/newuser-entry4");
Expand Down Expand Up @@ -541,7 +545,7 @@ void TestCli::testCreate()

QScopedPointer<QTemporaryDir> testDir(new QTemporaryDir());

QString databaseFilename = testDir->path() + "testCreate1.kdbx";
QString databaseFilename = testDir->path() + "/testCreate1.kdbx";
// Password
Utils::Test::setNextPassword("a");
createCmd.execute({"create", databaseFilename});
Expand All @@ -568,8 +572,8 @@ void TestCli::testCreate()
QCOMPARE(m_stderrFile->readAll(), errorMessage.toUtf8());

// Testing with keyfile creation
QString databaseFilename2 = testDir->path() + "testCreate2.kdbx";
QString keyfilePath = testDir->path() + "keyfile.txt";
QString databaseFilename2 = testDir->path() + "/testCreate2.kdbx";
QString keyfilePath = testDir->path() + "/keyfile.txt";
pos = m_stdoutFile->pos();
errPos = m_stderrFile->pos();
Utils::Test::setNextPassword("a");
Expand All @@ -586,7 +590,7 @@ void TestCli::testCreate()
QVERIFY(db2);

// Testing with existing keyfile
QString databaseFilename3 = testDir->path() + "testCreate3.kdbx";
QString databaseFilename3 = testDir->path() + "/testCreate3.kdbx";
pos = m_stdoutFile->pos();
errPos = m_stderrFile->pos();
Utils::Test::setNextPassword("a");
Expand Down
3 changes: 0 additions & 3 deletions tests/TestCli.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ private slots:
QScopedPointer<TemporaryFile> m_stdoutFile;
QScopedPointer<TemporaryFile> m_stderrFile;
QScopedPointer<TemporaryFile> m_stdinFile;
FILE* m_stdoutHandle = stdout;
FILE* m_stderrHandle = stderr;
FILE* m_stdinHandle = stdin;
};

#endif // KEEPASSXC_TESTCLI_H
67 changes: 54 additions & 13 deletions tests/TestDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@

#include "config-keepassx-tests.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include "crypto/Crypto.h"
#include "format/KeePass2Writer.h"
#include "keys/PasswordKey.h"
#include "util/TemporaryFile.h"

QTEST_GUILESS_MAIN(TestDatabase)

static QString dbFileName = QStringLiteral(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx");

void TestDatabase::initTestCase()
{
QVERIFY(Crypto::init());
Expand All @@ -45,7 +46,7 @@ void TestDatabase::testOpen()
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("a"));

bool ok = db->open(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx"), key);
bool ok = db->open(dbFileName, key);
QVERIFY(ok);

QVERIFY(db->isInitialized());
Expand All @@ -57,16 +58,8 @@ void TestDatabase::testOpen()

void TestDatabase::testSave()
{
QByteArray data;
QFile sourceDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx"));
QVERIFY(sourceDbFile.open(QIODevice::ReadOnly));
QVERIFY(Tools::readAllFromDevice(&sourceDbFile, data));
sourceDbFile.close();

TemporaryFile tempFile;
QVERIFY(tempFile.open());
QCOMPARE(tempFile.write(data), static_cast<qint64>((data.size())));
tempFile.close();
QVERIFY(tempFile.copyFromFile(dbFileName));

auto db = QSharedPointer<Database>::create();
auto key = QSharedPointer<CompositeKey>::create();
Expand All @@ -79,15 +72,63 @@ void TestDatabase::testSave()
// Test safe saves
db->metadata()->setName("test");
QVERIFY(db->isModified());
QVERIFY2(db->save(&error), error.toLatin1());
QVERIFY(!db->isModified());

// Test unsafe saves
db->metadata()->setName("test2");
QVERIFY2(db->save(&error, false, false), error.toLatin1());

QVERIFY2(db->save(&error), error.toLatin1());
QVERIFY(!db->isModified());

// Test save backups
db->metadata()->setName("test3");
QVERIFY2(db->save(&error, true, true), error.toLatin1());
QVERIFY(!db->isModified());

// Confirm backup exists and then delete it
auto re = QRegularExpression("(\\.[^.]+)$");
auto match = re.match(tempFile.fileName());
auto backupFilePath = tempFile.fileName();
backupFilePath = backupFilePath.replace(re, "") + ".old" + match.captured(1);
QVERIFY(QFile::exists(backupFilePath));
QFile::remove(backupFilePath);
QVERIFY(!QFile::exists(backupFilePath));
}

void TestDatabase::testSignals()
{
TemporaryFile tempFile;
QVERIFY(tempFile.copyFromFile(dbFileName));

auto db = QSharedPointer<Database>::create();
auto key = QSharedPointer<CompositeKey>::create();
key->addKey(QSharedPointer<PasswordKey>::create("a"));

QSignalSpy spyFilePathChanged(db.data(), SIGNAL(filePathChanged(const QString&, const QString&)));
QString error;
bool ok = db->open(tempFile.fileName(), key, &error);
QVERIFY(ok);
QCOMPARE(spyFilePathChanged.count(), 1);

QSignalSpy spyModified(db.data(), SIGNAL(databaseModified()));
db->metadata()->setName("test1");
QTRY_COMPARE(spyModified.count(), 1);

QSignalSpy spySaved(db.data(), SIGNAL(databaseSaved()));
QVERIFY(db->save(&error));
QCOMPARE(spySaved.count(), 1);

QSignalSpy spyFileChanged(db.data(), SIGNAL(databaseFileChanged()));
QVERIFY(tempFile.copyFromFile(dbFileName));
QTRY_COMPARE(spyFileChanged.count(), 1);
QTRY_VERIFY(!db->isModified());

db->metadata()->setName("test2");
QTRY_VERIFY(db->isModified());

QSignalSpy spyDiscarded(db.data(), SIGNAL(databaseDiscarded()));
QVERIFY(db->open(tempFile.fileName(), key, &error));
QCOMPARE(spyDiscarded.count(), 1);
}

void TestDatabase::testEmptyRecycleBinOnDisabled()
Expand Down
1 change: 1 addition & 0 deletions tests/TestDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ private slots:
void initTestCase();
void testOpen();
void testSave();
void testSignals();
void testEmptyRecycleBinOnDisabled();
void testEmptyRecycleBinOnNotCreated();
void testEmptyRecycleBinOnEmpty();
Expand Down
49 changes: 14 additions & 35 deletions tests/gui/TestGui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@

QTEST_MAIN(TestGui)

static QString dbFileName = QStringLiteral(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx");

void TestGui::initTestCase()
{
QVERIFY(Crypto::init());
Expand All @@ -91,26 +93,19 @@ void TestGui::initTestCase()

m_mainWindow.reset(new MainWindow());
Bootstrap::restoreMainWindowState(*m_mainWindow);
Bootstrap::bootstrapApplication();
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
m_mainWindow->show();

// Load the NewDatabase.kdbx file into temporary storage
QFile sourceDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx"));
QVERIFY(sourceDbFile.open(QIODevice::ReadOnly));
QVERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData));
sourceDbFile.close();
}

// Every test starts with opening the temp database
void TestGui::init()
{
m_dbFile.reset(new TemporaryFile());
// Write the temp storage to a temp database file for use in our tests
QVERIFY(m_dbFile->open());
QCOMPARE(m_dbFile->write(m_dbData), static_cast<qint64>((m_dbData.size())));
m_dbFileName = QFileInfo(m_dbFile->fileName()).fileName();
m_dbFilePath = m_dbFile->fileName();
m_dbFile->close();
// Copy the test database file to the temporary file
QVERIFY(m_dbFile.copyFromFile(dbFileName));

m_dbFileName = QFileInfo(m_dbFile.fileName()).fileName();
m_dbFilePath = m_dbFile.fileName();

// make sure window is activated or focus tests may fail
m_mainWindow->activateWindow();
Expand Down Expand Up @@ -145,13 +140,11 @@ void TestGui::cleanup()
if (m_dbWidget) {
delete m_dbWidget;
}

m_dbFile->remove();
}

void TestGui::cleanupTestCase()
{
m_dbFile->remove();
m_dbFile.remove();
}

void TestGui::testSettingsDefaultTabOrder()
Expand Down Expand Up @@ -333,19 +326,10 @@ void TestGui::testAutoreloadDatabase()
{
config()->set("AutoReloadOnChange", false);

// Load the MergeDatabase.kdbx file into temporary storage
QByteArray unmodifiedMergeDatabase;
QFile mergeDbFile(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx"));
QVERIFY(mergeDbFile.open(QIODevice::ReadOnly));
QVERIFY(Tools::readAllFromDevice(&mergeDbFile, unmodifiedMergeDatabase));
mergeDbFile.close();

// Test accepting new file in autoreload
MessageBox::setNextAnswer(MessageBox::Yes);
// Overwrite the current database with the temp data
QVERIFY(m_dbFile->open());
QVERIFY(m_dbFile->write(unmodifiedMergeDatabase, static_cast<qint64>(unmodifiedMergeDatabase.size())));
m_dbFile->close();
QVERIFY(m_dbFile.copyFromFile(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx")));

QTRY_VERIFY(m_db != m_dbWidget->database());
m_db = m_dbWidget->database();
Expand All @@ -360,10 +344,8 @@ void TestGui::testAutoreloadDatabase()

// Test rejecting new file in autoreload
MessageBox::setNextAnswer(MessageBox::No);
// Overwrite the current temp database with a new file
QVERIFY(m_dbFile->open());
QVERIFY(m_dbFile->write(unmodifiedMergeDatabase, static_cast<qint64>(unmodifiedMergeDatabase.size())));
m_dbFile->close();
// Overwrite the current database with the temp data
QVERIFY(m_dbFile.copyFromFile(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx")));

// Ensure the merge did not take place
QCOMPARE(m_db->rootGroup()->findChildByName("General")->entries().size(), 0);
Expand All @@ -382,9 +364,7 @@ void TestGui::testAutoreloadDatabase()
// This is saying yes to merging the entries
MessageBox::setNextAnswer(MessageBox::Merge);
// Overwrite the current database with the temp data
QVERIFY(m_dbFile->open());
QVERIFY(m_dbFile->write(unmodifiedMergeDatabase, static_cast<qint64>(unmodifiedMergeDatabase.size())));
m_dbFile->close();
QVERIFY(m_dbFile.copyFromFile(QString(KEEPASSX_TEST_DATA_DIR).append("/MergeDatabase.kdbx")));

QTRY_VERIFY(m_db != m_dbWidget->database());
m_db = m_dbWidget->database();
Expand Down Expand Up @@ -1279,9 +1259,8 @@ void TestGui::testDragAndDropKdbxFiles()

QCOMPARE(m_tabWidget->count(), openedDatabasesCount);

const QString goodDatabaseFilePath(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx"));
QMimeData goodMimeData;
goodMimeData.setUrls({QUrl::fromLocalFile(goodDatabaseFilePath)});
goodMimeData.setUrls({QUrl::fromLocalFile(dbFileName)});
QDragEnterEvent goodDragEvent(QPoint(1, 1), Qt::LinkAction, &goodMimeData, Qt::LeftButton, Qt::NoModifier);
qApp->notify(m_mainWindow.data(), &goodDragEvent);
QCOMPARE(goodDragEvent.isAccepted(), true);
Expand Down
3 changes: 1 addition & 2 deletions tests/gui/TestGui.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ private slots:
QPointer<DatabaseTabWidget> m_tabWidget;
QPointer<DatabaseWidget> m_dbWidget;
QSharedPointer<Database> m_db;
QByteArray m_dbData;
QScopedPointer<TemporaryFile> m_dbFile;
TemporaryFile m_dbFile;
QString m_dbFileName;
QString m_dbFilePath;
};
Expand Down
Loading

0 comments on commit 1e69427

Please sign in to comment.