/* * Copyright (C) 2012 Felix Geyer <debfx@fobos.de> * * 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 <http://www.gnu.org/licenses/>. */ #include "TestModified.h" #include "mock/MockClock.h" #include <QSignalSpy> #include <QTest> #include "core/Clock.h" #include "core/Database.h" #include "core/Group.h" #include "core/Metadata.h" #include "crypto/Crypto.h" QTEST_GUILESS_MAIN(TestModified) namespace { MockClock* m_clock = nullptr; } void TestModified::initTestCase() { QVERIFY(Crypto::init()); } void TestModified::init() { Q_ASSERT(m_clock == nullptr); m_clock = new MockClock(2010, 5, 5, 10, 30, 10); MockClock::setup(m_clock); } void TestModified::cleanup() { MockClock::teardown(); m_clock = nullptr; } void TestModified::testSignals() { int spyCount = 0; int spyCount2 = 0; auto compositeKey = QSharedPointer<CompositeKey>::create(); QScopedPointer<Database> db(new Database()); auto* root = db->rootGroup(); QSignalSpy spyModified(db.data(), SIGNAL(databaseModified())); db->setKey(compositeKey); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); auto* group1 = new Group(); group1->setParent(root); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); auto* group2 = new Group(); group2->setParent(root); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group2->setParent(root, 0); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); auto* entry1 = new Entry(); entry1->setGroup(group1); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); QScopedPointer<Database> db2(new Database()); auto* root2 = db2->rootGroup(); QSignalSpy spyModified2(db2.data(), SIGNAL(databaseModified())); group1->setParent(root2); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); ++spyCount2; QTRY_COMPARE(spyModified2.count(), spyCount2); entry1->setTitle("test"); QTRY_COMPARE(spyModified.count(), spyCount); ++spyCount2; QTRY_COMPARE(spyModified2.count(), spyCount2); auto* entry2 = new Entry(); entry2->setGroup(group2); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); QTRY_COMPARE(spyModified2.count(), spyCount2); entry2->setGroup(root2); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); ++spyCount2; QTRY_COMPARE(spyModified2.count(), spyCount2); entry2->setTitle("test2"); QTRY_COMPARE(spyModified.count(), spyCount); ++spyCount2; QTRY_COMPARE(spyModified2.count(), spyCount2); auto* group3 = new Group(); group3->setParent(root); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); auto* group4 = new Group(); group4->setParent(group3); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); delete group4; ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); delete entry2; ++spyCount2; QTRY_COMPARE(spyModified2.count(), spyCount2); QTRY_COMPARE(spyModified.count(), spyCount); QTRY_COMPARE(spyModified2.count(), spyCount2); } void TestModified::testGroupSets() { int spyCount = 0; QScopedPointer<Database> db(new Database()); auto* root = db->rootGroup(); auto* group = new Group(); group->setParent(root); QSignalSpy spyModified(db.data(), SIGNAL(databaseModified())); root->setUuid(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); root->setUuid(root->uuid()); QTRY_COMPARE(spyModified.count(), spyCount); root->setName("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); root->setName(root->name()); QTRY_COMPARE(spyModified.count(), spyCount); root->setNotes("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); root->setNotes(root->notes()); QTRY_COMPARE(spyModified.count(), spyCount); root->setIcon(1); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); root->setIcon(root->iconNumber()); QTRY_COMPARE(spyModified.count(), spyCount); root->setIcon(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); root->setIcon(root->iconUuid()); QTRY_COMPARE(spyModified.count(), spyCount); group->setUuid(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group->setUuid(group->uuid()); QTRY_COMPARE(spyModified.count(), spyCount); group->setName("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group->setName(group->name()); QTRY_COMPARE(spyModified.count(), spyCount); group->setNotes("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group->setNotes(group->notes()); QTRY_COMPARE(spyModified.count(), spyCount); group->setIcon(1); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group->setIcon(group->iconNumber()); QTRY_COMPARE(spyModified.count(), spyCount); group->setIcon(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group->setIcon(group->iconUuid()); QTRY_COMPARE(spyModified.count(), spyCount); } void TestModified::testEntrySets() { int spyCount = 0; QScopedPointer<Database> db(new Database()); auto* root = db->rootGroup(); auto* group = new Group(); group->setParent(root); auto* entry = new Entry(); entry->setGroup(group); QSignalSpy spyModified(db.data(), SIGNAL(databaseModified())); entry->setUuid(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setUuid(entry->uuid()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setTitle("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setTitle(entry->title()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setUrl("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setUrl(entry->url()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setUsername("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setUsername(entry->username()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setPassword("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setPassword(entry->password()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setNotes("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setNotes(entry->notes()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setIcon(1); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setIcon(entry->iconNumber()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setIcon(QUuid::createUuid()); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setIcon(entry->iconUuid()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setTags("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setTags(entry->tags()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setExpires(true); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setExpires(entry->timeInfo().expires()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setExpiryTime(Clock::currentDateTimeUtc().addYears(1)); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setExpiryTime(entry->timeInfo().expiryTime()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setAutoTypeEnabled(false); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setAutoTypeEnabled(entry->autoTypeEnabled()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setAutoTypeObfuscation(1); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setAutoTypeObfuscation(entry->autoTypeObfuscation()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setDefaultAutoTypeSequence("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setDefaultAutoTypeSequence(entry->defaultAutoTypeSequence()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setForegroundColor(QString("#FF0000")); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setForegroundColor(entry->foregroundColor()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setBackgroundColor(QString("#FF0000")); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setBackgroundColor(entry->backgroundColor()); QTRY_COMPARE(spyModified.count(), spyCount); entry->setOverrideUrl("test"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->setOverrideUrl(entry->overrideUrl()); QTRY_COMPARE(spyModified.count(), spyCount); entry->attributes()->set("test key", "test value", false); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->attributes()->set("test key", entry->attributes()->value("test key"), false); QTRY_COMPARE(spyModified.count(), spyCount); entry->attributes()->set("test key", entry->attributes()->value("test key"), true); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->attributes()->set("test key", "new test value", true); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->attributes()->set("test key2", "test value2", true); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true); QTRY_COMPARE(spyModified.count(), spyCount); } void TestModified::testHistoryItems() { QScopedPointer<Entry> entry(new Entry()); QDateTime created = entry->timeInfo().creationTime(); entry->setUuid(QUuid::createUuid()); entry->setTitle("a"); entry->setTags("a"); QScopedPointer<EntryAttributes> attributes(new EntryAttributes()); attributes->copyCustomKeysFrom(entry->attributes()); int historyItemsSize = 0; entry->beginUpdate(); entry->setTitle("a"); entry->setTags("a"); entry->setOverrideUrl(""); entry->endUpdate(); QCOMPARE(entry->historyItems().size(), historyItemsSize); QDateTime modified = entry->timeInfo().lastModificationTime(); m_clock->advanceSecond(10); entry->beginUpdate(); entry->setTitle("b"); entry->endUpdate(); QCOMPARE(entry->historyItems().size(), ++historyItemsSize); auto* historyEntry = entry->historyItems().at(historyItemsSize - 1); QCOMPARE(historyEntry->title(), QString("a")); QCOMPARE(historyEntry->uuid(), entry->uuid()); QCOMPARE(historyEntry->tags(), entry->tags()); QCOMPARE(historyEntry->overrideUrl(), entry->overrideUrl()); QCOMPARE(historyEntry->timeInfo().creationTime(), created); QCOMPARE(historyEntry->timeInfo().lastModificationTime(), modified); QCOMPARE(historyEntry->historyItems().size(), 0); entry->beginUpdate(); entry->setTags("b"); entry->endUpdate(); QCOMPARE(entry->historyItems().size(), ++historyItemsSize); QCOMPARE(entry->historyItems().at(historyItemsSize - 1)->tags(), QString("a")); entry->beginUpdate(); entry->attachments()->set("test", QByteArray("value")); entry->endUpdate(); QCOMPARE(entry->historyItems().size(), ++historyItemsSize); QCOMPARE(entry->historyItems().at(historyItemsSize - 1)->attachments()->keys().size(), 0); attributes->set("k", "myvalue"); entry->beginUpdate(); entry->attributes()->copyCustomKeysFrom(attributes.data()); entry->endUpdate(); QCOMPARE(entry->historyItems().size(), ++historyItemsSize); QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k")); QScopedPointer<Database> db(new Database()); auto* root = db->rootGroup(); db->metadata()->setHistoryMaxItems(3); db->metadata()->setHistoryMaxSize(-1); auto* entry2 = new Entry(); entry2->setGroup(root); entry2->beginUpdate(); entry2->setTitle("1"); entry2->endUpdate(); entry2->beginUpdate(); entry2->setTitle("2"); entry2->endUpdate(); entry2->beginUpdate(); entry2->setTitle("3"); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 3); entry2->beginUpdate(); entry2->setTitle("4"); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 3); db->metadata()->setHistoryMaxItems(1); entry2->beginUpdate(); entry2->setTitle("5"); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 1); auto* historyEntry2 = entry2->historyItems().at(0); QCOMPARE(historyEntry2->title(), QString("4")); db->metadata()->setHistoryMaxItems(-1); for (int i = 0; i < 20; i++) { entry2->beginUpdate(); entry2->setTitle("6"); entry2->endUpdate(); entry2->beginUpdate(); entry2->setTitle("6b"); entry2->endUpdate(); } QCOMPARE(entry2->historyItems().size(), 41); db->metadata()->setHistoryMaxItems(0); entry2->beginUpdate(); entry2->setTitle("7"); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 0); const int historyMaxSize = 19000; db->metadata()->setHistoryMaxItems(-1); db->metadata()->setHistoryMaxSize(historyMaxSize); const QString key("test"); entry2->beginUpdate(); entry2->attachments()->set(key, QByteArray(18000, 'X')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 18000 + key.size()); QCOMPARE(entry2->historyItems().size(), 1); historyEntry2 = entry2->historyItems().at(0); QCOMPARE(historyEntry2->title(), QString("7")); entry2->beginUpdate(); entry2->setTitle("8"); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 2); entry2->beginUpdate(); entry2->attachments()->remove(key); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 0); QCOMPARE(entry2->historyItems().size(), 1); entry2->beginUpdate(); entry2->attachments()->set("test2", QByteArray(6000, 'a')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 6000 + key.size() + 1); QCOMPARE(entry2->historyItems().size(), 2); entry2->beginUpdate(); entry2->attachments()->set("test3", QByteArray(6000, 'b')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 12000 + (key.size() + 1) * 2); QCOMPARE(entry2->historyItems().size(), 2); entry2->beginUpdate(); entry2->attachments()->set("test4", QByteArray(6000, 'c')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 18000 + (key.size() + 1) * 3); QCOMPARE(entry2->historyItems().size(), 3); entry2->beginUpdate(); entry2->attachments()->set("test5", QByteArray(6000, 'd')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 24000 + (key.size() + 1) * 4); QCOMPARE(entry2->historyItems().size(), 1); } void TestModified::testHistoryMaxSize() { QScopedPointer<Database> db(new Database()); const QString key("test"); auto entry1 = new Entry(); entry1->setGroup(db->rootGroup()); QCOMPARE(entry1->historyItems().size(), 0); const int reservedSize1 = entry1->attributes()->attributesSize(); db->metadata()->setHistoryMaxItems(-1); db->metadata()->setHistoryMaxSize(18000 + key.size() * 3 + reservedSize1 * 4); entry1->beginUpdate(); entry1->attachments()->set(key, QByteArray(6000, 'a')); entry1->endUpdate(); QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); QCOMPARE(entry1->historyItems().size(), 1); entry1->beginUpdate(); entry1->attachments()->set(key, QByteArray(6000, 'b')); entry1->endUpdate(); QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); QCOMPARE(entry1->historyItems().size(), 2); entry1->beginUpdate(); entry1->attachments()->set(key, QByteArray(6000, 'c')); entry1->endUpdate(); QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); QCOMPARE(entry1->historyItems().size(), 3); entry1->beginUpdate(); entry1->attachments()->set(key, QByteArray(6000, 'd')); entry1->endUpdate(); QCOMPARE(entry1->attachments()->attachmentsSize(), 6000 + key.size()); QCOMPARE(entry1->historyItems().size(), 4); auto entry2 = new Entry(); entry2->setGroup(db->rootGroup()); QCOMPARE(entry2->historyItems().size(), 0); const int historyMaxSize = 17000; const int reservedSize2 = entry2->attributes()->attributesSize(); db->metadata()->setHistoryMaxSize(historyMaxSize); entry2->beginUpdate(); entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), historyMaxSize - reservedSize2 + 1); QCOMPARE(entry2->historyItems().size(), 1); // history size overflow entry2->beginUpdate(); entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'b')); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->attachments()->remove(key); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 0); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a')); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), historyMaxSize - reservedSize2 + 1); QCOMPARE(entry2->historyItems().size(), 1); // history size overflow entry2->beginUpdate(); entry2->attachments()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'b')); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->attachments()->remove(key); entry2->endUpdate(); QCOMPARE(entry2->attachments()->attachmentsSize(), 0); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->setTags(QByteArray(historyMaxSize - reservedSize2 + 1, 'a')); entry2->endUpdate(); QCOMPARE(entry2->tags().size(), historyMaxSize - reservedSize2 + 1); QCOMPARE(entry2->historyItems().size(), 1); // history size overflow entry2->beginUpdate(); entry2->setTags(QByteArray(historyMaxSize - reservedSize2 + 1, 'b')); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->setTags(""); entry2->endUpdate(); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->attributes()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a')); entry2->endUpdate(); QCOMPARE(entry2->attributes()->attributesSize(), historyMaxSize + 1); QCOMPARE(entry2->historyItems().size(), 1); // history size overflow entry2->beginUpdate(); entry2->attributes()->set(key, QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'b')); entry2->endUpdate(); QCOMPARE(entry2->attributes()->attributesSize(), historyMaxSize + 1); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); entry2->attributes()->remove(key); entry2->endUpdate(); QCOMPARE(entry2->attributes()->attributesSize(), reservedSize2); QCOMPARE(entry2->historyItems().size(), 0); entry2->beginUpdate(); AutoTypeAssociations::Association association; association.window = key; association.sequence = QByteArray(historyMaxSize - key.size() - reservedSize2 + 1, 'a'); entry2->autoTypeAssociations()->add(association); entry2->endUpdate(); QCOMPARE(entry2->autoTypeAssociations()->associationsSize(), historyMaxSize - reservedSize2 + 1); QCOMPARE(entry2->historyItems().size(), 1); entry2->beginUpdate(); entry2->autoTypeAssociations()->remove(0); entry2->endUpdate(); QCOMPARE(entry2->autoTypeAssociations()->associationsSize(), 0); QCOMPARE(entry2->historyItems().size(), 0); } void TestModified::testCustomData() { int spyCount = 0; QScopedPointer<Database> db(new Database()); auto* root = db->rootGroup(); auto* group = new Group(); group->setParent(root); auto* entry = new Entry(); entry->setGroup(group); QSignalSpy spyModified(db.data(), SIGNAL(databaseModified())); db->metadata()->customData()->set("Key", "Value"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); db->metadata()->customData()->set("Key", "Value"); QTRY_COMPARE(spyModified.count(), spyCount); entry->customData()->set("Key", "Value"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); entry->customData()->set("Key", "Value"); QTRY_COMPARE(spyModified.count(), spyCount); group->customData()->set("Key", "Value"); ++spyCount; QTRY_COMPARE(spyModified.count(), spyCount); group->customData()->set("Key", "Value"); QTRY_COMPARE(spyModified.count(), spyCount); }