Commit 1fedf941 authored by Igor Kushnir's avatar Igor Kushnir
Browse files

Test and benchmark LanguageController::languagesForUrl()

LanguageController's initialize() and cleanup() member functions were
called once each by Core and didn't reset LanguageController's state.
This commit expands the definition of initialize() to make these two
member functions work for the added tests. Most of the added code
belongs in cleanup(), but moving it there seems a bit risky: what if
this data is used at KDevelop exit?
parent 16cc7b87
......@@ -125,14 +125,24 @@ void LanguageController::initialize()
{
Q_D(LanguageController);
d->activeLanguages = {};
d->languages = {};
d->languageCache = {};
d->mimeTypeCache = {};
d->backgroundParser->loadSettings();
delete d->staticAssistantsManager;
d->staticAssistantsManager = new StaticAssistantsManager(this);
d->m_cleanedUp = false;
// make sure the DUChain is setup before we try to access it from different threads at the same time
DUChain::self();
connect(Core::self()->documentController(), &IDocumentController::documentActivated,
this, [this] (IDocument* document) { Q_D(LanguageController); d->documentActivated(document); });
this, [this] (IDocument* document) { Q_D(LanguageController); d->documentActivated(document); },
Qt::UniqueConnection);
}
void LanguageController::cleanup()
......
......@@ -73,3 +73,19 @@ ecm_add_test(test_problemmodel.cpp
ecm_add_test(test_checkerstatus.cpp
LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell)
add_library(languagecontrollertestbase STATIC
languagecontrollertestbase.cpp)
target_link_libraries(languagecontrollertestbase PUBLIC
Qt5::Test KDev::Tests)
ecm_add_test(test_languagecontroller.cpp LINK_LIBRARIES languagecontrollertestbase)
if(NOT WIN32)
find_package(Threads REQUIRED)
target_link_libraries(test_languagecontroller Threads::Threads)
endif()
if(BUILD_BENCHMARKS)
ecm_add_test(bench_languagecontroller.cpp LINK_LIBRARIES languagecontrollertestbase)
set_tests_properties(bench_languagecontroller PROPERTIES TIMEOUT 30)
endif()
/*
SPDX-FileCopyrightText: 2021 Igor Kushnir <igorkuo@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "bench_languagecontroller.h"
#include <ilanguagecontroller.h>
#include <language/interfaces/ilanguagesupport.h>
#include <QString>
#include <QTest>
#include <QUrl>
void BenchLanguageController::benchLanguagesForUrlNoCache()
{
benchmarkLanguagesForUrl();
}
void BenchLanguageController::benchLanguagesForUrlNoCache_data()
{
matchingLanguagesForUrlTestData();
}
void BenchLanguageController::benchLanguagesForUrlFilledCache()
{
fillLanguageControllerMimeTypeCache();
benchmarkLanguagesForUrl();
}
void BenchLanguageController::benchLanguagesForUrlFilledCache_data()
{
matchingLanguagesForUrlTestData();
}
void BenchLanguageController::benchLanguagesForUrlNoMatchNoCache()
{
benchmarkLanguagesForUrlNoMatch();
}
void BenchLanguageController::benchLanguagesForUrlNoMatchNoCache_data()
{
nonmatchingLanguagesForUrlTestData();
}
void BenchLanguageController::benchLanguagesForUrlNoMatchFilledCache()
{
fillLanguageControllerMimeTypeCache();
benchmarkLanguagesForUrlNoMatch();
}
void BenchLanguageController::benchLanguagesForUrlNoMatchFilledCache_data()
{
nonmatchingLanguagesForUrlTestData();
}
void BenchLanguageController::benchmarkLanguagesForUrl()
{
QFETCH(QUrl, url);
QFETCH(QString, languageName);
// Warm up and check correctness.
const auto languages = m_subject->languagesForUrl(url);
QCOMPARE(languages.size(), 1);
QCOMPARE(languages.back()->name(), languageName);
QBENCHMARK {
m_subject->languagesForUrl(url);
}
}
void BenchLanguageController::benchmarkLanguagesForUrlNoMatch()
{
QFETCH(QUrl, url);
// Warm up and check correctness.
const auto languages = m_subject->languagesForUrl(url);
QCOMPARE(languages.size(), 0);
QBENCHMARK {
m_subject->languagesForUrl(url);
}
}
QTEST_MAIN(BenchLanguageController)
/*
SPDX-FileCopyrightText: 2021 Igor Kushnir <igorkuo@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDEVPLATFORM_BENCH_LANGUAGECONTROLLER_H
#define KDEVPLATFORM_BENCH_LANGUAGECONTROLLER_H
#include "languagecontrollertestbase.h"
class BenchLanguageController : public LanguageControllerTestBase
{
Q_OBJECT
private Q_SLOTS:
void benchLanguagesForUrlNoCache();
void benchLanguagesForUrlNoCache_data();
void benchLanguagesForUrlFilledCache();
void benchLanguagesForUrlFilledCache_data();
void benchLanguagesForUrlNoMatchNoCache();
void benchLanguagesForUrlNoMatchNoCache_data();
void benchLanguagesForUrlNoMatchFilledCache();
void benchLanguagesForUrlNoMatchFilledCache_data();
private:
void benchmarkLanguagesForUrl();
void benchmarkLanguagesForUrlNoMatch();
};
#endif // KDEVPLATFORM_BENCH_LANGUAGECONTROLLER_H
diff --git a/plugins/patchreview/kdevpatchreview.json b/plugins/patchreview/kdevpatchreview.json
/*
SPDX-FileCopyrightText: 2021 Igor Kushnir <igorkuo@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "languagecontrollertestbase.h"
#include "testfilepaths.h"
#include <ilanguagecontroller.h>
#include <language/backgroundparser/backgroundparser.h>
#include <languagecontroller.h>
#include <shell/core.h>
#include <tests/autotestshell.h>
#include <tests/testcore.h>
#include <tests/testhelpers.h>
#include <QByteArray>
#include <QFileInfo>
#include <QString>
#include <QTest>
#include <QUrl>
using namespace KDevelop;
namespace {
QUrl testUrl(const QString& filename)
{
return QUrl::fromLocalFile(filename);
}
QUrl existentTestUrl(const QString& filename)
{
const QString filePath = TEST_FILES_DIR "/" + filename;
QVERIFY_RETURN(QFileInfo::exists(filePath), QUrl{});
return QUrl::fromLocalFile(filePath);
}
}
LanguageControllerTestBase::LanguageControllerTestBase(QObject* parent)
: QObject(parent)
, m_differentLanguagesUrls{
{testUrl("plus.cc"), "clang"},
{testUrl("project.cmake"), "CMake"},
{testUrl("patch.diff"), "diff"},
{testUrl("gui.qml"), "qml/js"},
}
{
}
void LanguageControllerTestBase::initTestCase()
{
AutoTestShell::init({"kdevclangsupport", "kdevpatchreview", "kdevqmljs",
"KDevCMakeManager", "KDevCMakeBuilder", "KDevMakeBuilder", "KDevStandardOutputView"});
TestCore::initialize();
m_subject = Core::self()->languageController();
}
void LanguageControllerTestBase::init()
{
Core::self()->languageControllerInternal()->initialize();
m_subject->backgroundParser()->suspend();
}
void LanguageControllerTestBase::cleanup()
{
Core::self()->languageControllerInternal()->cleanup();
}
void LanguageControllerTestBase::cleanupTestCase()
{
TestCore::shutdown();
}
void LanguageControllerTestBase::fillLanguageControllerMimeTypeCache() const
{
for (const auto& url : m_differentLanguagesUrls) {
const auto languages = m_subject->languagesForUrl(url.url);
QCOMPARE(languages.size(), 1);
QCOMPARE(languages.back()->name(), url.languageName);
}
}
void LanguageControllerTestBase::matchingLanguagesForUrlInBackgroundThreadTestData() const
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QString>("languageName");
QTest::newRow("CMakeLists") << testUrl("CMakeLists.txt") << "CMake";
QTest::newRow("cmakelists wrong case") << testUrl("cmakelists.TXT") << "CMake";
QTest::newRow("lower-case") << testUrl("x.cpp") << "clang";
QTest::newRow("upper-case") << testUrl("Y.CPP") << "clang";
QTest::newRow("mixed-case") << testUrl("aBc.CpP") << "clang";
QTest::newRow(".C") << testUrl("ambiguous.C") << "clang";
QTest::newRow(".cl") << testUrl("Open.cl") << "clang";
QTest::newRow("existent C with extension") << existentTestUrl("t.c") << "clang";
for (const auto& url : m_differentLanguagesUrls) {
const auto filename = url.url.fileName();
const auto extension = filename.mid(filename.lastIndexOf('.'));
QTest::newRow(extension.toUtf8().constData()) << url.url << url.languageName;
}
}
void LanguageControllerTestBase::matchingLanguagesForUrlTestData() const
{
matchingLanguagesForUrlInBackgroundThreadTestData();
QTest::newRow("existent C w/o extension") << existentTestUrl("X") << "clang";
QTest::newRow("existent patch w/o extension") << existentTestUrl("y") << "diff";
}
void LanguageControllerTestBase::nonmatchingLanguagesForUrlTestData()
{
QTest::addColumn<QUrl>("url");
QTest::newRow("empty") << testUrl(QString());
QTest::newRow("archive") << testUrl("a.tar.gz");
QTest::newRow("OpenDocument Text") << testUrl("b.odt");
QTest::newRow("existent archive with extension") << existentTestUrl("N.tar.gz");
QTest::newRow("existent archive w/o extension") << existentTestUrl("z");
}
/*
SPDX-FileCopyrightText: 2021 Igor Kushnir <igorkuo@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDEVPLATFORM_LANGUAGECONTROLLERTESTBASE_H
#define KDEVPLATFORM_LANGUAGECONTROLLERTESTBASE_H
#include <QObject>
#include <QString>
#include <QUrl>
#include <vector>
namespace KDevelop {
class ILanguageController;
}
class LanguageControllerTestBase : public QObject
{
Q_OBJECT
protected:
explicit LanguageControllerTestBase(QObject* parent = nullptr);
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void cleanupTestCase();
protected:
void fillLanguageControllerMimeTypeCache() const;
void matchingLanguagesForUrlInBackgroundThreadTestData() const;
void matchingLanguagesForUrlTestData() const;
static void nonmatchingLanguagesForUrlTestData();
KDevelop::ILanguageController* m_subject = nullptr;
private:
struct UrlEntry
{
QUrl url;
QString languageName;
};
const std::vector<UrlEntry> m_differentLanguagesUrls;
};
#endif // KDEVPLATFORM_LANGUAGECONTROLLERTESTBASE_H
/*
SPDX-FileCopyrightText: 2021 Igor Kushnir <igorkuo@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "test_languagecontroller.h"
#include <ilanguagecontroller.h>
#include <language/interfaces/ilanguagesupport.h>
#include <tests/testhelpers.h>
#include <QString>
#include <QTest>
#include <QUrl>
#include <future>
void TestLanguageController::testLanguagesForUrlInTheMainThread()
{
QFETCH(QUrl, url);
QFETCH(QString, languageName);
const auto languages = m_subject->languagesForUrl(url);
QCOMPARE(languages.size(), 1);
QCOMPARE(languages.back()->name(), languageName);
}
void TestLanguageController::testLanguagesForUrlInTheMainThread_data()
{
matchingLanguagesForUrlTestData();
}
void TestLanguageController::testLanguagesForUrlWithCache()
{
QFETCH(QUrl, url);
QFETCH(QString, languageName);
// Add the MIME type to LanguageController's cache for use in the background thread.
const auto languages = m_subject->languagesForUrl(url);
QCOMPARE(languages.size(), 1);
QCOMPARE(languages.back()->name(), languageName);
auto future = std::async(std::launch::async, [&] {
const auto languages = m_subject->languagesForUrl(url);
QCOMPARE_RETURN(languages.size(), 1, false);
QCOMPARE_RETURN(languages.back()->name(), languageName, false);
return true;
});
QVERIFY(future.get());
}
void TestLanguageController::testLanguagesForUrlWithCache_data()
{
matchingLanguagesForUrlInBackgroundThreadTestData();
}
void TestLanguageController::testLanguagesForUrlNoCache()
{
QFETCH(QUrl, url);
auto future = std::async(std::launch::async, [&] {
const auto languages = m_subject->languagesForUrl(url);
// When languagesForUrl() is called from a non-main thread, it cannot
// determine languages for a MIME type without a cache.
QVERIFY_RETURN(languages.empty(), false);
return true;
});
QVERIFY(future.get());
}
void TestLanguageController::testLanguagesForUrlNoCache_data()
{
matchingLanguagesForUrlTestData();
}
void TestLanguageController::testLanguagesForUrlNoMatch()
{
QFETCH(QUrl, url);
const auto languages = m_subject->languagesForUrl(url);
QVERIFY(languages.empty());
}
void TestLanguageController::testLanguagesForUrlNoMatch_data()
{
nonmatchingLanguagesForUrlTestData();
}
QTEST_MAIN(TestLanguageController)
/*
SPDX-FileCopyrightText: 2021 Igor Kushnir <igorkuo@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KDEVPLATFORM_TEST_LANGUAGECONTROLLER_H
#define KDEVPLATFORM_TEST_LANGUAGECONTROLLER_H
#include "languagecontrollertestbase.h"
class TestLanguageController : public LanguageControllerTestBase
{
Q_OBJECT
private Q_SLOTS:
void testLanguagesForUrlInTheMainThread();
void testLanguagesForUrlInTheMainThread_data();
void testLanguagesForUrlWithCache();
void testLanguagesForUrlWithCache_data();
void testLanguagesForUrlNoCache();
void testLanguagesForUrlNoCache_data();
void testLanguagesForUrlNoMatch();
void testLanguagesForUrlNoMatch_data();
};
#endif // KDEVPLATFORM_TEST_LANGUAGECONTROLLER_H
#define TEST_PLUGIN_DIR "${TEST_PLUGIN_DIR}"
#define TEST_FILES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/files"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment