2015-05-08 15:48:17 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
|
** Contact: http://www.qt.io/licensing
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms and
|
|
|
|
|
** conditions see http://www.qt.io/terms-conditions. For further information
|
|
|
|
|
** use the contact form at http://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, The Qt Company gives you certain additional
|
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "clangcodecompletion_test.h"
|
|
|
|
|
|
2015-06-16 11:56:00 +02:00
|
|
|
#include "../clangbackendipcintegration.h"
|
2015-07-06 12:05:02 +02:00
|
|
|
#include "../clangcompletionassistinterface.h"
|
2015-05-08 15:48:17 +02:00
|
|
|
#include "../clangmodelmanagersupport.h"
|
|
|
|
|
|
2015-07-14 19:01:32 +02:00
|
|
|
#include <clangcodemodel/clangeditordocumentprocessor.h>
|
2015-05-08 15:48:17 +02:00
|
|
|
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <cpptools/cpptoolsreuse.h>
|
|
|
|
|
#include <cpptools/cpptoolstestcase.h>
|
|
|
|
|
#include <cpptools/modelmanagertesthelper.h>
|
2016-01-12 12:51:33 +01:00
|
|
|
#include <cpptools/projectinfo.h>
|
2015-05-08 15:48:17 +02:00
|
|
|
#include <texteditor/codeassist/assistinterface.h>
|
2015-07-06 12:05:02 +02:00
|
|
|
#include <texteditor/codeassist/assistproposalitem.h>
|
2015-05-08 15:48:17 +02:00
|
|
|
#include <texteditor/codeassist/completionassistprovider.h>
|
|
|
|
|
#include <texteditor/codeassist/genericproposalmodel.h>
|
|
|
|
|
#include <texteditor/codeassist/iassistprocessor.h>
|
|
|
|
|
#include <texteditor/codeassist/iassistproposal.h>
|
|
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
#include <texteditor/texteditor.h>
|
|
|
|
|
|
2015-08-19 12:36:43 +02:00
|
|
|
#include <clangbackendipc/cmbcompletecodemessage.h>
|
|
|
|
|
#include <clangbackendipc/cmbendmessage.h>
|
2015-08-31 14:36:58 +02:00
|
|
|
#include <clangbackendipc/cmbregisterprojectsforeditormessage.h>
|
|
|
|
|
#include <clangbackendipc/cmbregistertranslationunitsforeditormessage.h>
|
|
|
|
|
#include <clangbackendipc/cmbunregisterprojectsforeditormessage.h>
|
|
|
|
|
#include <clangbackendipc/cmbunregistertranslationunitsforeditormessage.h>
|
2015-08-31 16:10:36 +02:00
|
|
|
#include <clangbackendipc/registerunsavedfilesforeditormessage.h>
|
2015-10-13 15:56:41 +02:00
|
|
|
#include <clangbackendipc/updatetranslationunitsforeditormessage.h>
|
2015-05-08 15:48:17 +02:00
|
|
|
#include <utils/changeset.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QtTest>
|
|
|
|
|
|
2015-06-16 11:56:00 +02:00
|
|
|
using namespace ClangBackEnd;
|
2015-05-08 15:48:17 +02:00
|
|
|
using namespace ClangCodeModel;
|
|
|
|
|
using namespace ClangCodeModel::Internal;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2015-07-17 11:17:11 +02:00
|
|
|
QString _(const char text[])
|
|
|
|
|
{ return QString::fromUtf8(text); }
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
QString qrcPath(const QByteArray relativeFilePath)
|
|
|
|
|
{ return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath); }
|
|
|
|
|
|
2015-07-17 11:17:11 +02:00
|
|
|
QString fileName(const QString &filePath)
|
|
|
|
|
{ return QFileInfo(filePath).fileName(); }
|
|
|
|
|
|
2015-07-17 11:18:42 +02:00
|
|
|
CppTools::Tests::TemporaryDir *globalTemporaryDir()
|
|
|
|
|
{
|
|
|
|
|
static CppTools::Tests::TemporaryDir dir;
|
|
|
|
|
QTC_CHECK(dir.isValid());
|
|
|
|
|
return &dir;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
struct LogOutput
|
|
|
|
|
{
|
|
|
|
|
LogOutput(const QString &text) : text(text.toUtf8()) {}
|
|
|
|
|
LogOutput(const char text[]) : text(text) {}
|
|
|
|
|
QByteArray text;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void printRawLines(QTextStream &out, const QList<QByteArray> &lines)
|
|
|
|
|
{
|
|
|
|
|
foreach (const QByteArray &line, lines) {
|
|
|
|
|
QByteArray rawLine = line;
|
|
|
|
|
rawLine.prepend("\"");
|
|
|
|
|
rawLine.append("\\n\"\n");
|
|
|
|
|
out << rawLine;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void printDifference(const LogOutput &actual, const LogOutput &expected)
|
|
|
|
|
{
|
|
|
|
|
QTextStream out(stderr);
|
|
|
|
|
|
|
|
|
|
const QList<QByteArray> actualLines = actual.text.split('\n');
|
|
|
|
|
const QList<QByteArray> expectedLines = expected.text.split('\n');
|
|
|
|
|
|
|
|
|
|
out << "-- ACTUAL:\n";
|
|
|
|
|
printRawLines(out, actualLines);
|
|
|
|
|
out << "-- EXPECTED:\n";
|
|
|
|
|
printRawLines(out, expectedLines);
|
|
|
|
|
|
|
|
|
|
if (actualLines.size() != expectedLines.size()) {
|
|
|
|
|
out << "-- DIFFERENCE IN LINE COUNT:\n"
|
|
|
|
|
<< " actual lines:" << actualLines.size() << '\n'
|
|
|
|
|
<< " expected lines:" << expectedLines.size() << '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out << "-- FIRST LINE THAT DIFFERS:\n";
|
|
|
|
|
auto actualLineIt = actualLines.cbegin();
|
|
|
|
|
auto expectedLineIt = expectedLines.cbegin();
|
|
|
|
|
int line = 1;
|
|
|
|
|
forever {
|
|
|
|
|
if (actualLineIt == actualLines.cend() && expectedLineIt != expectedLines.cend()) {
|
|
|
|
|
out << " line: " << line << '\n';
|
|
|
|
|
out << " actual: <none>\n";
|
|
|
|
|
out << " expected: \"" << *expectedLineIt << "\"\n";
|
|
|
|
|
} else if (actualLineIt != actualLines.cend() && expectedLineIt == expectedLines.cend()) {
|
|
|
|
|
out << " line: " << line << '\n';
|
|
|
|
|
out << " actual: \"" << *actualLineIt << "\"\n";
|
|
|
|
|
out << " expected: <none>\n";
|
|
|
|
|
} else {
|
|
|
|
|
if (*actualLineIt != *expectedLineIt) {
|
|
|
|
|
out << " line: " << line << '\n';
|
|
|
|
|
out << " actual: \"" << *actualLineIt << "\"\n";
|
|
|
|
|
out << " expected: \"" << *expectedLineIt << "\"\n";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++line;
|
|
|
|
|
++actualLineIt;
|
|
|
|
|
++expectedLineIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool compare(const LogOutput &actual, const LogOutput &expected)
|
|
|
|
|
{
|
|
|
|
|
const bool isEqual = actual.text == expected.text;
|
|
|
|
|
if (!isEqual)
|
|
|
|
|
printDifference(actual, expected);
|
|
|
|
|
return isEqual;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray readFile(const QString &filePath)
|
|
|
|
|
{
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
QTC_ASSERT(file.open(QFile::ReadOnly | QFile::Text), return QByteArray());
|
|
|
|
|
return file.readAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool writeFile(const QString &filePath, const QByteArray &contents)
|
|
|
|
|
{
|
|
|
|
|
QFile file(filePath);
|
|
|
|
|
if (!file.open(QFile::WriteOnly | QFile::Text))
|
|
|
|
|
return false;
|
|
|
|
|
if (file.write(contents) != contents.size())
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void insertTextAtTopOfEditor(TextEditor::BaseTextEditor *editor, const QByteArray &text)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(editor, return);
|
2015-11-27 16:02:38 +01:00
|
|
|
::Utils::ChangeSet cs;
|
2015-05-08 15:48:17 +02:00
|
|
|
cs.insert(0, QString::fromUtf8(text));
|
|
|
|
|
QTextCursor textCursor = editor->textCursor();
|
|
|
|
|
cs.apply(&textCursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WaitForAsyncCompletions
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
enum WaitResult { GotResults, GotInvalidResults, Timeout };
|
|
|
|
|
WaitResult wait(TextEditor::IAssistProcessor *processor,
|
|
|
|
|
TextEditor::AssistInterface *assistInterface);
|
|
|
|
|
|
|
|
|
|
TextEditor::IAssistProposalModel *proposalModel;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WaitForAsyncCompletions::WaitResult WaitForAsyncCompletions::wait(
|
|
|
|
|
TextEditor::IAssistProcessor *processor,
|
|
|
|
|
TextEditor::AssistInterface *assistInterface)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(processor, return Timeout);
|
|
|
|
|
QTC_ASSERT(assistInterface, return Timeout);
|
|
|
|
|
|
|
|
|
|
bool gotResults = false;
|
|
|
|
|
|
|
|
|
|
processor->setAsyncCompletionAvailableHandler(
|
|
|
|
|
[this, &gotResults] (TextEditor::IAssistProposal *proposal) {
|
|
|
|
|
QTC_ASSERT(proposal, return);
|
|
|
|
|
proposalModel = proposal->model();
|
|
|
|
|
delete proposal;
|
|
|
|
|
gotResults = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Are there any immediate results?
|
|
|
|
|
if (TextEditor::IAssistProposal *proposal = processor->perform(assistInterface)) {
|
|
|
|
|
delete processor;
|
|
|
|
|
proposalModel = proposal->model();
|
|
|
|
|
delete proposal;
|
|
|
|
|
QTC_ASSERT(proposalModel, return GotInvalidResults);
|
|
|
|
|
return GotResults;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There are not any, so wait for async results.
|
|
|
|
|
QElapsedTimer timer; timer.start();
|
|
|
|
|
while (!gotResults) {
|
|
|
|
|
if (timer.elapsed() >= 5 * 1000)
|
|
|
|
|
return Timeout;
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return proposalModel ? GotResults : GotInvalidResults;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ChangeDocumentReloadSetting
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ChangeDocumentReloadSetting(Core::IDocument::ReloadSetting reloadSetting)
|
|
|
|
|
: m_previousValue(Core::EditorManager::reloadSetting())
|
|
|
|
|
{
|
|
|
|
|
Core::EditorManager::setReloadSetting(reloadSetting);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ChangeDocumentReloadSetting()
|
|
|
|
|
{
|
|
|
|
|
Core::EditorManager::setReloadSetting(m_previousValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Core::IDocument::ReloadSetting m_previousValue;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ChangeIpcSender
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ChangeIpcSender(IpcSenderInterface *ipcSender)
|
|
|
|
|
{
|
2015-07-06 11:15:12 +02:00
|
|
|
auto &ipc = ModelManagerSupportClang::instance_forTestsOnly()->ipcCommunicator();
|
|
|
|
|
m_previousSender = ipc.setIpcSender(ipcSender);
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ChangeIpcSender()
|
|
|
|
|
{
|
2015-07-06 11:15:12 +02:00
|
|
|
auto &ipc = ModelManagerSupportClang::instance_forTestsOnly()->ipcCommunicator();
|
|
|
|
|
ipc.setIpcSender(m_previousSender);
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
IpcSenderInterface *m_previousSender;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
QString toString(const FileContainer &fileContainer)
|
|
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
2015-07-17 11:17:11 +02:00
|
|
|
ts << " Path: " << fileName(fileContainer.filePath().toString())
|
|
|
|
|
<< " ProjectPart: " << fileName(fileContainer.projectPartId().toString()) << "\n";
|
2015-05-08 15:48:17 +02:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString toString(const QVector<FileContainer> &fileContainers)
|
|
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
foreach (const FileContainer &fileContainer, fileContainers)
|
|
|
|
|
ts << toString(fileContainer);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString toString(const ProjectPartContainer &projectPartContainer)
|
|
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
ts << " ProjectPartContainer"
|
2015-07-17 11:17:11 +02:00
|
|
|
<< " id: " << fileName(projectPartContainer.projectPartId().toString());
|
2015-05-08 15:48:17 +02:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString toString(const QVector<ProjectPartContainer> &projectPartContainers)
|
|
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
foreach (const ProjectPartContainer &projectPartContainer, projectPartContainers)
|
|
|
|
|
ts << toString(projectPartContainer);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-19 12:36:43 +02:00
|
|
|
QString toString(const EndMessage &)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-08-19 12:36:43 +02:00
|
|
|
return QLatin1String("EndMessage\n");
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
QString toString(const RegisterTranslationUnitForEditorMessage &message)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
ts << "RegisterTranslationUnitForEditorMessage\n"
|
2015-08-19 12:36:43 +02:00
|
|
|
<< toString(message.fileContainers());
|
2015-05-08 15:48:17 +02:00
|
|
|
return out;
|
2015-10-13 15:56:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString toString(const UpdateTranslationUnitsForEditorMessage &message)
|
|
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-10-13 15:56:41 +02:00
|
|
|
ts << "UpdateTranslationUnitForEditorMessage\n"
|
|
|
|
|
<< toString(message.fileContainers());
|
|
|
|
|
return out;
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
QString toString(const UnregisterTranslationUnitsForEditorMessage &)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-08-31 14:36:58 +02:00
|
|
|
return QLatin1String("UnregisterTranslationUnitsForEditorMessage\n");
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
QString toString(const RegisterProjectPartsForEditorMessage &message)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
ts << "RegisterProjectPartsForEditorMessage\n"
|
2015-08-19 12:36:43 +02:00
|
|
|
<< toString(message.projectContainers()) << "\n";
|
2015-05-08 15:48:17 +02:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
QString toString(const UnregisterProjectPartsForEditorMessage &message)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
ts << "UnregisterProjectPartsForEditorMessage\n"
|
2015-08-19 12:36:43 +02:00
|
|
|
<< message.projectPartIds().join(Utf8String::fromUtf8(",")).toByteArray() << "\n";
|
2015-05-08 15:48:17 +02:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-31 16:10:36 +02:00
|
|
|
QString toString(const RegisterUnsavedFilesForEditorMessage &message)
|
|
|
|
|
{
|
|
|
|
|
QString out;
|
|
|
|
|
QTextStream ts(&out);
|
|
|
|
|
|
|
|
|
|
ts << "RegisterUnsavedFilesForEditorMessage\n"
|
|
|
|
|
<< toString(message.fileContainers());
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString toString(const UnregisterUnsavedFilesForEditorMessage &)
|
|
|
|
|
{
|
|
|
|
|
return QLatin1String("UnregisterUnsavedFilesForEditorMessage\n");
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-19 12:36:43 +02:00
|
|
|
QString toString(const CompleteCodeMessage &)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-08-19 12:36:43 +02:00
|
|
|
return QLatin1String("CompleteCodeMessage\n");
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-31 16:28:26 +02:00
|
|
|
QString toString(const RequestDiagnosticsMessage &)
|
|
|
|
|
{
|
|
|
|
|
return QStringLiteral("RequestDiagnosticsMessage\n");
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-18 17:07:44 +01:00
|
|
|
QString toString(const RequestHighlightingMessage &)
|
|
|
|
|
{
|
|
|
|
|
return QStringLiteral("RequestHighlightingMessage\n");
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-01 11:57:08 +01:00
|
|
|
QString toString(const UpdateVisibleTranslationUnitsMessage &)
|
|
|
|
|
{
|
|
|
|
|
return QStringLiteral("UpdateVisibleTranslationUnitsMessage\n");
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
class IpcSenderSpy : public IpcSenderInterface
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
void end() override
|
2015-08-19 12:36:43 +02:00
|
|
|
{ senderLog.append(toString(EndMessage())); }
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
void registerTranslationUnitsForEditor(const RegisterTranslationUnitForEditorMessage &message) override
|
2015-08-19 12:36:43 +02:00
|
|
|
{ senderLog.append(toString(message)); }
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-10-13 15:56:41 +02:00
|
|
|
void updateTranslationUnitsForEditor(const UpdateTranslationUnitsForEditorMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
|
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
void unregisterTranslationUnitsForEditor(const UnregisterTranslationUnitsForEditorMessage &message) override
|
2015-08-19 12:36:43 +02:00
|
|
|
{ senderLog.append(toString(message)); }
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
void registerProjectPartsForEditor(const RegisterProjectPartsForEditorMessage &message) override
|
2015-08-19 12:36:43 +02:00
|
|
|
{ senderLog.append(toString(message)); }
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-08-31 14:36:58 +02:00
|
|
|
void unregisterProjectPartsForEditor(const UnregisterProjectPartsForEditorMessage &message) override
|
2015-08-19 12:36:43 +02:00
|
|
|
{ senderLog.append(toString(message)); }
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-08-31 16:10:36 +02:00
|
|
|
void registerUnsavedFilesForEditor(const RegisterUnsavedFilesForEditorMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
|
|
|
|
|
|
|
|
|
void unregisterUnsavedFilesForEditor(const UnregisterUnsavedFilesForEditorMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
|
|
|
|
|
2015-08-19 12:36:43 +02:00
|
|
|
void completeCode(const CompleteCodeMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-08-31 16:28:26 +02:00
|
|
|
void requestDiagnostics(const RequestDiagnosticsMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
|
|
|
|
|
2015-11-18 17:07:44 +01:00
|
|
|
void requestHighlighting(const RequestHighlightingMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
2015-08-31 16:28:26 +02:00
|
|
|
|
2015-12-01 11:57:08 +01:00
|
|
|
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override
|
|
|
|
|
{ senderLog.append(toString(message)); }
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
public:
|
|
|
|
|
QString senderLog;
|
|
|
|
|
};
|
|
|
|
|
|
2016-01-13 14:12:15 +01:00
|
|
|
const CppTools::ProjectPartHeaderPaths toHeaderPaths(const QStringList &paths)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
using namespace CppTools;
|
|
|
|
|
|
2016-01-13 14:12:15 +01:00
|
|
|
ProjectPartHeaderPaths result;
|
2015-05-08 15:48:17 +02:00
|
|
|
foreach (const QString &path, paths)
|
2016-01-13 14:12:15 +01:00
|
|
|
result << ProjectPartHeaderPath(path, ProjectPartHeaderPath::IncludePath);
|
2015-05-08 15:48:17 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using ProposalModel = QSharedPointer<TextEditor::IAssistProposalModel>;
|
|
|
|
|
|
|
|
|
|
ProposalModel completionResults(
|
|
|
|
|
TextEditor::BaseTextEditor *textEditor,
|
|
|
|
|
const QStringList &includePaths = QStringList())
|
|
|
|
|
{
|
|
|
|
|
using namespace TextEditor;
|
|
|
|
|
|
|
|
|
|
TextEditorWidget *textEditorWidget = qobject_cast<TextEditorWidget *>(textEditor->widget());
|
|
|
|
|
QTC_ASSERT(textEditorWidget, return ProposalModel());
|
|
|
|
|
AssistInterface *assistInterface = textEditorWidget->createAssistInterface(
|
|
|
|
|
TextEditor::Completion, TextEditor::ExplicitlyInvoked);
|
|
|
|
|
QTC_ASSERT(assistInterface, return ProposalModel());
|
|
|
|
|
if (!includePaths.isEmpty()) {
|
|
|
|
|
auto clangAssistInterface = static_cast<ClangCompletionAssistInterface *>(assistInterface);
|
|
|
|
|
clangAssistInterface->setHeaderPaths(toHeaderPaths(includePaths));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompletionAssistProvider *assistProvider
|
|
|
|
|
= textEditor->textDocument()->completionAssistProvider();
|
|
|
|
|
QTC_ASSERT(qobject_cast<ClangCompletionAssistProvider *>(assistProvider),
|
|
|
|
|
return ProposalModel());
|
|
|
|
|
QTC_ASSERT(assistProvider, return ProposalModel());
|
|
|
|
|
QTC_ASSERT(assistProvider->runType() == IAssistProvider::Asynchronous, return ProposalModel());
|
|
|
|
|
|
|
|
|
|
IAssistProcessor *processor = assistProvider->createProcessor();
|
|
|
|
|
QTC_ASSERT(processor, return ProposalModel());
|
|
|
|
|
|
|
|
|
|
WaitForAsyncCompletions waitForCompletions;
|
|
|
|
|
const WaitForAsyncCompletions::WaitResult result = waitForCompletions.wait(processor,
|
|
|
|
|
assistInterface);
|
|
|
|
|
QTC_ASSERT(result == WaitForAsyncCompletions::GotResults, return ProposalModel());
|
|
|
|
|
return QSharedPointer<TextEditor::IAssistProposalModel>(waitForCompletions.proposalModel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class TestDocument
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TestDocument(const QByteArray &fileName, CppTools::Tests::TemporaryDir *temporaryDir = 0)
|
|
|
|
|
: cursorPosition(-1)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!fileName.isEmpty(), return);
|
|
|
|
|
const QResource resource(qrcPath(fileName));
|
|
|
|
|
QTC_ASSERT(resource.isValid(), return);
|
|
|
|
|
const QByteArray contents = QByteArray(reinterpret_cast<const char*>(resource.data()),
|
|
|
|
|
resource.size());
|
|
|
|
|
cursorPosition = findCursorMarkerPosition(contents);
|
|
|
|
|
if (!contents.isEmpty()) {
|
|
|
|
|
if (!temporaryDir) {
|
|
|
|
|
m_temporaryDir.reset(new CppTools::Tests::TemporaryDir);
|
|
|
|
|
temporaryDir = m_temporaryDir.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filePath = temporaryDir->createFile(fileName, contents);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static TestDocument fromExistingFile(const QString &filePath)
|
|
|
|
|
{
|
|
|
|
|
TestDocument testDocument;
|
|
|
|
|
QTC_ASSERT(!filePath.isEmpty(), return testDocument);
|
|
|
|
|
testDocument.filePath = filePath;
|
|
|
|
|
testDocument.cursorPosition = findCursorMarkerPosition(readFile(filePath));
|
|
|
|
|
return testDocument;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int findCursorMarkerPosition(const QByteArray &contents)
|
|
|
|
|
{
|
|
|
|
|
return contents.indexOf(" /* COMPLETE HERE */");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isCreated() const { return !filePath.isEmpty(); }
|
|
|
|
|
bool hasValidCursorPosition() const { return cursorPosition >= 0; }
|
|
|
|
|
bool isCreatedAndHasValidCursorPosition() const
|
|
|
|
|
{ return isCreated() && hasValidCursorPosition(); }
|
|
|
|
|
|
|
|
|
|
QString filePath;
|
|
|
|
|
int cursorPosition;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
TestDocument() : cursorPosition(-1) {}
|
|
|
|
|
QSharedPointer<CppTools::Tests::TemporaryDir> m_temporaryDir;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class OpenEditorAtCursorPosition
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
OpenEditorAtCursorPosition(const TestDocument &testDocument);
|
|
|
|
|
~OpenEditorAtCursorPosition(); // Close editor
|
|
|
|
|
|
2015-07-14 19:01:32 +02:00
|
|
|
bool succeeded() const { return m_editor && m_backendIsNotified; }
|
|
|
|
|
bool waitUntilBackendIsNotified(int timeout = 10000);
|
2015-09-28 17:42:43 +02:00
|
|
|
bool waitUntilProjectPartChanged(const QString &newProjectPartId, int timeout = 10000);
|
|
|
|
|
bool waitUntil(const std::function<bool()> &condition, int timeout);
|
2015-05-08 15:48:17 +02:00
|
|
|
TextEditor::BaseTextEditor *editor() const { return m_editor; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
TextEditor::BaseTextEditor *m_editor;
|
2015-07-14 19:01:32 +02:00
|
|
|
bool m_backendIsNotified = false;
|
2015-05-08 15:48:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
OpenEditorAtCursorPosition::OpenEditorAtCursorPosition(const TestDocument &testDocument)
|
|
|
|
|
{
|
|
|
|
|
Core::IEditor *coreEditor = Core::EditorManager::openEditor(testDocument.filePath);
|
|
|
|
|
m_editor = qobject_cast<TextEditor::BaseTextEditor *>(coreEditor);
|
|
|
|
|
QTC_CHECK(m_editor);
|
|
|
|
|
if (m_editor && testDocument.hasValidCursorPosition())
|
|
|
|
|
m_editor->setCursorPosition(testDocument.cursorPosition);
|
2015-07-14 19:01:32 +02:00
|
|
|
m_backendIsNotified = waitUntilBackendIsNotified();
|
|
|
|
|
QTC_CHECK(m_backendIsNotified);
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OpenEditorAtCursorPosition::~OpenEditorAtCursorPosition()
|
|
|
|
|
{
|
|
|
|
|
if (m_editor)
|
|
|
|
|
Core::EditorManager::closeEditor(m_editor, /* askAboutModifiedEditors= */ false);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-28 17:42:43 +02:00
|
|
|
|
|
|
|
|
|
2015-07-14 19:01:32 +02:00
|
|
|
bool OpenEditorAtCursorPosition::waitUntilBackendIsNotified(int timeout)
|
|
|
|
|
{
|
2015-09-28 17:42:43 +02:00
|
|
|
const QString filePath = m_editor->document()->filePath().toString();
|
|
|
|
|
|
|
|
|
|
auto condition = [filePath] () {
|
|
|
|
|
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
|
|
|
|
|
return processor && processor->projectPart();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return waitUntil(condition, timeout);
|
|
|
|
|
}
|
2015-07-14 19:01:32 +02:00
|
|
|
|
2015-09-28 17:42:43 +02:00
|
|
|
bool OpenEditorAtCursorPosition::waitUntilProjectPartChanged(const QString &newProjectPartId,
|
|
|
|
|
int timeout)
|
|
|
|
|
{
|
2015-07-14 19:01:32 +02:00
|
|
|
const QString filePath = m_editor->document()->filePath().toString();
|
|
|
|
|
|
2015-09-28 17:42:43 +02:00
|
|
|
auto condition = [newProjectPartId, filePath] () {
|
|
|
|
|
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
|
|
|
|
|
return processor
|
|
|
|
|
&& processor->projectPart()
|
|
|
|
|
&& processor->projectPart()->id() == newProjectPartId;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return waitUntil(condition, timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OpenEditorAtCursorPosition::waitUntil(const std::function<bool ()> &condition, int timeout)
|
|
|
|
|
{
|
2015-07-14 19:01:32 +02:00
|
|
|
QTime time;
|
|
|
|
|
time.start();
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
if (time.elapsed() > timeout)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-09-28 17:42:43 +02:00
|
|
|
if (condition())
|
2015-07-14 19:01:32 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
QThread::msleep(20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
CppTools::ProjectPart::Ptr createProjectPart(const QStringList &files,
|
|
|
|
|
const QString &defines)
|
|
|
|
|
{
|
|
|
|
|
using namespace CppTools;
|
|
|
|
|
|
|
|
|
|
ProjectPart::Ptr projectPart(new ProjectPart);
|
|
|
|
|
projectPart->projectFile = QLatin1String("myproject.project");
|
|
|
|
|
foreach (const QString &file, files)
|
|
|
|
|
projectPart->files.append(ProjectFile(file, ProjectFile::classify(file)));
|
|
|
|
|
projectPart->languageVersion = ProjectPart::CXX11;
|
|
|
|
|
projectPart->qtVersion = ProjectPart::NoQt;
|
|
|
|
|
projectPart->projectDefines = defines.toUtf8();
|
|
|
|
|
|
|
|
|
|
return projectPart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CppTools::ProjectInfo createProjectInfo(ProjectExplorer::Project *project,
|
|
|
|
|
const QStringList &files,
|
|
|
|
|
const QString &defines)
|
|
|
|
|
{
|
|
|
|
|
using namespace CppTools;
|
|
|
|
|
QTC_ASSERT(project, return ProjectInfo());
|
|
|
|
|
|
|
|
|
|
const CppTools::ProjectPart::Ptr projectPart = createProjectPart(files, defines);
|
|
|
|
|
ProjectInfo projectInfo = ProjectInfo(project);
|
|
|
|
|
projectInfo.appendProjectPart(projectPart);
|
|
|
|
|
projectInfo.finish();
|
|
|
|
|
return projectInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ProjectLoader
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ProjectLoader(const QStringList &projectFiles,
|
|
|
|
|
const QString &projectDefines,
|
|
|
|
|
bool testOnlyForCleanedProjects = false)
|
|
|
|
|
: m_project(0)
|
|
|
|
|
, m_projectFiles(projectFiles)
|
|
|
|
|
, m_projectDefines(projectDefines)
|
|
|
|
|
, m_helper(0, testOnlyForCleanedProjects)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool load()
|
|
|
|
|
{
|
|
|
|
|
m_project = m_helper.createProject(QLatin1String("testProject"));
|
|
|
|
|
const CppTools::ProjectInfo projectInfo = createProjectInfo(m_project,
|
|
|
|
|
m_projectFiles,
|
|
|
|
|
m_projectDefines);
|
|
|
|
|
const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo);
|
|
|
|
|
return m_projectFiles.size() == filesIndexedAfterLoading.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool updateProject(const QString &updatedProjectDefines)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_project, return false);
|
|
|
|
|
const CppTools::ProjectInfo updatedProjectInfo = createProjectInfo(m_project,
|
|
|
|
|
m_projectFiles,
|
|
|
|
|
updatedProjectDefines);
|
|
|
|
|
return updateProjectInfo(updatedProjectInfo);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool updateProjectInfo(const CppTools::ProjectInfo &projectInfo)
|
|
|
|
|
{
|
|
|
|
|
const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo);
|
|
|
|
|
return m_projectFiles.size() == filesIndexedAfterLoading.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProjectExplorer::Project *m_project;
|
|
|
|
|
QStringList m_projectFiles;
|
|
|
|
|
QString m_projectDefines;
|
|
|
|
|
CppTools::Tests::ModelManagerTestHelper m_helper;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ProjectLessCompletionTest
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ProjectLessCompletionTest(const QByteArray &testFileName,
|
|
|
|
|
const QStringList &includePaths = QStringList())
|
|
|
|
|
{
|
|
|
|
|
CppTools::Tests::TestCase garbageCollectionGlobalSnapshot;
|
|
|
|
|
QVERIFY(garbageCollectionGlobalSnapshot.succeededSoFar());
|
|
|
|
|
|
2015-07-17 11:18:42 +02:00
|
|
|
const TestDocument testDocument(testFileName, globalTemporaryDir());
|
2015-05-08 15:48:17 +02:00
|
|
|
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
OpenEditorAtCursorPosition openEditor(testDocument);
|
|
|
|
|
|
|
|
|
|
QVERIFY(openEditor.succeeded());
|
|
|
|
|
proposal = completionResults(openEditor.editor(), includePaths);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProposalModel proposal;
|
|
|
|
|
};
|
|
|
|
|
|
2015-08-07 10:58:17 +02:00
|
|
|
int indexOfItemWithText(ProposalModel model, const QByteArray &text)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
if (!model)
|
2015-08-07 10:58:17 +02:00
|
|
|
return -1;
|
2015-05-08 15:48:17 +02:00
|
|
|
|
|
|
|
|
for (int i = 0, size = model->size(); i < size; ++i) {
|
|
|
|
|
const QString itemText = model->text(i);
|
|
|
|
|
if (itemText == QString::fromUtf8(text))
|
2015-08-07 10:58:17 +02:00
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasItem(ProposalModel model, const QByteArray &text)
|
|
|
|
|
{
|
|
|
|
|
return indexOfItemWithText(model, text) != -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasItem(ProposalModel model, const QByteArray &text, const QByteArray &detail)
|
|
|
|
|
{
|
|
|
|
|
const int index = indexOfItemWithText(model, text);
|
|
|
|
|
if (index != -1 && index < model->size()) {
|
|
|
|
|
TextEditor::IAssistProposalModel *imodel = model.data();
|
|
|
|
|
const auto genericModel = static_cast<TextEditor::GenericProposalModel *>(imodel);
|
|
|
|
|
const auto itemDetail = genericModel->detail(index);
|
|
|
|
|
return itemDetail == QString::fromUtf8(detail);
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasSnippet(ProposalModel model, const QByteArray &text)
|
|
|
|
|
{
|
|
|
|
|
if (!model)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Snippets seem to end with a whitespace
|
|
|
|
|
const QString snippetText = QString::fromUtf8(text) + QLatin1Char(' ');
|
|
|
|
|
|
|
|
|
|
auto *genericModel = static_cast<TextEditor::GenericProposalModel *>(model.data());
|
|
|
|
|
for (int i = 0, size = genericModel->size(); i < size; ++i) {
|
|
|
|
|
TextEditor::AssistProposalItem *item = genericModel->proposalItem(i);
|
|
|
|
|
QTC_ASSERT(item, continue);
|
|
|
|
|
if (item->text() == snippetText)
|
|
|
|
|
return item->data().toString().contains(QLatin1Char('$'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-04 11:38:59 +02:00
|
|
|
class MonitorGeneratedUiFile : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
MonitorGeneratedUiFile();
|
|
|
|
|
bool waitUntilGenerated(int timeout = 10000) const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void onUiFileGenerated() { m_isGenerated = true; }
|
|
|
|
|
|
|
|
|
|
bool m_isGenerated = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MonitorGeneratedUiFile::MonitorGeneratedUiFile()
|
|
|
|
|
{
|
|
|
|
|
connect(CppTools::CppModelManager::instance(),
|
|
|
|
|
&CppTools::CppModelManager::abstractEditorSupportContentsUpdated,
|
|
|
|
|
this, &MonitorGeneratedUiFile::onUiFileGenerated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MonitorGeneratedUiFile::waitUntilGenerated(int timeout) const
|
|
|
|
|
{
|
|
|
|
|
if (m_isGenerated)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
QTime time;
|
|
|
|
|
time.start();
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
if (m_isGenerated)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (time.elapsed() > timeout)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
QThread::msleep(20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-10 10:39:05 +02:00
|
|
|
class WriteFileAndWaitForReloadedDocument : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteFileAndWaitForReloadedDocument(const QString &filePath,
|
|
|
|
|
const QByteArray &fileContents,
|
|
|
|
|
Core::IDocument *document)
|
|
|
|
|
: m_filePath(filePath)
|
|
|
|
|
, m_fileContents(fileContents)
|
|
|
|
|
{
|
|
|
|
|
QTC_CHECK(document);
|
|
|
|
|
connect(document, &Core::IDocument::reloadFinished,
|
|
|
|
|
this, &WriteFileAndWaitForReloadedDocument::onReloadFinished);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onReloadFinished()
|
|
|
|
|
{
|
|
|
|
|
m_onReloadFinished = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool wait() const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false);
|
|
|
|
|
|
|
|
|
|
QTime totalTime;
|
|
|
|
|
totalTime.start();
|
|
|
|
|
|
|
|
|
|
QTime writeFileAgainTime;
|
|
|
|
|
writeFileAgainTime.start();
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
if (m_onReloadFinished)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (totalTime.elapsed() > 10000)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-09-02 12:10:00 +02:00
|
|
|
if (writeFileAgainTime.elapsed() > 3000) {
|
2015-07-10 10:39:05 +02:00
|
|
|
// The timestamp did not change, try again now.
|
|
|
|
|
QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false);
|
|
|
|
|
writeFileAgainTime.restart();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
QThread::msleep(20);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool m_onReloadFinished = false;
|
|
|
|
|
QString m_filePath;
|
|
|
|
|
QByteArray m_fileContents;
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
namespace ClangCodeModel {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
namespace Tests {
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompleteDoxygenKeywords()
|
|
|
|
|
{
|
|
|
|
|
ProjectLessCompletionTest t("doxygenKeywordsCompletion.cpp");
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "brief"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "param"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "return"));
|
|
|
|
|
QVERIFY(!hasSnippet(t.proposal, "class"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompletePreprocessorKeywords()
|
|
|
|
|
{
|
|
|
|
|
ProjectLessCompletionTest t("preprocessorKeywordsCompletion.cpp");
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "ifdef"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "endif"));
|
|
|
|
|
QVERIFY(!hasSnippet(t.proposal, "class"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompleteIncludeDirective()
|
|
|
|
|
{
|
|
|
|
|
CppTools::Tests::TemporaryCopiedDir testDir(qrcPath("exampleIncludeDir"));
|
|
|
|
|
ProjectLessCompletionTest t("includeDirectiveCompletion.cpp", QStringList(testDir.path()));
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "file.h"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "otherFile.h"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "mylib/"));
|
|
|
|
|
QVERIFY(!hasSnippet(t.proposal, "class"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompleteGlobals()
|
|
|
|
|
{
|
|
|
|
|
ProjectLessCompletionTest t("globalCompletion.cpp");
|
|
|
|
|
|
2015-08-07 10:58:17 +02:00
|
|
|
QVERIFY(hasItem(t.proposal, "globalVariable", "int globalVariable"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "globalFunction", "void globalFunction ()"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "GlobalClass", "GlobalClass"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "class", "class")); // Keyword
|
2015-05-08 15:48:17 +02:00
|
|
|
QVERIFY(hasSnippet(t.proposal, "class")); // Snippet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompleteMembers()
|
|
|
|
|
{
|
|
|
|
|
ProjectLessCompletionTest t("memberCompletion.cpp");
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "member"));
|
|
|
|
|
QVERIFY(!hasItem(t.proposal, "globalVariable"));
|
|
|
|
|
QVERIFY(!hasItem(t.proposal, "class")); // Keyword
|
|
|
|
|
QVERIFY(!hasSnippet(t.proposal, "class")); // Snippet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompleteFunctions()
|
|
|
|
|
{
|
|
|
|
|
ProjectLessCompletionTest t("functionCompletion.cpp");
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "void f()"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "void f(int a)"));
|
2015-12-09 17:51:14 +01:00
|
|
|
QVERIFY(hasItem(t.proposal, "void f(const QString &s)"));
|
2015-12-09 14:30:18 +01:00
|
|
|
QVERIFY(hasItem(t.proposal, "void f(char c<i>, int optional</i>)")); // TODO: No default argument?
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "void f(char c<i>, int optional1, int optional2</i>)")); // TODO: No default argument?
|
2015-12-09 17:51:14 +01:00
|
|
|
QVERIFY(hasItem(t.proposal, "void f(const TType<QString> *t)"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "TType<QString> f(bool)"));
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-22 13:09:44 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteConstructorAndFallbackToGlobalCompletion()
|
|
|
|
|
{
|
|
|
|
|
ProjectLessCompletionTest t("constructorCompletion.cpp");
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "globalVariable"));
|
|
|
|
|
QVERIFY(hasItem(t.proposal, "GlobalClassWithCustomConstructor"));
|
|
|
|
|
QVERIFY(!hasSnippet(t.proposal, "class"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 15:24:59 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteProjectDependingCode()
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
const TestDocument testDocument("completionWithProject.cpp");
|
|
|
|
|
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
|
|
|
|
|
ProjectLoader projectLoader(QStringList(testDocument.filePath),
|
|
|
|
|
_("#define PROJECT_CONFIGURATION_1\n"));
|
|
|
|
|
QVERIFY(projectLoader.load());
|
|
|
|
|
|
|
|
|
|
OpenEditorAtCursorPosition openEditor(testDocument);
|
|
|
|
|
QVERIFY(openEditor.succeeded());
|
|
|
|
|
|
|
|
|
|
ProposalModel proposal = completionResults(openEditor.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "projectConfiguration1"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 15:24:59 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProject()
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
|
|
|
|
const TestDocument testDocument("completionWithProject.cpp");
|
|
|
|
|
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
|
|
|
|
|
OpenEditorAtCursorPosition openEditor(testDocument);
|
|
|
|
|
QVERIFY(openEditor.succeeded());
|
|
|
|
|
|
|
|
|
|
// Check completion without project
|
|
|
|
|
ProposalModel proposal = completionResults(openEditor.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "noProjectConfigurationDetected"));
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Check completion with project configuration 1
|
|
|
|
|
ProjectLoader projectLoader(QStringList(testDocument.filePath),
|
|
|
|
|
_("#define PROJECT_CONFIGURATION_1\n"),
|
|
|
|
|
/* testOnlyForCleanedProjects= */ true);
|
|
|
|
|
QVERIFY(projectLoader.load());
|
2015-09-28 17:42:43 +02:00
|
|
|
openEditor.waitUntilProjectPartChanged(QLatin1String("myproject.project"));
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
proposal = completionResults(openEditor.editor());
|
|
|
|
|
|
|
|
|
|
QVERIFY(hasItem(proposal, "projectConfiguration1"));
|
|
|
|
|
QVERIFY(!hasItem(proposal, "projectConfiguration2"));
|
|
|
|
|
|
|
|
|
|
// Check completion with project configuration 2
|
|
|
|
|
QVERIFY(projectLoader.updateProject(_("#define PROJECT_CONFIGURATION_2\n")));
|
|
|
|
|
proposal = completionResults(openEditor.editor());
|
|
|
|
|
|
|
|
|
|
QVERIFY(!hasItem(proposal, "projectConfiguration1"));
|
|
|
|
|
QVERIFY(hasItem(proposal, "projectConfiguration2"));
|
|
|
|
|
} // Project closed
|
|
|
|
|
|
|
|
|
|
// Check again completion without project
|
2015-09-18 12:19:46 +02:00
|
|
|
openEditor.waitUntilProjectPartChanged(QLatin1String(""));
|
2015-05-08 15:48:17 +02:00
|
|
|
proposal = completionResults(openEditor.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "noProjectConfigurationDetected"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 15:24:59 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteProjectDependingCodeInGeneratedUiFile()
|
|
|
|
|
{
|
|
|
|
|
CppTools::Tests::TemporaryCopiedDir testDir(qrcPath("qt-widgets-app"));
|
|
|
|
|
QVERIFY(testDir.isValid());
|
|
|
|
|
|
|
|
|
|
MonitorGeneratedUiFile monitorGeneratedUiFile;
|
|
|
|
|
|
|
|
|
|
// Open project
|
|
|
|
|
const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro");
|
|
|
|
|
CppTools::Tests::ProjectOpenerAndCloser projectManager;
|
|
|
|
|
const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
|
|
|
|
|
QVERIFY(projectInfo.isValid());
|
2015-09-15 11:48:25 +02:00
|
|
|
QVERIFY(monitorGeneratedUiFile.waitUntilGenerated());
|
2015-09-16 15:24:59 +02:00
|
|
|
|
|
|
|
|
// Open file with ui object
|
|
|
|
|
const QString completionFile = testDir.absolutePath("mainwindow.cpp");
|
|
|
|
|
const TestDocument testDocument = TestDocument::fromExistingFile(completionFile);
|
|
|
|
|
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
OpenEditorAtCursorPosition openSource(testDocument);
|
|
|
|
|
QVERIFY(openSource.succeeded());
|
|
|
|
|
|
|
|
|
|
// ...and check comletions
|
|
|
|
|
ProposalModel proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "menuBar"));
|
|
|
|
|
QVERIFY(hasItem(proposal, "statusBar"));
|
|
|
|
|
QVERIFY(hasItem(proposal, "centralWidget"));
|
|
|
|
|
QVERIFY(hasItem(proposal, "setupUi"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCodeCompletionTest::testCompleteAfterModifyingIncludedHeaderInOtherEditor()
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-12-02 13:31:07 +01:00
|
|
|
QSKIP("We don't reparse anymore before a code completion so we get wrong completion results.");
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
CppTools::Tests::TemporaryDir temporaryDir;
|
|
|
|
|
const TestDocument sourceDocument("mysource.cpp", &temporaryDir);
|
|
|
|
|
QVERIFY(sourceDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
const TestDocument headerDocument("myheader.h", &temporaryDir);
|
|
|
|
|
QVERIFY(headerDocument.isCreated());
|
|
|
|
|
|
|
|
|
|
// Test that declarations from header file are visible in source file
|
|
|
|
|
OpenEditorAtCursorPosition openSource(sourceDocument);
|
|
|
|
|
QVERIFY(openSource.succeeded());
|
|
|
|
|
ProposalModel proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeader"));
|
|
|
|
|
|
|
|
|
|
// Open header and insert a new declaration
|
|
|
|
|
OpenEditorAtCursorPosition openHeader(headerDocument);
|
|
|
|
|
QVERIFY(openHeader.succeeded());
|
|
|
|
|
insertTextAtTopOfEditor(openHeader.editor(), "int globalFromHeaderUnsaved;\n");
|
|
|
|
|
|
|
|
|
|
// Switch back to source file and check if modified header is reflected in completions.
|
|
|
|
|
Core::EditorManager::activateEditor(openSource.editor());
|
2015-12-01 11:57:08 +01:00
|
|
|
QCoreApplication::processEvents(); // connections are queued
|
2015-05-08 15:48:17 +02:00
|
|
|
proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeader"));
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeaderUnsaved"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 15:24:59 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteAfterModifyingIncludedHeaderByRefactoringActions()
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-12-02 13:31:07 +01:00
|
|
|
QSKIP("We don't reparse anymore before a code completion so we get wrong completion results.");
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
CppTools::Tests::TemporaryDir temporaryDir;
|
|
|
|
|
const TestDocument sourceDocument("mysource.cpp", &temporaryDir);
|
|
|
|
|
QVERIFY(sourceDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
const TestDocument headerDocument("myheader.h", &temporaryDir);
|
|
|
|
|
QVERIFY(headerDocument.isCreated());
|
|
|
|
|
|
|
|
|
|
// Open header
|
|
|
|
|
OpenEditorAtCursorPosition openHeader(headerDocument);
|
|
|
|
|
QVERIFY(openHeader.succeeded());
|
|
|
|
|
|
|
|
|
|
// Open source and test that declaration from header file is visible in source file
|
|
|
|
|
OpenEditorAtCursorPosition openSource(sourceDocument);
|
|
|
|
|
QVERIFY(openSource.succeeded());
|
|
|
|
|
ProposalModel proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeader"));
|
|
|
|
|
|
|
|
|
|
// Modify header document without switching to its editor.
|
|
|
|
|
// This simulates e.g. changes from refactoring actions.
|
2015-11-27 16:02:38 +01:00
|
|
|
::Utils::ChangeSet cs;
|
2015-05-08 15:48:17 +02:00
|
|
|
cs.insert(0, QLatin1String("int globalFromHeaderUnsaved;\n"));
|
|
|
|
|
QTextCursor textCursor = openHeader.editor()->textCursor();
|
|
|
|
|
cs.apply(&textCursor);
|
|
|
|
|
|
|
|
|
|
// Check whether modified header is reflected in the completions.
|
|
|
|
|
proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeader"));
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeaderUnsaved"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 15:24:59 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteAfterChangingIncludedAndOpenHeaderExternally()
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-09-28 17:42:43 +02:00
|
|
|
QSKIP("The file system watcher is doing it in backend process but we wait not long enough");
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
ChangeDocumentReloadSetting reloadSettingsChanger(Core::IDocument::ReloadUnmodified);
|
|
|
|
|
|
|
|
|
|
CppTools::Tests::TemporaryDir temporaryDir;
|
|
|
|
|
const TestDocument sourceDocument("mysource.cpp", &temporaryDir);
|
|
|
|
|
QVERIFY(sourceDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
const TestDocument headerDocument("myheader.h", &temporaryDir);
|
|
|
|
|
QVERIFY(headerDocument.isCreated());
|
|
|
|
|
|
|
|
|
|
// Open header
|
|
|
|
|
OpenEditorAtCursorPosition openHeader(headerDocument);
|
|
|
|
|
QVERIFY(openHeader.succeeded());
|
|
|
|
|
|
|
|
|
|
// Open source and test completions
|
|
|
|
|
OpenEditorAtCursorPosition openSource(sourceDocument);
|
|
|
|
|
QVERIFY(openSource.succeeded());
|
|
|
|
|
ProposalModel proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeader"));
|
|
|
|
|
|
2015-07-10 10:39:05 +02:00
|
|
|
// Simulate external modification and wait for reload
|
|
|
|
|
WriteFileAndWaitForReloadedDocument waitForReloadedDocument(
|
|
|
|
|
headerDocument.filePath,
|
|
|
|
|
"int globalFromHeaderReloaded;\n",
|
|
|
|
|
openHeader.editor()->document());
|
2015-05-08 15:48:17 +02:00
|
|
|
QVERIFY(waitForReloadedDocument.wait());
|
|
|
|
|
|
|
|
|
|
// Retrigger completion and check if its updated
|
|
|
|
|
proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeaderReloaded"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 15:24:59 +02:00
|
|
|
void ClangCodeCompletionTest::testCompleteAfterChangingIncludedAndNotOpenHeaderExternally()
|
2015-09-16 15:41:50 +02:00
|
|
|
{
|
|
|
|
|
QSKIP("The file system watcher is doing it in backend process but we wait not long enough");
|
|
|
|
|
|
|
|
|
|
CppTools::Tests::TemporaryDir temporaryDir;
|
|
|
|
|
const TestDocument sourceDocument("mysource.cpp", &temporaryDir);
|
|
|
|
|
QVERIFY(sourceDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
const TestDocument headerDocument("myheader.h", &temporaryDir);
|
|
|
|
|
QVERIFY(headerDocument.isCreated());
|
|
|
|
|
|
|
|
|
|
// Open source and test completions
|
|
|
|
|
OpenEditorAtCursorPosition openSource(sourceDocument);
|
|
|
|
|
QVERIFY(openSource.succeeded());
|
|
|
|
|
ProposalModel proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeader"));
|
|
|
|
|
|
|
|
|
|
// Simulate external modification, e.g version control checkout
|
|
|
|
|
QVERIFY(writeFile(headerDocument.filePath, "int globalFromHeaderReloaded;\n"));
|
|
|
|
|
|
|
|
|
|
// Retrigger completion and check if its updated
|
|
|
|
|
proposal = completionResults(openSource.editor());
|
|
|
|
|
QVERIFY(hasItem(proposal, "globalFromHeaderReloaded"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
void ClangCodeCompletionTest::testUpdateBackendAfterRestart()
|
|
|
|
|
{
|
2015-08-31 16:22:28 +02:00
|
|
|
QSKIP("Must be rewritten with a more robust approach instead of sender log!");
|
2015-05-08 15:48:17 +02:00
|
|
|
IpcSenderSpy spy;
|
|
|
|
|
ChangeIpcSender changeIpcSender(&spy);
|
|
|
|
|
|
|
|
|
|
CppTools::Tests::TemporaryCopiedDir testDir(qrcPath("qt-widgets-app"));
|
|
|
|
|
QVERIFY(testDir.isValid());
|
|
|
|
|
|
|
|
|
|
// Open file not part of any project...
|
|
|
|
|
const TestDocument headerDocument("myheader.h", &testDir);
|
|
|
|
|
QVERIFY(headerDocument.isCreated());
|
|
|
|
|
OpenEditorAtCursorPosition openHeader(headerDocument);
|
|
|
|
|
QVERIFY(openHeader.succeeded());
|
|
|
|
|
// ... and modify it, so we have an unsaved file.
|
|
|
|
|
insertTextAtTopOfEditor(openHeader.editor(), "int someGlobal;\n");
|
|
|
|
|
// Open project ...
|
2015-06-04 11:38:59 +02:00
|
|
|
MonitorGeneratedUiFile monitorGeneratedUiFile;
|
2015-05-08 15:48:17 +02:00
|
|
|
const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro");
|
|
|
|
|
CppTools::Tests::ProjectOpenerAndCloser projectManager;
|
|
|
|
|
const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
|
|
|
|
|
QVERIFY(projectInfo.isValid());
|
2015-07-14 19:01:32 +02:00
|
|
|
QVERIFY(monitorGeneratedUiFile.waitUntilGenerated());
|
2015-05-08 15:48:17 +02:00
|
|
|
// ...and a file of the project
|
|
|
|
|
const QString completionFile = testDir.absolutePath("mainwindow.cpp");
|
|
|
|
|
const TestDocument testDocument = TestDocument::fromExistingFile(completionFile);
|
|
|
|
|
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
|
|
|
|
|
OpenEditorAtCursorPosition openSource(testDocument);
|
|
|
|
|
QVERIFY(openSource.succeeded());
|
|
|
|
|
|
2015-08-19 12:36:43 +02:00
|
|
|
// Check messages that would have been sent
|
2015-05-08 15:48:17 +02:00
|
|
|
QVERIFY(compare(LogOutput(spy.senderLog),
|
|
|
|
|
LogOutput(
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-07-14 19:01:32 +02:00
|
|
|
" Path: myheader.h ProjectPart: \n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-08-31 16:28:26 +02:00
|
|
|
" Path: myheader.h ProjectPart: \n"
|
|
|
|
|
"RequestDiagnosticsMessage\n"
|
2015-07-14 17:30:17 +02:00
|
|
|
" ProjectPartContainer id: qt-widgets-app.pro qt-widgets-app\n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-08-31 16:28:26 +02:00
|
|
|
" Path: myheader.h ProjectPart: \n"
|
|
|
|
|
"RequestDiagnosticsMessage\n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-08-31 16:28:26 +02:00
|
|
|
" Path: myheader.h ProjectPart: \n"
|
|
|
|
|
"RequestDiagnosticsMessage\n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-07-14 19:01:32 +02:00
|
|
|
" Path: ui_mainwindow.h ProjectPart: \n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-05-08 15:48:17 +02:00
|
|
|
" Path: myheader.h ProjectPart: \n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-07-14 19:01:32 +02:00
|
|
|
" Path: mainwindow.cpp ProjectPart: qt-widgets-app.pro qt-widgets-app\n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-08-31 16:28:26 +02:00
|
|
|
" Path: mainwindow.cpp ProjectPart: qt-widgets-app.pro qt-widgets-app\n"
|
|
|
|
|
"RequestDiagnosticsMessage\n"
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
)));
|
|
|
|
|
spy.senderLog.clear();
|
|
|
|
|
|
|
|
|
|
// Kill backend process...
|
2015-07-06 11:15:12 +02:00
|
|
|
auto &ipcCommunicator = ModelManagerSupportClang::instance_forTestsOnly()->ipcCommunicator();
|
2015-06-16 13:59:08 +02:00
|
|
|
ipcCommunicator.killBackendProcess();
|
|
|
|
|
QSignalSpy waitForReinitializedBackend(&ipcCommunicator,
|
2015-05-08 15:48:17 +02:00
|
|
|
SIGNAL(backendReinitialized()));
|
|
|
|
|
QVERIFY(waitForReinitializedBackend.wait());
|
|
|
|
|
|
|
|
|
|
// ...and check if code model backend would have been provided with current data
|
|
|
|
|
QVERIFY(compare(LogOutput(spy.senderLog),
|
|
|
|
|
LogOutput(
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterProjectPartsForEditorMessage\n"
|
2015-05-08 15:48:17 +02:00
|
|
|
" ProjectPartContainer id: \n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterProjectPartsForEditorMessage\n"
|
2015-07-14 17:30:17 +02:00
|
|
|
" ProjectPartContainer id: qt-widgets-app.pro qt-widgets-app\n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-05-08 15:48:17 +02:00
|
|
|
" Path: myheader.h ProjectPart: \n"
|
2015-08-31 14:36:58 +02:00
|
|
|
"RegisterTranslationUnitForEditorMessage\n"
|
2015-05-08 15:48:17 +02:00
|
|
|
" Path: ui_mainwindow.h ProjectPart: \n"
|
|
|
|
|
)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Tests
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangCodeModel
|
2015-06-04 11:38:59 +02:00
|
|
|
|
|
|
|
|
#include "clangcodecompletion_test.moc"
|