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:
Christian Kandeler
2021-05-19 13:22:49 +02:00
parent 003ab51c3d
commit ff0301635e
16 changed files with 207 additions and 40 deletions

View File

@@ -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()) {

View File

@@ -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) \

View File

@@ -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();

View File

@@ -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

View File

@@ -17,6 +17,7 @@ QtcPlugin {
pluginTestDepends: [ pluginTestDepends: [
"QmakeProjectManager", "QmakeProjectManager",
"QbsProjectManager",
] ]
files: [ files: [

View File

@@ -9,4 +9,5 @@ QTC_PLUGIN_DEPENDS += \
cpptools \ cpptools \
projectexplorer projectexplorer
QTC_TEST_DEPENDS += \ QTC_TEST_DEPENDS += \
qbsprojectmanager \
qmakeprojectmanager qmakeprojectmanager

View File

@@ -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();

View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View File

@@ -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);
} }

View File

@@ -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")};
} }

View File

@@ -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

View File

@@ -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:

View File

@@ -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);

View File

@@ -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;