forked from qt-creator/qt-creator
CppEditor: Tweak test infrastructure
... so we can re-use the "follow symbol" test cases with clangd. Call Creator like this (clangd needs to be version 12 or later): $ QTC_CLANGD=<path to clangd> qtcreator -test 'CppEditor,*Follow*,*Switch*' -test 'ClangCodeModel,*dummy*' During testing, some invalid code in the test cases was uncovered and fixed. Change-Id: I9dc650fdba2a27600e6a550420ee873f6fb31d23 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -36,6 +36,7 @@
|
|||||||
#include <cpptools/cppvirtualfunctionassistprovider.h>
|
#include <cpptools/cppvirtualfunctionassistprovider.h>
|
||||||
#include <cpptools/cppvirtualfunctionproposalitem.h>
|
#include <cpptools/cppvirtualfunctionproposalitem.h>
|
||||||
#include <languageclient/languageclientinterface.h>
|
#include <languageclient/languageclientinterface.h>
|
||||||
|
#include <projectexplorer/project.h>
|
||||||
#include <projectexplorer/projecttree.h>
|
#include <projectexplorer/projecttree.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <texteditor/basefilefind.h>
|
#include <texteditor/basefilefind.h>
|
||||||
@@ -513,11 +514,14 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
|||||||
setLocatorsEnabled(false);
|
setLocatorsEnabled(false);
|
||||||
setProgressTitleForToken(indexingToken(), tr("Parsing C/C++ Files (clangd)"));
|
setProgressTitleForToken(indexingToken(), tr("Parsing C/C++ Files (clangd)"));
|
||||||
setCurrentProject(project);
|
setCurrentProject(project);
|
||||||
connect(this, &Client::workDone, this, [this](const ProgressToken &token) {
|
connect(this, &Client::workDone, this, [this, project](const ProgressToken &token) {
|
||||||
const QString * const val = Utils::get_if<QString>(&token);
|
const QString * const val = Utils::get_if<QString>(&token);
|
||||||
if (val && *val == indexingToken()) {
|
if (val && *val == indexingToken()) {
|
||||||
d->isFullyIndexed = true;
|
d->isFullyIndexed = true;
|
||||||
emit indexingFinished();
|
emit indexingFinished();
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
emit project->indexingFinished("Indexer.Clangd");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -848,6 +852,8 @@ void ClangdClient::followSymbol(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qCDebug(clangdLog) << "follow symbol requested" << document->filePath()
|
||||||
|
<< cursor.blockNumber() << cursor.positionInBlock();
|
||||||
d->followSymbolData.emplace(this, ++d->nextFollowSymbolId, cursor, editorWidget,
|
d->followSymbolData.emplace(this, ++d->nextFollowSymbolId, cursor, editorWidget,
|
||||||
DocumentUri::fromFilePath(document->filePath()),
|
DocumentUri::fromFilePath(document->filePath()),
|
||||||
std::move(callback), openInSplit);
|
std::move(callback), openInSplit);
|
||||||
@@ -856,6 +862,7 @@ void ClangdClient::followSymbol(
|
|||||||
// AST node corresponding to the cursor position, so we can find out whether
|
// AST node corresponding to the cursor position, so we can find out whether
|
||||||
// we have to look for overrides.
|
// we have to look for overrides.
|
||||||
const auto gotoDefCallback = [this, id = d->followSymbolData->id](const Utils::Link &link) {
|
const auto gotoDefCallback = [this, id = d->followSymbolData->id](const Utils::Link &link) {
|
||||||
|
qCDebug(clangdLog) << "received go to definition response";
|
||||||
if (!link.hasValidTarget()) {
|
if (!link.hasValidTarget()) {
|
||||||
d->followSymbolData.reset();
|
d->followSymbolData.reset();
|
||||||
return;
|
return;
|
||||||
@@ -872,6 +879,7 @@ void ClangdClient::followSymbol(
|
|||||||
Range(cursor)));
|
Range(cursor)));
|
||||||
astRequest.setResponseCallback([this, id = d->followSymbolData->id](
|
astRequest.setResponseCallback([this, id = d->followSymbolData->id](
|
||||||
const AstRequest::Response &response) {
|
const AstRequest::Response &response) {
|
||||||
|
qCDebug(clangdLog) << "received ast response";
|
||||||
if (!d->followSymbolData || d->followSymbolData->id != id)
|
if (!d->followSymbolData || d->followSymbolData->id != id)
|
||||||
return;
|
return;
|
||||||
const auto result = response.result();
|
const auto result = response.result();
|
||||||
@@ -891,6 +899,8 @@ void ClangdClient::Private::handleGotoDefinitionResult()
|
|||||||
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
||||||
QTC_ASSERT(followSymbolData->cursorNode.isValid(), return);
|
QTC_ASSERT(followSymbolData->cursorNode.isValid(), return);
|
||||||
|
|
||||||
|
qCDebug(clangdLog) << "handling go to definition result";
|
||||||
|
|
||||||
// No dis-ambiguation necessary. Call back with the link and finish.
|
// No dis-ambiguation necessary. Call back with the link and finish.
|
||||||
if (!followSymbolData->cursorNode.isMemberFunctionCall()
|
if (!followSymbolData->cursorNode.isMemberFunctionCall()
|
||||||
&& !followSymbolData->cursorNode.isPureVirtualDeclaration()) {
|
&& !followSymbolData->cursorNode.isPureVirtualDeclaration()) {
|
||||||
|
@@ -49,6 +49,7 @@
|
|||||||
|
|
||||||
using namespace CPlusPlus;
|
using namespace CPlusPlus;
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
using namespace CppTools::Tests;
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace ClangCodeModel {
|
namespace ClangCodeModel {
|
||||||
@@ -67,20 +68,6 @@ void ClangdTests::initTestCase()
|
|||||||
settings->setUseClangd(true);
|
settings->setUseClangd(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Signal> static bool waitForSignalOrTimeout(
|
|
||||||
const typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal)
|
|
||||||
{
|
|
||||||
QTimer timer;
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
timer.setInterval(timeOutInMs());
|
|
||||||
QEventLoop loop;
|
|
||||||
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
|
||||||
QObject::connect(sender, signal, &loop, &QEventLoop::quit);
|
|
||||||
timer.start();
|
|
||||||
loop.exec();
|
|
||||||
return timer.isActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The main point here is to test our access type categorization.
|
// The main point here is to test our access type categorization.
|
||||||
// We do not try to stress-test clangd's "Find References" functionality; such tests belong
|
// We do not try to stress-test clangd's "Find References" functionality; such tests belong
|
||||||
// into LLVM.
|
// into LLVM.
|
||||||
@@ -110,7 +97,7 @@ void ClangdTests::testFindReferences()
|
|||||||
ClangdClient *client = modelManagerSupport->clientForProject(openProjectResult.project());
|
ClangdClient *client = modelManagerSupport->clientForProject(openProjectResult.project());
|
||||||
if (!client) {
|
if (!client) {
|
||||||
QVERIFY(waitForSignalOrTimeout(modelManagerSupport,
|
QVERIFY(waitForSignalOrTimeout(modelManagerSupport,
|
||||||
&ClangModelManagerSupport::createdClient));
|
&ClangModelManagerSupport::createdClient, timeOutInMs()));
|
||||||
client = modelManagerSupport->clientForProject(openProjectResult.project());
|
client = modelManagerSupport->clientForProject(openProjectResult.project());
|
||||||
}
|
}
|
||||||
QVERIFY(client);
|
QVERIFY(client);
|
||||||
@@ -119,7 +106,7 @@ void ClangdTests::testFindReferences()
|
|||||||
// Wait until the client is fully initialized, i.e. it's completed the handshake
|
// Wait until the client is fully initialized, i.e. it's completed the handshake
|
||||||
// with the server.
|
// with the server.
|
||||||
if (!client->reachable())
|
if (!client->reachable())
|
||||||
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::initialized));
|
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::initialized, timeOutInMs()));
|
||||||
QVERIFY(client->reachable());
|
QVERIFY(client->reachable());
|
||||||
|
|
||||||
// The kind of AST support we need was introduced in LLVM 13.
|
// The kind of AST support we need was introduced in LLVM 13.
|
||||||
@@ -128,7 +115,7 @@ void ClangdTests::testFindReferences()
|
|||||||
|
|
||||||
// Wait for index to build.
|
// Wait for index to build.
|
||||||
if (!client->isFullyIndexed())
|
if (!client->isFullyIndexed())
|
||||||
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::indexingFinished));
|
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::indexingFinished, timeOutInMs()));
|
||||||
QVERIFY(client->isFullyIndexed());
|
QVERIFY(client->isFullyIndexed());
|
||||||
|
|
||||||
// Open cpp documents.
|
// Open cpp documents.
|
||||||
@@ -167,7 +154,7 @@ void ClangdTests::testFindReferences()
|
|||||||
cursor.setPosition((pos)); \
|
cursor.setPosition((pos)); \
|
||||||
searchResults.clear(); \
|
searchResults.clear(); \
|
||||||
client->findUsages((doc), cursor, {}); \
|
client->findUsages((doc), cursor, {}); \
|
||||||
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::findUsagesDone)); \
|
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::findUsagesDone, timeOutInMs())); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define EXPECT_RESULT(index, lne, col, type) \
|
#define EXPECT_RESULT(index, lne, col, type) \
|
||||||
|
@@ -192,6 +192,10 @@ signals:
|
|||||||
void autoSaved();
|
void autoSaved();
|
||||||
void currentEditorAboutToChange(Core::IEditor *editor);
|
void currentEditorAboutToChange(Core::IEditor *editor);
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
void linkOpened();
|
||||||
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
static void saveDocument();
|
static void saveDocument();
|
||||||
static void saveDocumentAs();
|
static void saveDocumentAs();
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
add_qtc_plugin(CppEditor
|
add_qtc_plugin(CppEditor
|
||||||
DEFINES CPPEDITOR_LIBRARY
|
DEFINES CPPEDITOR_LIBRARY
|
||||||
PLUGIN_DEPENDS Core CppTools ProjectExplorer TextEditor
|
PLUGIN_DEPENDS Core CppTools ProjectExplorer TextEditor
|
||||||
PLUGIN_TEST_DEPENDS QmakeProjectManager
|
PLUGIN_TEST_DEPENDS QbsProjectManager QmakeProjectManager
|
||||||
SOURCES
|
SOURCES
|
||||||
cppautocompleter.cpp cppautocompleter.h
|
cppautocompleter.cpp cppautocompleter.h
|
||||||
cppcodemodelinspectordialog.cpp cppcodemodelinspectordialog.h cppcodemodelinspectordialog.ui
|
cppcodemodelinspectordialog.cpp cppcodemodelinspectordialog.h cppcodemodelinspectordialog.ui
|
||||||
|
@@ -17,6 +17,7 @@ QtcPlugin {
|
|||||||
|
|
||||||
pluginTestDepends: [
|
pluginTestDepends: [
|
||||||
"QmakeProjectManager",
|
"QmakeProjectManager",
|
||||||
|
"QbsProjectManager",
|
||||||
]
|
]
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
|
@@ -9,4 +9,5 @@ QTC_PLUGIN_DEPENDS += \
|
|||||||
cpptools \
|
cpptools \
|
||||||
projectexplorer
|
projectexplorer
|
||||||
QTC_TEST_DEPENDS += \
|
QTC_TEST_DEPENDS += \
|
||||||
|
qbsprojectmanager \
|
||||||
qmakeprojectmanager
|
qmakeprojectmanager
|
||||||
|
@@ -27,6 +27,10 @@
|
|||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
namespace ProjectExplorer { class Kit; }
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace CppEditor {
|
namespace CppEditor {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -49,6 +53,10 @@ public:
|
|||||||
|
|
||||||
CppQuickFixAssistProvider *quickFixProvider() const;
|
CppQuickFixAssistProvider *quickFixProvider() const;
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
ProjectExplorer::Kit *m_testKit = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void outlineSortingChanged(bool sort);
|
void outlineSortingChanged(bool sort);
|
||||||
void typeHierarchyRequested();
|
void typeHierarchyRequested();
|
||||||
@@ -67,6 +75,8 @@ private:
|
|||||||
QVector<QObject *> createTestObjects() const override;
|
QVector<QObject *> createTestObjects() const override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
// The following tests expect that no projects are loaded on start-up.
|
// The following tests expect that no projects are loaded on start-up.
|
||||||
void test_SwitchMethodDeclarationDefinition_data();
|
void test_SwitchMethodDeclarationDefinition_data();
|
||||||
void test_SwitchMethodDeclarationDefinition();
|
void test_SwitchMethodDeclarationDefinition();
|
||||||
|
@@ -28,9 +28,12 @@
|
|||||||
#include "cppeditor.h"
|
#include "cppeditor.h"
|
||||||
#include "cppeditorwidget.h"
|
#include "cppeditorwidget.h"
|
||||||
#include "cppeditordocument.h"
|
#include "cppeditordocument.h"
|
||||||
|
#include "cppeditorplugin.h"
|
||||||
|
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <cpptools/cppcodemodelsettings.h>
|
||||||
#include <cpptools/cppsemanticinfo.h>
|
#include <cpptools/cppsemanticinfo.h>
|
||||||
|
#include <cpptools/cpptoolsreuse.h>
|
||||||
#include <cplusplus/CppDocument.h>
|
#include <cplusplus/CppDocument.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -74,12 +77,20 @@ bool TestDocument::hasCursorMarker() const { return m_cursorPosition != -1; }
|
|||||||
bool TestDocument::hasAnchorMarker() const { return m_anchorPosition != -1; }
|
bool TestDocument::hasAnchorMarker() const { return m_anchorPosition != -1; }
|
||||||
|
|
||||||
TestCase::TestCase(bool runGarbageCollector)
|
TestCase::TestCase(bool runGarbageCollector)
|
||||||
: CppTools::Tests::TestCase(runGarbageCollector)
|
: CppTools::Tests::TestCase(runGarbageCollector),
|
||||||
|
m_prevUseClangd(CppTools::codeModelSettings()->useClangd())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase::~TestCase()
|
TestCase::~TestCase()
|
||||||
{
|
{
|
||||||
|
CppTools::codeModelSettings()->setUseClangd(m_prevUseClangd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestCase::setUseClangd()
|
||||||
|
{
|
||||||
|
if (CppEditorPlugin::instance()->m_testKit)
|
||||||
|
CppTools::codeModelSettings()->setUseClangd(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestCase::openCppEditor(const QString &fileName, CppEditor **editor, CppEditorWidget **editorWidget)
|
bool TestCase::openCppEditor(const QString &fileName, CppEditor **editor, CppEditorWidget **editorWidget)
|
||||||
|
@@ -62,12 +62,17 @@ public:
|
|||||||
TestCase(bool runGarbageCollector = true);
|
TestCase(bool runGarbageCollector = true);
|
||||||
~TestCase();
|
~TestCase();
|
||||||
|
|
||||||
|
void setUseClangd();
|
||||||
|
|
||||||
static bool openCppEditor(const QString &fileName,
|
static bool openCppEditor(const QString &fileName,
|
||||||
CppEditor **editor,
|
CppEditor **editor,
|
||||||
CppEditorWidget **editorWidget = 0);
|
CppEditorWidget **editorWidget = 0);
|
||||||
|
|
||||||
static CPlusPlus::Document::Ptr waitForRehighlightedSemanticDocument(
|
static CPlusPlus::Document::Ptr waitForRehighlightedSemanticDocument(
|
||||||
CppEditorWidget *editorWidget);
|
CppEditorWidget *editorWidget);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool m_prevUseClangd;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tests
|
} // namespace Tests
|
||||||
|
@@ -7185,7 +7185,7 @@ void CppEditorPlugin::test_quickfix_ExtractFunction_data()
|
|||||||
"}\n"
|
"}\n"
|
||||||
"void NS::C::f(NS::C &c)\n"
|
"void NS::C::f(NS::C &c)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" @{start}C *c = &c;@{end}\n"
|
" @{start}C *c2 = &c;@{end}\n"
|
||||||
"}\n")
|
"}\n")
|
||||||
<< _("namespace NS {\n"
|
<< _("namespace NS {\n"
|
||||||
"class C {\n"
|
"class C {\n"
|
||||||
@@ -7197,7 +7197,7 @@ void CppEditorPlugin::test_quickfix_ExtractFunction_data()
|
|||||||
"}\n"
|
"}\n"
|
||||||
"inline void NS::C::extracted(NS::C &c)\n"
|
"inline void NS::C::extracted(NS::C &c)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" C *c = &c;\n"
|
" C *c2 = &c;\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"void NS::C::f(NS::C &c)\n"
|
"void NS::C::f(NS::C &c)\n"
|
||||||
|
@@ -28,6 +28,8 @@
|
|||||||
#include "cppeditorplugin.h"
|
#include "cppeditorplugin.h"
|
||||||
#include "cppeditortestcase.h"
|
#include "cppeditortestcase.h"
|
||||||
|
|
||||||
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
@@ -100,10 +102,17 @@ UseSelectionsTestCase::UseSelectionsTestCase(TestDocument &testFile,
|
|||||||
|
|
||||||
bool hasTimedOut;
|
bool hasTimedOut;
|
||||||
const SelectionList selections = waitForUseSelections(&hasTimedOut);
|
const SelectionList selections = waitForUseSelections(&hasTimedOut);
|
||||||
|
const bool clangCodeModel = CppTools::CppModelManager::instance()->isClangCodeModelActive();
|
||||||
|
if (clangCodeModel) {
|
||||||
|
QEXPECT_FAIL("local use as macro argument - argument eaten", "fails with CCM, find out why",
|
||||||
|
Abort);
|
||||||
|
} else {
|
||||||
QEXPECT_FAIL("non-local use as macro argument - argument expanded 1", "TODO", Abort);
|
QEXPECT_FAIL("non-local use as macro argument - argument expanded 1", "TODO", Abort);
|
||||||
|
}
|
||||||
QVERIFY(!hasTimedOut);
|
QVERIFY(!hasTimedOut);
|
||||||
// foreach (const Selection &selection, selections)
|
// foreach (const Selection &selection, selections)
|
||||||
// qDebug() << QTest::toString(selection);
|
// qDebug() << QTest::toString(selection);
|
||||||
|
if (!clangCodeModel)
|
||||||
QEXPECT_FAIL("non-local use as macro argument - argument expanded 2", "TODO", Abort);
|
QEXPECT_FAIL("non-local use as macro argument - argument expanded 2", "TODO", Abort);
|
||||||
QCOMPARE(selections, expectedSelections);
|
QCOMPARE(selections, expectedSelections);
|
||||||
}
|
}
|
||||||
|
@@ -28,13 +28,18 @@
|
|||||||
#include "cppeditorplugin.h"
|
#include "cppeditorplugin.h"
|
||||||
#include "cppeditortestcase.h"
|
#include "cppeditortestcase.h"
|
||||||
|
|
||||||
|
#include <cpptools/cppcodemodelsettings.h>
|
||||||
#include <cpptools/cppelementevaluator.h>
|
#include <cpptools/cppelementevaluator.h>
|
||||||
#include <cpptools/cppfollowsymbolundercursor.h>
|
#include <cpptools/cppfollowsymbolundercursor.h>
|
||||||
#include <cpptools/cppvirtualfunctionassistprovider.h>
|
#include <cpptools/cppvirtualfunctionassistprovider.h>
|
||||||
#include <cpptools/cppvirtualfunctionproposalitem.h>
|
#include <cpptools/cppvirtualfunctionproposalitem.h>
|
||||||
|
#include <cpptools/cpptoolsreuse.h>
|
||||||
#include <cpptools/cpptoolstestcase.h>
|
#include <cpptools/cpptoolstestcase.h>
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/kitmanager.h>
|
||||||
|
#include <projectexplorer/projectexplorer.h>
|
||||||
|
|
||||||
#include <texteditor/codeassist/genericproposalmodel.h>
|
#include <texteditor/codeassist/genericproposalmodel.h>
|
||||||
#include <texteditor/codeassist/iassistprocessor.h>
|
#include <texteditor/codeassist/iassistprocessor.h>
|
||||||
#include <texteditor/codeassist/iassistproposal.h>
|
#include <texteditor/codeassist/iassistproposal.h>
|
||||||
@@ -76,6 +81,7 @@ using namespace CPlusPlus;
|
|||||||
using namespace CppTools;
|
using namespace CppTools;
|
||||||
using namespace TextEditor;
|
using namespace TextEditor;
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
class OverrideItem {
|
class OverrideItem {
|
||||||
public:
|
public:
|
||||||
@@ -267,6 +273,8 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
{
|
{
|
||||||
QVERIFY(succeededSoFar());
|
QVERIFY(succeededSoFar());
|
||||||
|
|
||||||
|
setUseClangd();
|
||||||
|
|
||||||
// Check if there are initial and target position markers
|
// Check if there are initial and target position markers
|
||||||
TestDocumentPtr initialTestFile = testFileWithInitialCursorMarker(testFiles);
|
TestDocumentPtr initialTestFile = testFileWithInitialCursorMarker(testFiles);
|
||||||
QVERIFY2(initialTestFile,
|
QVERIFY2(initialTestFile,
|
||||||
@@ -275,13 +283,68 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
QVERIFY2(targetTestFile,
|
QVERIFY2(targetTestFile,
|
||||||
"No test file with target cursor marker is provided.");
|
"No test file with target cursor marker is provided.");
|
||||||
|
|
||||||
|
const QString curTestName = QLatin1String(QTest::currentTestFunction());
|
||||||
|
const QString tag = QLatin1String(QTest::currentDataTag());
|
||||||
|
const bool useClangd = CppTools::codeModelSettings()->useClangd();
|
||||||
|
if (useClangd) {
|
||||||
|
if (curTestName == "test_FollowSymbolUnderCursor_virtualFunctionCall"
|
||||||
|
|| curTestName == "test_FollowSymbolUnderCursor_virtualFunctionCall_multipleDocuments") {
|
||||||
|
QSKIP("TODO: Add test infrastructure for this");
|
||||||
|
}
|
||||||
|
if (curTestName == "test_FollowSymbolUnderCursor_QObject_connect"
|
||||||
|
|| curTestName == "test_FollowSymbolUnderCursor_QObject_oldStyleConnect") {
|
||||||
|
QSKIP("TODO: Implement fall-back");
|
||||||
|
}
|
||||||
|
if (curTestName == "test_FollowSymbolUnderCursor_classOperator" && tag == "backward")
|
||||||
|
QSKIP("clangd goes to operator name first");
|
||||||
|
if (tag.toLower().contains("fuzzy"))
|
||||||
|
QSKIP("fuzzy matching is not supposed to work with clangd"); // TODO: Implement fallback as we do with libclang
|
||||||
|
if (tag == "baseClassFunctionIntroducedByUsingDeclaration")
|
||||||
|
QSKIP("clangd points to the using declaration");
|
||||||
|
if (tag == "classDestructor")
|
||||||
|
QSKIP("clangd wants the cursor before the ~ character");
|
||||||
|
if (curTestName == "test_FollowSymbolUnderCursor_classOperator_inOp")
|
||||||
|
QSKIP("clangd goes to operator name first");
|
||||||
|
if (tag == "fromFunctionBody" || tag == "fromReturnType"
|
||||||
|
|| tag == "conversionOperatorDecl2Def") {
|
||||||
|
QSKIP("TODO: explicit decl/def switch not yet supported with clangd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write files to disk
|
// Write files to disk
|
||||||
CppTools::Tests::TemporaryDir temporaryDir;
|
CppTools::Tests::TemporaryDir temporaryDir;
|
||||||
QVERIFY(temporaryDir.isValid());
|
QVERIFY(temporaryDir.isValid());
|
||||||
|
QString projectFileContent = "CppApplication { files: [";
|
||||||
foreach (TestDocumentPtr testFile, testFiles) {
|
foreach (TestDocumentPtr testFile, testFiles) {
|
||||||
QVERIFY(testFile->baseDirectory().isEmpty());
|
QVERIFY(testFile->baseDirectory().isEmpty());
|
||||||
testFile->setBaseDirectory(temporaryDir.path());
|
testFile->setBaseDirectory(temporaryDir.path());
|
||||||
QVERIFY(testFile->writeToDisk());
|
QVERIFY(testFile->writeToDisk());
|
||||||
|
projectFileContent += QString::fromLatin1("\"%1\",").arg(testFile->filePath());
|
||||||
|
}
|
||||||
|
projectFileContent += "]}\n";
|
||||||
|
|
||||||
|
class ProjectCloser {
|
||||||
|
public:
|
||||||
|
void setProject(Project *p) { m_p = p; }
|
||||||
|
~ProjectCloser() { if (m_p) ProjectExplorerPlugin::unloadProject(m_p); }
|
||||||
|
private:
|
||||||
|
Project * m_p = nullptr;
|
||||||
|
} projectCloser;
|
||||||
|
|
||||||
|
if (useClangd) {
|
||||||
|
TestDocument projectFile(projectFileContent.toUtf8(), "project.qbs");
|
||||||
|
projectFile.setBaseDirectory(temporaryDir.path());
|
||||||
|
QVERIFY(projectFile.writeToDisk());
|
||||||
|
const auto openProjectResult = ProjectExplorerPlugin::openProject(projectFile.filePath());
|
||||||
|
QVERIFY2(openProjectResult && openProjectResult.project(),
|
||||||
|
qPrintable(openProjectResult.errorMessage()));
|
||||||
|
projectCloser.setProject(openProjectResult.project());
|
||||||
|
openProjectResult.project()->configureAsExampleProject(
|
||||||
|
CppEditorPlugin::instance()->m_testKit);
|
||||||
|
|
||||||
|
// Wait until project is fully indexed.
|
||||||
|
QVERIFY(CppTools::Tests::waitForSignalOrTimeout(openProjectResult.project(),
|
||||||
|
&Project::indexingFinished, CppTools::Tests::clangdIndexingTimeout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Code Model
|
// Update Code Model
|
||||||
@@ -294,6 +357,7 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
foreach (TestDocumentPtr testFile, testFiles) {
|
foreach (TestDocumentPtr testFile, testFiles) {
|
||||||
QVERIFY(openCppEditor(testFile->filePath(), &testFile->m_editor,
|
QVERIFY(openCppEditor(testFile->filePath(), &testFile->m_editor,
|
||||||
&testFile->m_editorWidget));
|
&testFile->m_editorWidget));
|
||||||
|
if (!useClangd) // Editors get closed when unloading project.
|
||||||
closeEditorAtEndOfTestCase(testFile->m_editor);
|
closeEditorAtEndOfTestCase(testFile->m_editor);
|
||||||
|
|
||||||
// Wait until the indexer processed the just opened file.
|
// Wait until the indexer processed the just opened file.
|
||||||
@@ -303,12 +367,15 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
const Document::Ptr document = waitForFileInGlobalSnapshot(testFile->filePath());
|
const Document::Ptr document = waitForFileInGlobalSnapshot(testFile->filePath());
|
||||||
QVERIFY(document);
|
QVERIFY(document);
|
||||||
if (document->checkMode() == Document::FullCheck) {
|
if (document->checkMode() == Document::FullCheck) {
|
||||||
|
if (!document->diagnosticMessages().isEmpty())
|
||||||
|
qDebug() << document->diagnosticMessages().first().text();
|
||||||
QVERIFY(document->diagnosticMessages().isEmpty());
|
QVERIFY(document->diagnosticMessages().isEmpty());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rehighlight
|
// Rehighlight
|
||||||
|
if (!useClangd)
|
||||||
waitForRehighlightedSemanticDocument(testFile->m_editorWidget);
|
waitForRehighlightedSemanticDocument(testFile->m_editorWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,14 +396,8 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
FollowSymbolInterface &delegate = CppModelManager::instance()->followSymbolInterface();
|
FollowSymbolInterface &delegate = CppModelManager::instance()->followSymbolInterface();
|
||||||
auto* builtinFollowSymbol = dynamic_cast<FollowSymbolUnderCursor *>(&delegate);
|
auto* builtinFollowSymbol = dynamic_cast<FollowSymbolUnderCursor *>(&delegate);
|
||||||
if (!builtinFollowSymbol) {
|
if (!builtinFollowSymbol) {
|
||||||
if (filePaths.size() > 1)
|
if (curTestName == "test_FollowSymbolUnderCursor_QTCREATORBUG7903")
|
||||||
QSKIP("Clang FollowSymbol does not currently support multiple files (except cpp+header)");
|
|
||||||
const QString curTestName = QLatin1String(QTest::currentTestFunction());
|
|
||||||
if (curTestName == "test_FollowSymbolUnderCursor_QObject_connect"
|
|
||||||
|| curTestName == "test_FollowSymbolUnderCursor_virtualFunctionCall"
|
|
||||||
|| curTestName == "test_FollowSymbolUnderCursor_QTCREATORBUG7903") {
|
|
||||||
QSKIP((curTestName + " is not supported by Clang FollowSymbol").toLatin1());
|
QSKIP((curTestName + " is not supported by Clang FollowSymbol").toLatin1());
|
||||||
}
|
|
||||||
|
|
||||||
widget->openLinkUnderCursor();
|
widget->openLinkUnderCursor();
|
||||||
break;
|
break;
|
||||||
@@ -358,6 +419,9 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SwitchBetweenMethodDeclarationDefinitionAction:
|
case SwitchBetweenMethodDeclarationDefinitionAction:
|
||||||
|
if (CppTools::codeModelSettings()->useClangd())
|
||||||
|
initialTestFile->m_editorWidget->openLinkUnderCursor();
|
||||||
|
else
|
||||||
CppEditorPlugin::instance()->switchDeclarationDefinition();
|
CppEditorPlugin::instance()->switchDeclarationDefinition();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -365,7 +429,14 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useClangd) {
|
||||||
|
QEXPECT_FAIL("infiniteLoopLocalTypedef_QTCREATORBUG-11999",
|
||||||
|
"clangd bug: Go to definition does not return", Abort);
|
||||||
|
QVERIFY(CppTools::Tests::waitForSignalOrTimeout(EditorManager::instance(),
|
||||||
|
&EditorManager::linkOpened, 10000));
|
||||||
|
} else {
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
// Compare
|
// Compare
|
||||||
IEditor *currentEditor = EditorManager::currentEditor();
|
IEditor *currentEditor = EditorManager::currentEditor();
|
||||||
@@ -379,8 +450,11 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
// qDebug() << "Expected line:" << expectedLine;
|
// qDebug() << "Expected line:" << expectedLine;
|
||||||
// qDebug() << "Expected column:" << expectedColumn;
|
// qDebug() << "Expected column:" << expectedColumn;
|
||||||
|
|
||||||
|
if (!CppTools::codeModelSettings()->useClangd()) {
|
||||||
QEXPECT_FAIL("globalVarFromEnum", "Contributor works on a fix.", Abort);
|
QEXPECT_FAIL("globalVarFromEnum", "Contributor works on a fix.", Abort);
|
||||||
QEXPECT_FAIL("matchFunctionSignature_Follow_5", "foo(int) resolved as CallAST", Abort);
|
QEXPECT_FAIL("matchFunctionSignature_Follow_5", "foo(int) resolved as CallAST", Abort);
|
||||||
|
}
|
||||||
|
|
||||||
QCOMPARE(currentTextEditor->currentLine(), expectedLine);
|
QCOMPARE(currentTextEditor->currentLine(), expectedLine);
|
||||||
QCOMPARE(currentTextEditor->currentColumn(), expectedColumn);
|
QCOMPARE(currentTextEditor->currentColumn(), expectedColumn);
|
||||||
|
|
||||||
@@ -421,6 +495,25 @@ Q_DECLARE_METATYPE(QList<CppEditor::Internal::TestDocumentPtr>)
|
|||||||
namespace CppEditor {
|
namespace CppEditor {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
void CppEditorPlugin::initTestCase()
|
||||||
|
{
|
||||||
|
const auto settings = CppTools::codeModelSettings();
|
||||||
|
const QString clangdFromEnv = qEnvironmentVariable("QTC_CLANGD");
|
||||||
|
if (clangdFromEnv.isEmpty())
|
||||||
|
return;
|
||||||
|
settings->setClangdFilePath(Utils::FilePath::fromString(clangdFromEnv));
|
||||||
|
const auto clangd = settings->clangdFilePath();
|
||||||
|
if (clangd.isEmpty() || !clangd.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find suitable kit.
|
||||||
|
m_testKit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
|
||||||
|
return k->isValid();
|
||||||
|
});
|
||||||
|
if (!m_testKit)
|
||||||
|
QSKIP("This test requires at least one kit to be present");
|
||||||
|
}
|
||||||
|
|
||||||
void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
|
void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QByteArray>("header");
|
QTest::addColumn<QByteArray>("header");
|
||||||
@@ -576,7 +669,6 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
|
|||||||
"int OtherClass::var;\n"
|
"int OtherClass::var;\n"
|
||||||
"namespace NS {\n"
|
"namespace NS {\n"
|
||||||
"int OtherClass::var;\n"
|
"int OtherClass::var;\n"
|
||||||
"float Test::var;\n"
|
|
||||||
"int Test::$var;\n"
|
"int Test::$var;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
|
|
||||||
@@ -1276,7 +1368,6 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_multipleDocuments_data()
|
|||||||
"int OtherClass::var;\n"
|
"int OtherClass::var;\n"
|
||||||
"namespace NS {\n"
|
"namespace NS {\n"
|
||||||
"int OtherClass::var;\n"
|
"int OtherClass::var;\n"
|
||||||
"float Test::var;\n"
|
|
||||||
"int Test::$var;\n"
|
"int Test::$var;\n"
|
||||||
"}\n", "file.cpp")};
|
"}\n", "file.cpp")};
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,7 @@
|
|||||||
#include <cplusplus/CppDocument.h>
|
#include <cplusplus/CppDocument.h>
|
||||||
#include <utils/executeondestruction.h>
|
#include <utils/executeondestruction.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/temporarydirectory.h>
|
#include <utils/temporarydirectory.h>
|
||||||
|
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
@@ -421,5 +422,15 @@ bool VerifyCleanCppModelManager::isClean(bool testOnlyForCleanedProjects)
|
|||||||
|
|
||||||
#undef RETURN_FALSE_IF_NOT
|
#undef RETURN_FALSE_IF_NOT
|
||||||
|
|
||||||
|
int clangdIndexingTimeout()
|
||||||
|
{
|
||||||
|
const QByteArray timeoutAsByteArray = qgetenv("QTC_CLANGD_INDEXING_TIMEOUT");
|
||||||
|
bool isConversionOk = false;
|
||||||
|
const int intervalAsInt = timeoutAsByteArray.toInt(&isConversionOk);
|
||||||
|
if (!isConversionOk)
|
||||||
|
return Utils::HostOsInfo::isWindowsHost() ? 20000 : 10000;
|
||||||
|
return intervalAsInt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Tests
|
} // namespace Tests
|
||||||
} // namespace CppTools
|
} // namespace CppTools
|
||||||
|
@@ -30,7 +30,9 @@
|
|||||||
#include <cplusplus/CppDocument.h>
|
#include <cplusplus/CppDocument.h>
|
||||||
#include <utils/temporarydirectory.h>
|
#include <utils/temporarydirectory.h>
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace CPlusPlus {
|
namespace CPlusPlus {
|
||||||
class Document;
|
class Document;
|
||||||
@@ -54,6 +56,23 @@ class ProjectInfo;
|
|||||||
|
|
||||||
namespace Tests {
|
namespace Tests {
|
||||||
|
|
||||||
|
int CPPTOOLS_EXPORT clangdIndexingTimeout();
|
||||||
|
|
||||||
|
template <typename Signal> inline bool waitForSignalOrTimeout(
|
||||||
|
const typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal,
|
||||||
|
int timeoutInMs)
|
||||||
|
{
|
||||||
|
QTimer timer;
|
||||||
|
timer.setSingleShot(true);
|
||||||
|
timer.setInterval(timeoutInMs);
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
QObject::connect(sender, signal, &loop, &QEventLoop::quit);
|
||||||
|
timer.start();
|
||||||
|
loop.exec();
|
||||||
|
return timer.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
class CPPTOOLS_EXPORT TestDocument
|
class CPPTOOLS_EXPORT TestDocument
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@@ -212,6 +212,10 @@ signals:
|
|||||||
|
|
||||||
void rootProjectDirectoryChanged();
|
void rootProjectDirectoryChanged();
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
void indexingFinished(Utils::Id indexer);
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage);
|
virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage);
|
||||||
void createTargetFromMap(const QVariantMap &map, int index);
|
void createTargetFromMap(const QVariantMap &map, int index);
|
||||||
|
@@ -6257,6 +6257,10 @@ void TextEditorWidget::findLinkAt(const QTextCursor &cursor,
|
|||||||
|
|
||||||
bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit)
|
bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit)
|
||||||
{
|
{
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
struct Signaller { ~Signaller() { emit EditorManager::instance()->linkOpened(); } } s;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!link.hasValidTarget())
|
if (!link.hasValidTarget())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user