[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [KSecretService] 23e10a7: (Almost) complete the test for the ksecret
From: Michael Leupold <lemma () confuego ! org>
Date: 2010-11-09 19:14:34
Message-ID: 20101109191434.52E19A6109 () git ! kde ! org
[Download RAW message or body]
commit 23e10a7a4e8d14fb9d870d8cbe53002abdeeddfb
branch master
Author: Michael Leupold <lemma@confuego.org>
Date: Sat Oct 30 17:38:47 2010 +0000
(Almost) complete the test for the ksecret backend. Also introduce a sync-timer \
which syncs collections to disk when changed.
svn path=/trunk/playground/base/ksecretservice/; revision=1191341
diff --git a/backend/ksecret/ksecretcollection.cpp \
b/backend/ksecret/ksecretcollection.cpp index 4303d96..4369c57 100644
--- a/backend/ksecret/ksecretcollection.cpp
+++ b/backend/ksecret/ksecretcollection.cpp
@@ -25,14 +25,15 @@
#include "../securebuffer.h"
#include <secrettool.h>
+#include <klocalizedstring.h>
+#include <ksavefile.h>
+#include <kstandarddirs.h>
+
#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <QtCore/QSet>
#include <QtCore/QTimer>
-#include <klocalizedstring.h>
-#include <ksavefile.h>
-
// this must not be changed or else file compatibility is gone!
#define VERIFIER_LENGTH 64
@@ -67,6 +68,7 @@ KSecretCollection *KSecretCollection::create(const QString &id, \
const QCA::Secur {
KSecretCollection *coll = new KSecretCollection(parent);
coll->m_id = id;
+ coll->m_path = KGlobal::dirs()->saveLocation("ksecret") + '/' + id + ".ksecret";
coll->m_algoHash = KSecretFile::SHA256;
coll->m_algoCipher = KSecretFile::AES256;
@@ -107,14 +109,23 @@ KSecretCollection *KSecretCollection::create(const QString &id, \
const QCA::Secur
coll->m_cipher->setup(QCA::Encode, *coll->m_symmetricKey, \
coll->m_verInitVector); coll->m_verEncryptedRandom = \
coll->m_cipher->update(randomData); \
coll->m_verEncryptedRandom.append(coll->m_cipher->final()); +
+ // write new collection to disk
+ if (!coll->serialize(errorMessage)) {
+ delete coll;
+ return 0;
+ }
+ coll->m_dirty = false;
return coll;
}
KSecretCollection::KSecretCollection(BackendCollectionManager *parent)
- : BackendCollection(parent), m_hash(0),
- m_mac(0), m_cipher(0), m_symmetricKey(0)
+ : BackendCollection(parent), m_hash(0), m_mac(0), m_cipher(0),
+ m_symmetricKey(0), m_dirty(true)
{
+ m_syncTimer.setSingleShot(true);
+ connect(&m_syncTimer, SIGNAL(timeout()), SLOT(sync()));
}
KSecretCollection::~KSecretCollection()
@@ -147,6 +158,8 @@ BackendReturn<void> KSecretCollection::setLabel(const QString \
&label) }
m_label = label;
+ m_dirty = true;
+ startSyncTimer();
emit collectionChanged(this);
return NoError;
}
@@ -222,6 +235,8 @@ DeleteCollectionJob *KSecretCollection::createDeleteJob(const \
CollectionDeleteIn
BackendReturn<bool> KSecretCollection::deleteCollection()
{
+ // stop any sync timers in progress
+ m_syncTimer.stop();
// remove the ksecret file
if(!QFile::remove(m_path)) {
return false;
@@ -296,8 +311,13 @@ BackendReturn<BackendItem*> KSecretCollection::createItem(const \
QString &label, // new item, signals need to be wired
connect(item, SIGNAL(itemDeleted(BackendItem*)), \
SLOT(slotItemDeleted(BackendItem*)));
connect(item, SIGNAL(itemChanged(BackendItem*)), \
SIGNAL(itemChanged(BackendItem*))); + connect(item, \
SIGNAL(itemChanged(BackendItem*)), SLOT(startSyncTimer())); emit itemCreated(item);
}
+
+ // sync
+ m_dirty = true;
+ startSyncTimer();
return item;
}
@@ -321,6 +341,10 @@ void KSecretCollection::slotItemDeleted(BackendItem *item)
m_reverseItemHashes.remove(kitem);
}
m_items.remove(kitem->id());
+
+ // sync
+ m_dirty = true;
+ startSyncTimer();
emit itemDeleted(item);
}
@@ -345,6 +369,29 @@ void KSecretCollection::changeAttributeHashes(KSecretItem *item)
m_reverseItemHashes.insert(item, attributeHashes);
}
+void KSecretCollection::startSyncTimer()
+{
+ // mark collection dirty and start sync in 3 seconds.
+ // make sure it's really synced to constant changes to the collection don't
+ // leave it unsaved for too long.
+ if (!m_syncTimer.isActive()) {
+ m_syncTimer.start(3000);
+ }
+}
+
+void KSecretCollection::sync()
+{
+ if (m_dirty) {
+ QString errorMessage;
+ if (!serialize(errorMessage)) {
+ // try to resync if syncing failed
+ startSyncTimer();
+ } else {
+ m_dirty = false;
+ }
+ }
+}
+
KSecretCollection *KSecretCollection::deserialize(const QString &path,
KSecretCollectionManager *parent,
QString &errorMessage)
@@ -450,10 +497,12 @@ BackendReturn<bool> KSecretCollection::lock()
if(isLocked()) {
return true;
} else {
- // TODO: only serialize if dirty
- QString errorMessage;
- if(!serialize(errorMessage)) {
- return BackendReturn<bool>(false, ErrorOther, errorMessage);
+ if (m_dirty) {
+ QString errorMessage;
+ if(!serialize(errorMessage)) {
+ return BackendReturn<bool>(false, ErrorOther, errorMessage);
+ }
+ m_dirty = false;
}
// remove individual item secrets
@@ -929,7 +978,7 @@ bool KSecretCollection::serialize(QString &errorMessage) const
errorMessage = genericSavingErrorMessage();
return false;
}
-
+
return true;
}
@@ -1297,6 +1346,11 @@ bool KSecretCollection::setApplicationPermission(const \
QString& path, BackendCol {
// TODO: implement
Q_ASSERT(0);
+
+ // sync
+ m_dirty = true;
+ startSyncTimer();
+
return true;
}
diff --git a/backend/ksecret/ksecretcollection.h \
b/backend/ksecret/ksecretcollection.h index 1e2e896..a9dd303 100644
--- a/backend/ksecret/ksecretcollection.h
+++ b/backend/ksecret/ksecretcollection.h
@@ -25,6 +25,8 @@
#include "ksecretitem.h"
#include "ksecretfile.h"
+#include <QtCore/QTimer>
+
class KSecretCollectionManager;
class KSecretUnlockCollectionJob;
class KSecretLockCollectionJob;
@@ -209,7 +211,20 @@ private Q_SLOTS:
* @param item Item whose attributes changed
*/
void changeAttributeHashes(KSecretItem *item);
-
+
+ /**
+ * This slot can be called to start the sync timer (if it's not running
+ * yet). It's supposed to be called after every change to the collection
+ * or one of its items.
+ */
+ void startSyncTimer();
+
+ /**
+ * This slot is called by the sync timer to sync the collection
+ * on changes.
+ */
+ void sync();
+
public:
/**
* Deserialize a ksecret collection from a KSecretFile.
@@ -457,6 +472,12 @@ private:
*/
bool serializeAuthenticated(const QByteArray &data, KSecretFile &file) const;
+ // flag which is set when the collection was changed and which
+ // denotes that the collection should be synced back to disk.
+ bool m_dirty;
+ // timer for syncing the collection on changes
+ QTimer m_syncTimer;
+
QString m_id;
QString m_label;
QDateTime m_created;
diff --git a/backend/ksecret/ksecretcollectionmanager.cpp \
b/backend/ksecret/ksecretcollectionmanager.cpp index ad627a8..650d507 100644
--- a/backend/ksecret/ksecretcollectionmanager.cpp
+++ b/backend/ksecret/ksecretcollectionmanager.cpp
@@ -23,14 +23,19 @@
#include "ksecretjobs.h"
#include <secrettool.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
#include <QtCore/QTimer>
#include <QtCore/QDir>
KSecretCollectionManager::KSecretCollectionManager(const QString &path, QObject \
*parent)
- : BackendCollectionManager(parent), m_watcher(QStringList(path))
+ : BackendCollectionManager(parent), m_watcher(this)
{
- connect(&m_watcher, SIGNAL(directoryChanged(QString)), \
SLOT(slotDirectoryChanged(QString))); + \
KGlobal::dirs()->addResourceType("ksecret", 0, path); + \
m_watcher.addDir(KGlobal::dirs()->saveLocation("ksecret")); + connect(&m_watcher, \
SIGNAL(dirty(QString)), SLOT(slotDirectoryChanged(QString))); + \
m_watcher.startScan();
// list directory contents to discover existing collections on startup
QTimer::singleShot(0, this, SLOT(slotStartupDiscovery()));
}
@@ -72,8 +77,7 @@ void KSecretCollectionManager::slotDirectoryChanged(const QString \
&path)
void KSecretCollectionManager::slotStartupDiscovery()
{
- Q_ASSERT(m_watcher.directories().count() == 1);
- slotDirectoryChanged(m_watcher.directories().at(0));
+ slotDirectoryChanged(KGlobal::dirs()->saveLocation("ksecret"));
}
void KSecretCollectionManager::createCollectionJobResult(QueuedJob *job)
diff --git a/backend/ksecret/ksecretcollectionmanager.h \
b/backend/ksecret/ksecretcollectionmanager.h index 50cc76e..ade1a31 100644
--- a/backend/ksecret/ksecretcollectionmanager.h
+++ b/backend/ksecret/ksecretcollectionmanager.h
@@ -23,8 +23,9 @@
#include "../backendcollectionmanager.h"
+#include <kdirwatch.h>
+
#include <QtCore/QMap>
-#include <QtCore/QFileSystemWatcher>
class KSecretCollection;
class KSecretCreateCollectionJob;
@@ -40,7 +41,6 @@ public:
/**
* Constructor
*
- * @param path Path to detect ksecret files in
* @param parent parent object
*/
explicit KSecretCollectionManager(const QString &path, QObject *parent = 0);
@@ -92,7 +92,7 @@ private:
friend class KSecretCreateCollectionJob;
// filesystem watcher to detect new/removed ksecret files
- QFileSystemWatcher m_watcher;
+ KDirWatch m_watcher;
// map of paths pointing to the respective collection objects
QMap<QString, KSecretCollection*> m_collections;
diff --git a/backend/tests/ksecrettest.cpp b/backend/tests/ksecrettest.cpp
index 7a17ebc..9397cba 100644
--- a/backend/tests/ksecrettest.cpp
+++ b/backend/tests/ksecrettest.cpp
@@ -26,6 +26,8 @@
#include <ui/nouimanager.h>
#include <peer.h>
+#include <kstandarddirs.h>
+
Q_DECLARE_METATYPE(BackendCollection*)
Q_DECLARE_METATYPE(BackendItem*)
@@ -36,7 +38,15 @@ void KSecretTest::initTestCase()
QCA::init();
BackendMaster *master = BackendMaster::instance();
master->setUiManager(new NoUiManager);
- m_manager = new KSecretCollectionManager("/tmp", master);
+ // use special test-directory for the .ksecret files
+ m_manager = new KSecretCollectionManager("share/apps/ksecretservice-test", \
master); + // remove all files in the resource directory so no previous
+ // collections are present when performing the test!
+ QDir dir = QDir(KGlobal::dirs()->saveLocation("ksecret"));
+ QStringList entries = dir.entryList(QStringList("*.ksecret"), QDir::Files);
+ Q_FOREACH(const QString &file, entries) {
+ QVERIFY(dir.remove(file));
+ }
master->addManager(m_manager);
}
@@ -72,7 +82,11 @@ void KSecretTest::testCreateCollectionAsync()
// TODO: check collection attributes (eg. timestamps)
- // TODO: check if the collection has been written to disk
+ // check that the collection has been written to disk
+ QStringList entries = QDir(KGlobal::dirs()->saveLocation("ksecret")).entryList(
+ QStringList("*.ksecret"), QDir::Files);
+ QCOMPARE(entries.count(), 1);
+ QCOMPARE(entries.at(0), createColl->collection()->id() + ".ksecret");
// remember the collection
m_collection = createColl->collection();
@@ -118,7 +132,7 @@ void KSecretTest::testUnlockCollectionAsync()
QTestEventLoop loop;
QVERIFY(loop.connect(unlockColl, SIGNAL(result(QueuedJob*)), SLOT(exitLoop())));
unlockColl->enqueue();
- if(!unlockColl->isFinished()) {
+ if (!unlockColl->isFinished()) {
loop.enterLoop(5);
}
@@ -139,26 +153,155 @@ void KSecretTest::testUnlockCollectionAsync()
void KSecretTest::testCreateItemAsync()
{
+ QMap<QString, QString> attr;
+ attr["mainattr"] = "haha";
+ QCA::SecureArray array(4, 'c');
+ ItemCreateInfo createInfo("testitem", attr, array, false, false, Peer());
+ CreateItemJob *createItem = m_collection->createCreateItemJob(createInfo);
+ QSignalSpy collectionSpy(m_collection, SIGNAL(itemCreated(BackendItem*)));
+ QTestEventLoop loop;
+ QVERIFY(loop.connect(createItem, SIGNAL(result(QueuedJob*)), SLOT(exitLoop())));
+ createItem->enqueue();
+ if (!createItem->isFinished()) {
+ loop.enterLoop(5);
+ }
+
+ QVERIFY(createItem->isFinished());
+ QCOMPARE(createItem->error(), NoError);
+ QVERIFY(!createItem->isDismissed());
+ QVERIFY(createItem->item() != 0);
+
+ // Verify signals
+ QCOMPARE(collectionSpy.count(), 1);
+ QCOMPARE(collectionSpy.takeFirst().at(0).value<BackendItem*>(), \
createItem->item()); +
+ // Check the item is present and alive
+ QCOMPARE(m_collection->items().value().size(), 1);
+ QCOMPARE(m_collection->items().value().first(), createItem->item());
+ QCOMPARE(m_collection->items().value().first()->secret().value().toByteArray(), \
QByteArray("cccc")); }
void KSecretTest::testReplaceItemAsync()
{
+ QMap<QString, QString> attr;
+ attr["mainattr"] = "haha";
+ QCA::SecureArray array(QByteArray("arealsecrete243"));
+ ItemCreateInfo createInfo("testitem2", attr, array, true, false, Peer());
+ CreateItemJob *createItem = m_collection->createCreateItemJob(createInfo);
+ QSignalSpy collectionSpy(m_collection, SIGNAL(itemChanged(BackendItem*)));
+ QTestEventLoop loop;
+ QVERIFY(loop.connect(createItem, SIGNAL(result(QueuedJob*)), SLOT(exitLoop())));
+ createItem->enqueue();
+ if (!createItem->isFinished()) {
+ loop.enterLoop(5);
+ }
+
+ QVERIFY(createItem->isFinished());
+ QCOMPARE(createItem->error(), NoError);
+ QVERIFY(!createItem->isDismissed());
+ QVERIFY(createItem->item() != 0);
+
+ // Verify signals
+ QCOMPARE(collectionSpy.count(), 1);
+ QCOMPARE(collectionSpy.takeFirst().at(0).value<BackendItem*>(), \
createItem->item()); +
+ // Check the item is present and alive
+ QCOMPARE(m_collection->items().value().size(), 1);
+ QCOMPARE(m_collection->items().value().first(), createItem->item());
+ QCOMPARE(m_collection->items().value().first()->secret().value().toByteArray(), \
QByteArray("arealsecrete243")); }
void KSecretTest::testDoNotReplaceItemAsync()
{
+ QMap<QString, QString> attr;
+ attr["mainattr"] = "haha";
+ QCA::SecureArray array(QByteArray("anothersecret"));
+ ItemCreateInfo createInfo("testitem3", attr, array, false, false, Peer());
+ CreateItemJob *createItem = m_collection->createCreateItemJob(createInfo);
+ QSignalSpy collectionSpy(m_collection, SIGNAL(itemChanged(BackendItem*)));
+ QTestEventLoop loop;
+ QVERIFY(loop.connect(createItem, SIGNAL(result(QueuedJob*)), SLOT(exitLoop())));
+ createItem->enqueue();
+ if (!createItem->isFinished()) {
+ loop.enterLoop(5);
+ }
+
+ QVERIFY(createItem->isFinished());
+ QCOMPARE(createItem->error(), ErrorAlreadyExists);
+ QVERIFY(!createItem->isDismissed());
+ QVERIFY(createItem->item() == 0);
+
+ // Verify signals
+ QCOMPARE(collectionSpy.count(), 0);
+
+ // Check the item is present and alive
+ QCOMPARE(m_collection->items().value().size(), 1);
+ QCOMPARE(m_collection->items().value().first()->secret().value().toByteArray(), \
QByteArray("arealsecrete243")); }
void KSecretTest::testDeleteItemAsync()
{
+ BackendItem *item = m_collection->items().value().first();
+ ItemDeleteInfo deleteInfo = ItemDeleteInfo(Peer());
+ DeleteItemJob *deleteItem = item->createDeleteJob(deleteInfo);
+ QSignalSpy collectionSpy(m_collection, SIGNAL(itemDeleted(BackendItem*)));
+ QTestEventLoop loop;
+ QVERIFY(loop.connect(deleteItem, SIGNAL(result(QueuedJob*)), SLOT(exitLoop())));
+ deleteItem->enqueue();
+ if (!deleteItem->isFinished()) {
+ loop.enterLoop(5);
+ }
+
+ QVERIFY(deleteItem->isFinished());
+ QCOMPARE(deleteItem->error(), NoError);
+ QVERIFY(deleteItem->result());
+ QVERIFY(!deleteItem->isDismissed());
+
+ // Verify signals
+ QCOMPARE(collectionSpy.count(), 1);
+ QCOMPARE(collectionSpy.takeFirst().at(0).value<BackendItem*>()->label().value(), \
QString("testitem2")); +
+ // check if the item is gone
+ QVERIFY(m_collection->items().value().isEmpty());
}
void KSecretTest::testDeleteCollectionAsync()
{
+ BackendMaster *master = BackendMaster::instance();
+ CollectionDeleteInfo deleteInfo = CollectionDeleteInfo(Peer());
+ DeleteCollectionJob *deleteCollection = \
m_collection->createDeleteJob(deleteInfo); + QSignalSpy managerSpy(m_manager, \
SIGNAL(collectionDeleted(BackendCollection*))); + QSignalSpy masterSpy(master, \
SIGNAL(collectionDeleted(BackendCollection*))); + QTestEventLoop loop;
+ QVERIFY(loop.connect(deleteCollection, SIGNAL(result(QueuedJob*)), \
SLOT(exitLoop()))); + deleteCollection->enqueue();
+ if (!deleteCollection->isFinished()) {
+ loop.enterLoop(5);
+ }
+
+ QVERIFY(deleteCollection->isFinished());
+ QCOMPARE(deleteCollection->error(), NoError);
+ QVERIFY(deleteCollection->result());
+ QVERIFY(!deleteCollection->isDismissed());
+
+ // Verify signals
+ QCOMPARE(managerSpy.count(), 1);
+ QCOMPARE(managerSpy.takeFirst().at(0).value<BackendCollection*>(), \
m_collection); + QCOMPARE(masterSpy.count(), 1);
+ QCOMPARE(masterSpy.takeFirst().at(0).value<BackendCollection*>(), m_collection);
+
+ // check the collection is dead
+ QVERIFY(master->collections().isEmpty());
+
+ // check that the collection has been removed from disk
+ QStringList entries = QDir(KGlobal::dirs()->saveLocation("ksecret")).entryList(
+ QStringList("*.ksecret"), QDir::Files);
+ QCOMPARE(entries.count(), 0);
}
void KSecretTest::cleanupTestCase()
{
+ // TODO: delete stuff so this can also be used for valgrind leak-checking
}
QTEST_MAIN(KSecretTest)
diff --git a/backend/tests/temporarytest.cpp b/backend/tests/temporarytest.cpp
index 2a895a3..ba5ac6c 100644
--- a/backend/tests/temporarytest.cpp
+++ b/backend/tests/temporarytest.cpp
@@ -363,7 +363,7 @@ void TemporaryTest::testDeleteItemAsync()
QCOMPARE(collectionSpy.count(), 1);
QCOMPARE(collectionSpy.takeFirst().at(0).value<BackendItem*>()->label().value(), \
QString("testitem2"));
- // Check the item is present and alive
+ // Check if item is gone
QVERIFY(collection->items().value().isEmpty());
}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic