diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index b9ca64b855273af51adbe515282afa01d43f9404..9e1ad58c1fd42aa2032145bfa14c3cd70759f19b 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -57,6 +57,12 @@ public:
 public slots:
     void fileOpenRequest(const QString&);
 
+private slots:
+#if defined(WITH_TESTS)
+    void testVcsManager_data();
+    void testVcsManager();
+#endif
+
 private:
     void parseArguments(const QStringList & arguments);
 
diff --git a/src/plugins/coreplugin/iversioncontrol.cpp b/src/plugins/coreplugin/iversioncontrol.cpp
index f903152a97ba58fb00e7baa840f646252e647ee3..80977eecfdcf33d1b22f72df3badab01589308a1 100644
--- a/src/plugins/coreplugin/iversioncontrol.cpp
+++ b/src/plugins/coreplugin/iversioncontrol.cpp
@@ -52,4 +52,58 @@ IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const QString
     return NoOpen;
 }
 
+} // namespace Core
+
+#if defined(WITH_TESTS)
+
+#include "vcsmanager.h"
+
+#include <QFileInfo>
+
+namespace Core {
+
+TestVersionControl::~TestVersionControl()
+{
+    VcsManager::instance()->clearVersionControlCache();
+}
+
+void TestVersionControl::setManagedDirectories(const QHash<QString, QString> &dirs)
+{
+    m_managedDirs = dirs;
+    m_dirCount = 0;
+    VcsManager::instance()->clearVersionControlCache();
+}
+
+void TestVersionControl::setManagedFiles(const QSet<QString> &files)
+{
+    m_managedFiles = files;
+    m_fileCount = 0;
+    VcsManager::instance()->clearVersionControlCache();
+}
+
+bool TestVersionControl::managesDirectory(const QString &filename, QString *topLevel) const
+{
+    ++m_dirCount;
+
+    if (m_managedDirs.contains(filename)) {
+        if (topLevel)
+            *topLevel = m_managedDirs.value(filename);
+        return true;
+    }
+    return false;
 }
+
+bool TestVersionControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    ++m_fileCount;
+
+    QFileInfo fi(workingDirectory + QLatin1Char('/') + fileName);
+    QString dir = fi.absolutePath();
+    if (!managesDirectory(dir, 0))
+        return false;
+    QString file = fi.absoluteFilePath();
+    return m_managedFiles.contains(file);
+}
+
+} // namespace Core
+#endif
diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h
index 839b3f1fcbf772b481746b8a7c89a077e467c40a..06dd31d1e75b6a21c8d4f195bc3084718b0f6011 100644
--- a/src/plugins/coreplugin/iversioncontrol.h
+++ b/src/plugins/coreplugin/iversioncontrol.h
@@ -188,4 +188,54 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IVersionControl::SettingsFlags)
 
 } // namespace Core
 
+#if defined(WITH_TESTS)
+
+#include <QSet>
+
+namespace Core {
+
+class CORE_EXPORT TestVersionControl : public IVersionControl
+{
+    Q_OBJECT
+public:
+    TestVersionControl(Core::Id id, const QString &name) :
+        m_id(id), m_displayName(name), m_dirCount(0), m_fileCount(0)
+    { }
+    ~TestVersionControl();
+
+    void setManagedDirectories(const QHash<QString, QString> &dirs);
+    void setManagedFiles(const QSet<QString> &files);
+
+    int dirCount() const { return m_dirCount; }
+    int fileCount() const { return m_fileCount; }
+
+    // IVersionControl interface
+    QString displayName() const { return m_displayName; }
+    Id id() const { return m_id; }
+    bool managesDirectory(const QString &filename, QString *topLevel) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
+    bool isConfigured() const { return true; }
+    bool supportsOperation(Operation) const { return false; }
+    bool vcsOpen(const QString &) { return false; }
+    bool vcsAdd(const QString &) { return false; }
+    bool vcsDelete(const QString &) { return false; }
+    bool vcsMove(const QString &, const QString &) { return false; }
+    bool vcsCreateRepository(const QString &) { return false; }
+    bool vcsCheckout(const QString &, const QByteArray &) { return false; }
+    QString vcsGetRepositoryURL(const QString &) { return QString(); }
+    bool vcsAnnotate(const QString &, int) { return false; }
+
+private:
+    Id m_id;
+    QString m_displayName;
+    QHash<QString, QString> m_managedDirs;
+    QSet<QString> m_managedFiles;
+    mutable int m_dirCount;
+    mutable int m_fileCount;
+};
+
+} // namespace Core
+#endif
+
+
 #endif // IVERSIONCONTROL_H
diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp
index 2d4789e9e53380e912c53ce4db2d69cbf3165d0b..552f21f826c7bb5b8a4a1966ccdf1b607129e01d 100644
--- a/src/plugins/coreplugin/vcsmanager.cpp
+++ b/src/plugins/coreplugin/vcsmanager.cpp
@@ -59,6 +59,10 @@ static inline VersionControlList allVersionControls()
     return ExtensionSystem::PluginManager::getObjects<IVersionControl>();
 }
 
+#if defined(WITH_TESTS)
+const char TEST_PREFIX[] = "/8E3A9BA0-0B97-40DF-AEC1-2BDF9FC9EDBE/";
+#endif
+
 // ---- VCSManagerPrivate:
 // Maintains a cache of top-level directory->version control.
 
@@ -274,6 +278,11 @@ IVersionControl* VcsManager::findVersionControlForDirectory(const QString &input
 
     // Register Vcs(s) with the cache
     QString tmpDir = QFileInfo(directory).canonicalFilePath();
+#if defined WITH_TESTS
+    // Force caching of test directories (even though they do not exist):
+    if (directory.startsWith(QLatin1String(TEST_PREFIX)))
+        tmpDir = directory;
+#endif
     // directory might refer to a historical directory which doesn't exist.
     // In this case, don't cache it.
     if (!tmpDir.isEmpty()) {
@@ -450,3 +459,181 @@ void VcsManager::configureVcs()
 }
 
 } // namespace Core
+
+#if defined(WITH_TESTS)
+
+#include <QtTest>
+
+#include "coreplugin.h"
+#include "iversioncontrol.h"
+
+#include <extensionsystem/pluginmanager.h>
+
+namespace Core {
+namespace Internal {
+
+const char ID_VCS_A[] = "A";
+const char ID_VCS_B[] = "B";
+
+typedef QHash<QString, QString> FileHash;
+
+template<class T>
+class ObjectPoolGuard
+{
+public:
+    ObjectPoolGuard(T *watch) : m_watched(watch)
+    {
+        ExtensionSystem::PluginManager::addObject(watch);
+    }
+
+    operator bool() { return m_watched; }
+    bool operator !() { return !m_watched; }
+    T &operator*() { return *m_watched; }
+    T *operator->() { return m_watched; }
+    T *value() { return m_watched; }
+
+    ~ObjectPoolGuard()
+    {
+        ExtensionSystem::PluginManager::removeObject(m_watched);
+        delete m_watched;
+    }
+
+private:
+    T *m_watched;
+};
+
+static FileHash makeHash(const QStringList &list)
+{
+    FileHash result;
+    foreach (const QString &i, list) {
+        QStringList parts = i.split(QLatin1Char(':'));
+        QTC_ASSERT(parts.count() == 2, continue);
+        result.insert(QString::fromLatin1(TEST_PREFIX) + parts.at(0),
+                      QString::fromLatin1(TEST_PREFIX) + parts.at(1));
+    }
+    return result;
+}
+
+static QString makeString(const QString &s)
+{
+    if (s.isEmpty())
+        return QString();
+    return QString::fromLatin1(TEST_PREFIX) + s;
+}
+
+void CorePlugin::testVcsManager_data()
+{
+    // avoid conflicts with real files and directories:
+
+    QTest::addColumn<QStringList>("dirsVcsA"); // <directory>:<toplevel>
+    QTest::addColumn<QStringList>("dirsVcsB"); // <directory>:<toplevel>
+    // <directory>:<toplevel>:<vcsid>:<- from cache, * from VCS>
+    QTest::addColumn<QStringList>("results");
+
+    QTest::newRow("A and B next to each other")
+            << (QStringList()
+                << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a")
+                << QLatin1String("a/2/5:a") << QLatin1String("a/2/5/6:a"))
+            << (QStringList()
+                << QLatin1String("b:b") << QLatin1String("b/3:b") << QLatin1String("b/4:b"))
+            << (QStringList()
+                << QLatin1String(":::-") // empty directory to look up
+                << QLatin1String("c:::*") // Neither in A nor B
+                << QLatin1String("a:a:A:*") // in A
+                << QLatin1String("b:b:B:*") // in B
+                << QLatin1String("b/3:b:B:*") // in B
+                << QLatin1String("b/4:b:B:*") // in B
+                << QLatin1String("a/1:a:A:*") // in A
+                << QLatin1String("a/2:a:A:*") // in A
+                << QLatin1String(":::-") // empty directory to look up
+                << QLatin1String("a/2/5/6:a:A:*") // in A
+                << QLatin1String("a/2/5:a:A:-") // in A (cached from before!)
+                // repeat: These need to come from the cache now:
+                << QLatin1String("c:::-") // Neither in A nor B
+                << QLatin1String("a:a:A:-") // in A
+                << QLatin1String("b:b:B:-") // in B
+                << QLatin1String("b/3:b:B:-") // in B
+                << QLatin1String("b/4:b:B:-") // in B
+                << QLatin1String("a/1:a:A:-") // in A
+                << QLatin1String("a/2:a:A:-") // in A
+                << QLatin1String("a/2/5/6:a:A:-") // in A
+                << QLatin1String("a/2/5:a:A:-") // in A
+                );
+    QTest::newRow("B in A")
+            << (QStringList()
+                << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a")
+                << QLatin1String("a/2/5:a") << QLatin1String("a/2/5/6:a"))
+            << (QStringList()
+                << QLatin1String("a/1/b:a/1/b") << QLatin1String("a/1/b/3:a/1/b")
+                << QLatin1String("a/1/b/4:a/1/b") << QLatin1String("a/1/b/3/5:a/1/b")
+                << QLatin1String("a/1/b/3/5/6:a/1/b"))
+            << (QStringList()
+                << QLatin1String("a:a:A:*") // in A
+                << QLatin1String("c:::*") // Neither in A nor B
+                << QLatin1String("a/3:::*") // Neither in A nor B
+                << QLatin1String("a/1/b/x:::*") // Neither in A nor B
+                << QLatin1String("a/1/b:a/1/b:B:*") // in B
+                << QLatin1String("a/1:a:A:*") // in A
+                << QLatin1String("a/1/b/../../2:a:A:*") // in A
+                );
+    QTest::newRow("A and B") // first one wins...
+            << (QStringList() << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a"))
+            << (QStringList() << QLatin1String("a:a") << QLatin1String("a/1:a") << QLatin1String("a/2:a"))
+            << (QStringList() << QLatin1String("a/2:a:A:*"));
+}
+
+void CorePlugin::testVcsManager()
+{
+    // setup:
+    ObjectPoolGuard<TestVersionControl> vcsA(new TestVersionControl(ID_VCS_A, QLatin1String("A")));
+    ObjectPoolGuard<TestVersionControl> vcsB(new TestVersionControl(ID_VCS_B, QLatin1String("B")));
+
+    // test:
+    QFETCH(QStringList, dirsVcsA);
+    QFETCH(QStringList, dirsVcsB);
+    QFETCH(QStringList, results);
+
+    vcsA->setManagedDirectories(makeHash(dirsVcsA));
+    vcsB->setManagedDirectories(makeHash(dirsVcsB));
+
+    QString realTopLevel = QLatin1String("ABC"); // Make sure this gets cleared if needed.
+
+    // From VCSes:
+    int expectedCount = 0;
+    foreach (const QString &result, results) {
+        // qDebug() << "Expecting:" << result;
+
+        QStringList split = result.split(QLatin1String(":"));
+        QCOMPARE(split.count(), 4);
+        QVERIFY(split.at(3) == QLatin1String("*") || split.at(3) == QLatin1String("-"));
+
+
+        const QString directory = split.at(0);
+        const QString topLevel = split.at(1);
+        const QString vcsId = split.at(2);
+        bool fromCache = split.at(3) == QLatin1String("-");
+
+        if (!fromCache && !directory.isEmpty())
+            ++expectedCount;
+
+        IVersionControl *vcs;
+        vcs = VcsManager::findVersionControlForDirectory(makeString(directory), &realTopLevel);
+        QCOMPARE(realTopLevel, makeString(topLevel));
+        if (vcs)
+            QCOMPARE(vcs->id().toString(), vcsId);
+        else
+            QCOMPARE(QString(), vcsId);
+        QCOMPARE(vcsA->dirCount(), expectedCount);
+        QCOMPARE(vcsA->fileCount(), 0);
+        QCOMPARE(vcsB->dirCount(), expectedCount);
+        QCOMPARE(vcsB->fileCount(), 0);
+    }
+
+    // teardown:
+    // handled by guards
+}
+
+} // namespace Internal
+} // namespace Core
+
+#endif