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