[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