Rex Dieter adf30a
From 059d52845cbbc10e882764f64245c5995af4e741 Mon Sep 17 00:00:00 2001
Rex Dieter adf30a
From: =?UTF-8?q?Dan=20Vr=C3=A1til?= <dvratil@redhat.com>
Rex Dieter adf30a
Date: Mon, 8 Dec 2014 13:49:27 +0100
Rex Dieter adf30a
Subject: [PATCH 26/30] Avoid recursive collection listing in SearchHelper
Rex Dieter adf30a
Rex Dieter adf30a
The recursive listing generates one SQL query per collection, and since search
Rex Dieter adf30a
is invoked rather often (basically whenever you open an email in KMail), we get
Rex Dieter adf30a
lots of unnecessary queries. This new algorithm does one query to fetch all
Rex Dieter adf30a
folders with matching mime types, and only falls back to query the parent chain
Rex Dieter adf30a
when the requested ancestor is not 0 (root), or when the result collection is
Rex Dieter adf30a
not a direct descendant of the requested ancestor.
Rex Dieter adf30a
---
Rex Dieter adf30a
 server/src/handler/search.cpp        |   4 +-
Rex Dieter adf30a
 server/src/handler/searchhelper.cpp  | 111 ++++++++++++++----------
Rex Dieter adf30a
 server/src/handler/searchhelper.h    |   2 +-
Rex Dieter adf30a
 server/src/search/searchmanager.cpp  |   2 +-
Rex Dieter adf30a
 server/tests/unittest/CMakeLists.txt |   2 +
Rex Dieter adf30a
 server/tests/unittest/searchtest.cpp | 158 +++++++++++++++++++++++++++++++++++
Rex Dieter adf30a
 6 files changed, 230 insertions(+), 49 deletions(-)
Rex Dieter adf30a
 create mode 100644 server/tests/unittest/searchtest.cpp
Rex Dieter adf30a
Rex Dieter adf30a
diff --git a/server/src/handler/search.cpp b/server/src/handler/search.cpp
Rex Dieter adf30a
index 06d172f..00484ff 100644
Rex Dieter adf30a
--- a/server/src/handler/search.cpp
Rex Dieter adf30a
+++ b/server/src/handler/search.cpp
Rex Dieter adf30a
@@ -95,9 +95,7 @@ bool Search::parseStream()
Rex Dieter adf30a
     }
Rex Dieter adf30a
 
Rex Dieter adf30a
     if ( recursive ) {
Rex Dieter adf30a
-      Q_FOREACH ( qint64 collection, collectionIds ) {
Rex Dieter adf30a
-        collections << SearchHelper::listCollectionsRecursive( QVector<qint64>() << collection, mimeTypes );
Rex Dieter adf30a
-      }
Rex Dieter adf30a
+      collections << SearchHelper::matchSubcollectionsByMimeType( collectionIds, mimeTypes );
Rex Dieter adf30a
     } else {
Rex Dieter adf30a
       collections = collectionIds;
Rex Dieter adf30a
     }
Rex Dieter adf30a
diff --git a/server/src/handler/searchhelper.cpp b/server/src/handler/searchhelper.cpp
Rex Dieter adf30a
index aa6694d..1a06c0e 100644
Rex Dieter adf30a
--- a/server/src/handler/searchhelper.cpp
Rex Dieter adf30a
+++ b/server/src/handler/searchhelper.cpp
Rex Dieter adf30a
@@ -20,6 +20,7 @@
Rex Dieter adf30a
 
Rex Dieter adf30a
 #include "searchhelper.h"
Rex Dieter adf30a
 #include "storage/countquerybuilder.h"
Rex Dieter adf30a
+#include <storage/queryhelper.h>
Rex Dieter adf30a
 #include "entities.h"
Rex Dieter adf30a
 
Rex Dieter adf30a
 #include <libs/protocol_p.h>
Rex Dieter adf30a
@@ -89,55 +90,77 @@ QString SearchHelper::extractMimetype( const QList<QByteArray> &junks, int start
Rex Dieter adf30a
 }
Rex Dieter adf30a
 
Rex Dieter adf30a
 
Rex Dieter adf30a
-QVector<qint64> SearchHelper::listCollectionsRecursive( const QVector<qint64> &ancestors, const QStringList &mimeTypes )
Rex Dieter adf30a
+static qint64 parentCollectionId(qint64 collectionId)
Rex Dieter adf30a
 {
Rex Dieter adf30a
-  QVector<qint64> recursiveChildren;
Rex Dieter adf30a
-  Q_FOREACH ( qint64 ancestor, ancestors ) {
Rex Dieter adf30a
-    QVector<qint64> searchChildren;
Rex Dieter adf30a
-
Rex Dieter adf30a
-    { // Free the query before entering recursion to prevent too many opened connections
Rex Dieter adf30a
-
Rex Dieter adf30a
-      Query::Condition mimeTypeCondition;
Rex Dieter adf30a
-      mimeTypeCondition.addColumnCondition( CollectionMimeTypeRelation::rightFullColumnName(), Query::Equals, MimeType::idFullColumnName() );
Rex Dieter adf30a
-      // Exclude top-level collections and collections that cannot have items!
Rex Dieter adf30a
-      mimeTypeCondition.addValueCondition( MimeType::nameFullColumnName(), Query::NotEquals, QLatin1String( "inode/directory" ) );
Rex Dieter adf30a
-      if ( !mimeTypes.isEmpty() ) {
Rex Dieter adf30a
-        mimeTypeCondition.addValueCondition( MimeType::nameFullColumnName(), Query::In, mimeTypes );
Rex Dieter adf30a
-      }
Rex Dieter adf30a
+    QueryBuilder qb(Collection::tableName(), QueryBuilder::Select);
Rex Dieter adf30a
+    qb.addColumn(Collection::parentIdColumn());
Rex Dieter adf30a
+    qb.addValueCondition(Collection::idColumn(), Query::Equals, collectionId);
Rex Dieter adf30a
+    if (!qb.exec()) {
Rex Dieter adf30a
+        return -1;
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+    if (!qb.query().next()) {
Rex Dieter adf30a
+        return -1;
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+    return qb.query().value(0).toLongLong();
Rex Dieter adf30a
+}
Rex Dieter adf30a
 
Rex Dieter adf30a
-      CountQueryBuilder qb( Collection::tableName(), MimeType::nameFullColumnName(), CountQueryBuilder::All );
Rex Dieter adf30a
-      qb.addColumn( Collection::idFullColumnName() );
Rex Dieter adf30a
-      qb.addJoin( QueryBuilder::LeftJoin, CollectionMimeTypeRelation::tableName(), CollectionMimeTypeRelation::leftFullColumnName(), Collection::idFullColumnName() );
Rex Dieter adf30a
-      qb.addJoin( QueryBuilder::LeftJoin, MimeType::tableName(), mimeTypeCondition );
Rex Dieter adf30a
-      if ( ancestor == 0 ) {
Rex Dieter adf30a
-        qb.addValueCondition( Collection::parentIdFullColumnName(), Query::Is, QVariant() );
Rex Dieter adf30a
-      } else {
Rex Dieter adf30a
-        // Also include current ancestor's result, so that we know whether we should search in the ancestor too
Rex Dieter adf30a
-        Query::Condition idCond( Query::Or );
Rex Dieter adf30a
-        idCond.addValueCondition( Collection::parentIdFullColumnName(), Query::Equals, ancestor );
Rex Dieter adf30a
-        idCond.addValueCondition( Collection::idFullColumnName(), Query::Equals, ancestor );
Rex Dieter adf30a
-        qb.addCondition( idCond );
Rex Dieter adf30a
-      }
Rex Dieter adf30a
-      qb.addValueCondition( Collection::isVirtualFullColumnName(), Query::Equals, false );
Rex Dieter adf30a
-      qb.addGroupColumn( Collection::idFullColumnName() );
Rex Dieter adf30a
-      qb.exec();
Rex Dieter adf30a
-
Rex Dieter adf30a
-      QSqlQuery query = qb.query();
Rex Dieter adf30a
-      while ( query.next() ) {
Rex Dieter adf30a
-        const qint64 id = query.value( 1 ).toLongLong();
Rex Dieter adf30a
-        // Don't add ancestor into search children, we are resolving it right now
Rex Dieter adf30a
-        if ( id != ancestor ) {
Rex Dieter adf30a
-          searchChildren << id;
Rex Dieter adf30a
+
Rex Dieter adf30a
+QVector<qint64> SearchHelper::matchSubcollectionsByMimeType(const QVector<qint64> &ancestors, const QStringList &mimeTypes)
Rex Dieter adf30a
+{
Rex Dieter adf30a
+    // Get all collections with given mime types
Rex Dieter adf30a
+    QueryBuilder qb(Collection::tableName(), QueryBuilder::Select);
Rex Dieter adf30a
+    qb.setDistinct(true);
Rex Dieter adf30a
+    qb.addColumn(Collection::idFullColumnName());
Rex Dieter adf30a
+    qb.addColumn(Collection::parentIdFullColumnName());
Rex Dieter adf30a
+    qb.addJoin(QueryBuilder::LeftJoin, CollectionMimeTypeRelation::tableName(),
Rex Dieter adf30a
+               CollectionMimeTypeRelation::leftFullColumnName(), Collection::idFullColumnName());
Rex Dieter adf30a
+    qb.addJoin(QueryBuilder::LeftJoin, MimeType::tableName(),
Rex Dieter adf30a
+               CollectionMimeTypeRelation::rightFullColumnName(), MimeType::idFullColumnName());
Rex Dieter adf30a
+    Query::Condition cond(Query::Or);
Rex Dieter adf30a
+    Q_FOREACH (const QString &mt, mimeTypes) {
Rex Dieter adf30a
+        cond.addValueCondition(MimeType::nameFullColumnName(), Query::Equals, mt);
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+    qb.addCondition(cond);
Rex Dieter adf30a
+
Rex Dieter adf30a
+    if (!qb.exec()) {
Rex Dieter adf30a
+        qWarning() << "Failed to query search collections";
Rex Dieter adf30a
+        return QVector<qint64>();
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+    QMap<qint64 /* parentId */, QVector<qint64> /* collectionIds */> candidateCollections;
Rex Dieter adf30a
+    while (qb.query().next()) {
Rex Dieter adf30a
+        candidateCollections[qb.query().value(1).toLongLong()].append(qb.query().value(0).toLongLong());
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+    // If the ancestors list contains root, then return what we got, since everything
Rex Dieter adf30a
+    // is sub collection of root
Rex Dieter adf30a
+    QVector<qint64> results;
Rex Dieter adf30a
+    if (ancestors.contains(0)) {
Rex Dieter adf30a
+        Q_FOREACH (const QVector<qint64> &res, candidateCollections.values()) {
Rex Dieter adf30a
+            results += res;
Rex Dieter adf30a
         }
Rex Dieter adf30a
-        if ( query.value( 0 ).toInt() > 0 ) { // count( mimeTypeTable.name ) > 0
Rex Dieter adf30a
-          recursiveChildren << id;
Rex Dieter adf30a
+        return results;
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+    // Try to resolve direct descendants
Rex Dieter adf30a
+    Q_FOREACH (qint64 ancestor, ancestors) {
Rex Dieter adf30a
+        const QVector<qint64> cols = candidateCollections.take(ancestor);
Rex Dieter adf30a
+        if (!cols.isEmpty()) {
Rex Dieter adf30a
+            results += cols;
Rex Dieter adf30a
         }
Rex Dieter adf30a
-      }
Rex Dieter adf30a
     }
Rex Dieter adf30a
-    if ( !searchChildren.isEmpty() ) {
Rex Dieter adf30a
-      recursiveChildren << listCollectionsRecursive( searchChildren, mimeTypes );
Rex Dieter adf30a
+
Rex Dieter adf30a
+    for (auto iter = candidateCollections.begin(); iter != candidateCollections.end(); ++iter) {
Rex Dieter adf30a
+        // Traverse the collection chain up to root
Rex Dieter adf30a
+        qint64 parentId = iter.key();
Rex Dieter adf30a
+        while (!ancestors.contains(parentId) && parentId > 0) {
Rex Dieter adf30a
+            parentId = parentCollectionId(parentId);
Rex Dieter adf30a
+        }
Rex Dieter adf30a
+        // Ok, we found a requested ancestor in the parent chain
Rex Dieter adf30a
+        if (parentId > 0) {
Rex Dieter adf30a
+            results += iter.value();
Rex Dieter adf30a
+        }
Rex Dieter adf30a
     }
Rex Dieter adf30a
-  }
Rex Dieter adf30a
 
Rex Dieter adf30a
-  return recursiveChildren;
Rex Dieter adf30a
+    return results;
Rex Dieter adf30a
 }
Rex Dieter adf30a
diff --git a/server/src/handler/searchhelper.h b/server/src/handler/searchhelper.h
Rex Dieter adf30a
index a64bb61..1595501 100644
Rex Dieter adf30a
--- a/server/src/handler/searchhelper.h
Rex Dieter adf30a
+++ b/server/src/handler/searchhelper.h
Rex Dieter adf30a
@@ -33,7 +33,7 @@ class SearchHelper
Rex Dieter adf30a
   public:
Rex Dieter adf30a
     static QList<QByteArray> splitLine( const QByteArray &line );
Rex Dieter adf30a
     static QString extractMimetype( const QList<QByteArray> &junks, int start );
Rex Dieter adf30a
-    static QVector<qint64> listCollectionsRecursive( const QVector<qint64> &ancestors, const QStringList &mimeTypes );
Rex Dieter adf30a
+    static QVector<qint64> matchSubcollectionsByMimeType( const QVector<qint64> &ancestors, const QStringList &mimeTypes );
Rex Dieter adf30a
 };
Rex Dieter adf30a
 
Rex Dieter adf30a
 } // namespace Server
Rex Dieter adf30a
diff --git a/server/src/search/searchmanager.cpp b/server/src/search/searchmanager.cpp
Rex Dieter adf30a
index c821aa3..b940fcc 100644
Rex Dieter adf30a
--- a/server/src/search/searchmanager.cpp
Rex Dieter adf30a
+++ b/server/src/search/searchmanager.cpp
Rex Dieter adf30a
@@ -296,7 +296,7 @@ void SearchManager::updateSearchImpl( const Collection &collection, QWaitConditi
Rex Dieter adf30a
   }
Rex Dieter adf30a
 
Rex Dieter adf30a
   if ( recursive ) {
Rex Dieter adf30a
-    queryCollections = SearchHelper::listCollectionsRecursive( queryAncestors, queryMimeTypes );
Rex Dieter adf30a
+    queryCollections = SearchHelper::matchSubcollectionsByMimeType( queryAncestors, queryMimeTypes );
Rex Dieter adf30a
   } else {
Rex Dieter adf30a
     queryCollections = queryAncestors;
Rex Dieter adf30a
   }
Rex Dieter adf30a
diff --git a/server/tests/unittest/CMakeLists.txt b/server/tests/unittest/CMakeLists.txt
Rex Dieter adf30a
index b9744d9..acdc180 100644
Rex Dieter adf30a
--- a/server/tests/unittest/CMakeLists.txt
Rex Dieter adf30a
+++ b/server/tests/unittest/CMakeLists.txt
Rex Dieter adf30a
@@ -77,3 +77,5 @@ add_server_test(listhandlertest.cpp akonadiprivate)
Rex Dieter adf30a
 add_server_test(modifyhandlertest.cpp akonadiprivate)
Rex Dieter adf30a
 add_server_test(createhandlertest.cpp akonadiprivate)
Rex Dieter adf30a
 add_server_test(collectionreferencetest.cpp akonadiprivate)
Rex Dieter adf30a
+
Rex Dieter adf30a
+add_server_test(searchtest.cpp akonadiprivate)
Rex Dieter adf30a
\ No newline at end of file
Rex Dieter adf30a
diff --git a/server/tests/unittest/searchtest.cpp b/server/tests/unittest/searchtest.cpp
Rex Dieter adf30a
new file mode 100644
Rex Dieter adf30a
index 0000000..f523b09
Rex Dieter adf30a
--- /dev/null
Rex Dieter adf30a
+++ b/server/tests/unittest/searchtest.cpp
Rex Dieter adf30a
@@ -0,0 +1,158 @@
Rex Dieter adf30a
+/*
Rex Dieter adf30a
+ * Copyright (C) 2014  Daniel Vrátil <dvratil@redhat.com>
Rex Dieter adf30a
+ *
Rex Dieter adf30a
+ * This library is free software; you can redistribute it and/or
Rex Dieter adf30a
+ * modify it under the terms of the GNU Lesser General Public
Rex Dieter adf30a
+ * License as published by the Free Software Foundation; either
Rex Dieter adf30a
+ * version 2.1 of the License, or (at your option) any later version.
Rex Dieter adf30a
+ *
Rex Dieter adf30a
+ * This library is distributed in the hope that it will be useful,
Rex Dieter adf30a
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
Rex Dieter adf30a
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Rex Dieter adf30a
+ * Lesser General Public License for more details.
Rex Dieter adf30a
+ *
Rex Dieter adf30a
+ * You should have received a copy of the GNU Lesser General Public
Rex Dieter adf30a
+ * License along with this library; if not, write to the Free Software
Rex Dieter adf30a
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Rex Dieter adf30a
+ *
Rex Dieter adf30a
+ */
Rex Dieter adf30a
+
Rex Dieter adf30a
+#include "fakeakonadiserver.h"
Rex Dieter adf30a
+#include "searchhelper.h"
Rex Dieter adf30a
+#include "akdebug.h"
Rex Dieter adf30a
+#include "aktest.h"
Rex Dieter adf30a
+
Rex Dieter adf30a
+#include <entities.h>
Rex Dieter adf30a
+
Rex Dieter adf30a
+#include <QTest>
Rex Dieter adf30a
+
Rex Dieter adf30a
+using namespace Akonadi::Server;
Rex Dieter adf30a
+
Rex Dieter adf30a
+Q_DECLARE_METATYPE(QList<qint64>)
Rex Dieter adf30a
+Q_DECLARE_METATYPE(QList<QString>)
Rex Dieter adf30a
+
Rex Dieter adf30a
+
Rex Dieter adf30a
+class SearchTest : public QObject
Rex Dieter adf30a
+{
Rex Dieter adf30a
+    Q_OBJECT
Rex Dieter adf30a
+
Rex Dieter adf30a
+public:
Rex Dieter adf30a
+    SearchTest()
Rex Dieter adf30a
+        : QObject()
Rex Dieter adf30a
+    {
Rex Dieter adf30a
+        try {
Rex Dieter adf30a
+            FakeAkonadiServer::instance()->setPopulateDb(false);
Rex Dieter adf30a
+            FakeAkonadiServer::instance()->init();
Rex Dieter adf30a
+        } catch (const FakeAkonadiServerException &e) {
Rex Dieter adf30a
+            akError() << "Server exception: " << e.what();
Rex Dieter adf30a
+            akFatal() << "Fake Akonadi Server failed to start up, aborting test";
Rex Dieter adf30a
+        }
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+    ~SearchTest()
Rex Dieter adf30a
+    {
Rex Dieter adf30a
+        FakeAkonadiServer::instance()->quit();
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+    Collection createCollection(const Resource &res, const QString &name, const Collection &parent, const QStringList &mimetypes)
Rex Dieter adf30a
+    {
Rex Dieter adf30a
+        Collection col;
Rex Dieter adf30a
+        col.setName(name);
Rex Dieter adf30a
+        col.setResource(res);
Rex Dieter adf30a
+        col.setParentId(parent.isValid() ? parent.id() : 0);
Rex Dieter adf30a
+        col.insert();
Rex Dieter adf30a
+        Q_FOREACH (const QString &mimeType, mimetypes) {
Rex Dieter adf30a
+            MimeType mt = MimeType::retrieveByName(mimeType);
Rex Dieter adf30a
+            if (!mt.isValid()) {
Rex Dieter adf30a
+                mt = MimeType(mimeType);
Rex Dieter adf30a
+                mt.insert();
Rex Dieter adf30a
+            }
Rex Dieter adf30a
+            col.addMimeType(mt);
Rex Dieter adf30a
+        }
Rex Dieter adf30a
+        return col;
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+private Q_SLOTS:
Rex Dieter adf30a
+    void testSearchHelperCollectionListing_data()
Rex Dieter adf30a
+    {
Rex Dieter adf30a
+        /*
Rex Dieter adf30a
+        Fake Resource
Rex Dieter adf30a
+          |- Col 1 (inode/directory)
Rex Dieter adf30a
+          |  |- Col 2 (inode/direcotry, application/octet-stream)
Rex Dieter adf30a
+          |  |  |- Col 3(application/octet-stream)
Rex Dieter adf30a
+          |  |- Col 4 (text/plain)
Rex Dieter adf30a
+          |- Col 5 (inode/directory, text/plain)
Rex Dieter adf30a
+             |- Col 6 (inode/directory, application/octet-stream)
Rex Dieter adf30a
+             |- Col 7 (inode/directory, text/plain)
Rex Dieter adf30a
+                 |- Col 8 (inode/directory, application/octet-stream)
Rex Dieter adf30a
+                    |- Col 9 (unique/mime-type)
Rex Dieter adf30a
+        */
Rex Dieter adf30a
+
Rex Dieter adf30a
+        Resource res(QLatin1String("Test Resource"), false);
Rex Dieter adf30a
+        res.insert();
Rex Dieter adf30a
+
Rex Dieter adf30a
+        Collection col1 = createCollection(res, QLatin1String("Col 1"), Collection(),
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("inode/directory"));
Rex Dieter adf30a
+        Collection col2 = createCollection(res, QLatin1String("Col 2"), col1,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("inode/directory")
Rex Dieter adf30a
+                                                         << QLatin1String("application/octet-stream"));
Rex Dieter adf30a
+        Collection col3 = createCollection(res, QLatin1String("Col 3"), col2,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("application/octet-stream"));
Rex Dieter adf30a
+        Collection col4 = createCollection(res, QLatin1String("Col 4"), col2,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("text/plain"));
Rex Dieter adf30a
+        Collection col5 = createCollection(res, QLatin1String("Col 5"), Collection(),
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("inode/directory")
Rex Dieter adf30a
+                                                         << QLatin1String("text/plain"));
Rex Dieter adf30a
+        Collection col6 = createCollection(res, QLatin1String("Col 6"), col5,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("inode/directory")
Rex Dieter adf30a
+                                                         << QLatin1String("application/octet-stream"));
Rex Dieter adf30a
+        Collection col7 = createCollection(res, QLatin1String("Col 7"), col5,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("inode/directory")
Rex Dieter adf30a
+                                                         << QLatin1String("text/plain"));
Rex Dieter adf30a
+        Collection col8 = createCollection(res, QLatin1String("Col 8"), col7,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("text/directory")
Rex Dieter adf30a
+                                                         << QLatin1String("application/octet-stream"));
Rex Dieter adf30a
+        Collection col9 = createCollection(res, QLatin1String("Col 9"), col8,
Rex Dieter adf30a
+                                           QStringList() << QLatin1String("unique/mime-type"));
Rex Dieter adf30a
+
Rex Dieter adf30a
+        QTest::addColumn<QVector<qint64>>("ancestors");
Rex Dieter adf30a
+        QTest::addColumn<QStringList>("mimetypes");
Rex Dieter adf30a
+        QTest::addColumn<QVector<qint64>>("expectedResults");
Rex Dieter adf30a
+
Rex Dieter adf30a
+        QTest::newRow("") << QVector<qint64>({ 0 })
Rex Dieter adf30a
+                          << QStringList({ QLatin1String("text/plain") })
Rex Dieter adf30a
+                          << QVector<qint64>({ col4.id(), col5.id(), col7.id() });
Rex Dieter adf30a
+        QTest::newRow("") << QVector<qint64>({ 0 })
Rex Dieter adf30a
+                          << QStringList({ QLatin1String("application/octet-stream") })
Rex Dieter adf30a
+                          << QVector<qint64>({ col2.id(), col3.id(), col6.id(), col8.id() });
Rex Dieter adf30a
+        QTest::newRow("") << QVector<qint64>({ col1.id() })
Rex Dieter adf30a
+                          << QStringList({ QLatin1String("text/plain") })
Rex Dieter adf30a
+                          << QVector<qint64>({ col4.id() });
Rex Dieter adf30a
+        QTest::newRow("") << QVector<qint64>({ col1.id() })
Rex Dieter adf30a
+                          << QStringList({ QLatin1String("unique/mime-type") })
Rex Dieter adf30a
+                          << QVector<qint64>();
Rex Dieter adf30a
+        QTest::newRow("") << QVector<qint64>({ col2.id(), col7.id() })
Rex Dieter adf30a
+                          << QStringList({ QLatin1String("application/octet-stream") })
Rex Dieter adf30a
+                          << QVector<qint64>({ col3.id(), col8.id() });
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+    void testSearchHelperCollectionListing()
Rex Dieter adf30a
+    {
Rex Dieter adf30a
+        QFETCH(QVector<qint64>, ancestors);
Rex Dieter adf30a
+        QFETCH(QStringList, mimetypes);
Rex Dieter adf30a
+        QFETCH(QVector<qint64>, expectedResults);
Rex Dieter adf30a
+
Rex Dieter adf30a
+        QVector<qint64> results = SearchHelper::matchSubcollectionsByMimeType(ancestors, mimetypes);
Rex Dieter adf30a
+
Rex Dieter adf30a
+        qSort(expectedResults);
Rex Dieter adf30a
+        qSort(results);
Rex Dieter adf30a
+
Rex Dieter adf30a
+        QCOMPARE(results.size(), expectedResults.size());
Rex Dieter adf30a
+        QCOMPARE(results, expectedResults);
Rex Dieter adf30a
+    }
Rex Dieter adf30a
+
Rex Dieter adf30a
+};
Rex Dieter adf30a
+
Rex Dieter adf30a
+AKTEST_FAKESERVER_MAIN(SearchTest)
Rex Dieter adf30a
+
Rex Dieter adf30a
+#include "searchtest.moc"
Rex Dieter adf30a
-- 
Rex Dieter adf30a
2.1.0
Rex Dieter adf30a