Files
qt-creator/tests/unit/unittest/clangipcservertest.cpp
Marco Bubke d027cab44e Clang: Rename Ipc* in ClangCodeModel*
We want to share more functionality of the IPC mechanism and for what we
need more interface classes. But we use this names already for the
ClangCodeModel implementation. So we rename the them to ClangCodeModel*.

Change-Id: Ie320e0d3b993586a9bcc6a5aa0d32427af41202e
Reviewed-by: Tim Jenssen <tim.jenssen@theqtcompany.com>
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
2016-06-30 09:28:56 +00:00

501 lines
22 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "mockclangcodemodelclient.h"
#include <clangcodemodelserver.h>
#include <highlightingchangedmessage.h>
#include <highlightingmarkcontainer.h>
#include <clangcodemodelclientproxy.h>
#include <clangcodemodelserverproxy.h>
#include <requesthighlightingmessage.h>
#include <translationunitdoesnotexistexception.h>
#include <translationunitparseerrorexception.h>
#include <cmbcodecompletedmessage.h>
#include <cmbcompletecodemessage.h>
#include <cmbechomessage.h>
#include <cmbregisterprojectsforeditormessage.h>
#include <cmbregistertranslationunitsforeditormessage.h>
#include <cmbunregisterprojectsforeditormessage.h>
#include <cmbunregistertranslationunitsforeditormessage.h>
#include <highlightingchangedmessage.h>
#include <diagnosticschangedmessage.h>
#include <projectpartsdonotexistmessage.h>
#include <translationunitdoesnotexistmessage.h>
#include <updatetranslationunitsforeditormessage.h>
#include <updatevisibletranslationunitsmessage.h>
#include <QBuffer>
#include <QFile>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#include "gtest-qt-printing.h"
using testing::Property;
using testing::Contains;
using testing::Not;
using testing::Eq;
using testing::PrintToString;
using testing::_;
namespace {
using ClangBackEnd::RegisterTranslationUnitForEditorMessage;
using ClangBackEnd::UnregisterTranslationUnitsForEditorMessage;
using ClangBackEnd::RegisterProjectPartsForEditorMessage;
using ClangBackEnd::UnregisterProjectPartsForEditorMessage;
using ClangBackEnd::CompleteCodeMessage;
using ClangBackEnd::CodeCompletedMessage;
using ClangBackEnd::CodeCompletion;
using ClangBackEnd::FileContainer;
using ClangBackEnd::ProjectPartContainer;
using ClangBackEnd::TranslationUnitDoesNotExistMessage;
using ClangBackEnd::ProjectPartsDoNotExistMessage;
using ClangBackEnd::UpdateTranslationUnitsForEditorMessage;
using ClangBackEnd::UpdateVisibleTranslationUnitsMessage;
using ClangBackEnd::RequestHighlightingMessage;
using ClangBackEnd::HighlightingChangedMessage;
using ClangBackEnd::HighlightingMarkContainer;
using ClangBackEnd::HighlightingTypes;
MATCHER_P5(HasDirtyTranslationUnit,
filePath,
projectPartId,
documentRevision,
isNeedingReparse,
hasNewDiagnostics,
std::string(negation ? "isn't" : "is")
+ " translation unit with file path "+ PrintToString(filePath)
+ " and project " + PrintToString(projectPartId)
+ " and document revision " + PrintToString(documentRevision)
+ " and isNeedingReparse = " + PrintToString(isNeedingReparse)
+ " and hasNewDiagnostics = " + PrintToString(hasNewDiagnostics)
)
{
auto &&translationUnits = arg.translationUnitsForTestOnly();
try {
auto translationUnit = translationUnits.translationUnit(filePath, projectPartId);
if (translationUnit.documentRevision() == documentRevision) {
if (translationUnit.hasNewDiagnostics() && !hasNewDiagnostics) {
*result_listener << "hasNewDiagnostics is true";
return false;
} else if (!translationUnit.hasNewDiagnostics() && hasNewDiagnostics) {
*result_listener << "hasNewDiagnostics is false";
return false;
}
if (translationUnit.isNeedingReparse() && !isNeedingReparse) {
*result_listener << "isNeedingReparse is true";
return false;
} else if (!translationUnit.isNeedingReparse() && isNeedingReparse) {
*result_listener << "isNeedingReparse is false";
return false;
}
return true;
}
*result_listener << "revision number is " << PrintToString(translationUnit.documentRevision());
return false;
} catch (...) {
*result_listener << "has no translation unit";
return false;
}
}
class ClangClangCodeModelServer : public ::testing::Test
{
protected:
void SetUp() override;
void registerFiles();
void registerProjectPart();
void changeProjectPartArguments();
void changeProjectPartArgumentsToWrongValues();
void updateVisibilty(const Utf8String &currentEditor, const Utf8String &additionalVisibleEditor);
static const Utf8String unsavedContent(const QString &unsavedFilePath);
protected:
MockClangCodeModelClient mockClangCodeModelClient;
ClangBackEnd::ClangCodeModelServer clangServer;
const ClangBackEnd::TranslationUnits &translationUnits = clangServer.translationUnitsForTestOnly();
const Utf8String projectPartId = Utf8StringLiteral("pathToProjectPart.pro");
const Utf8String functionTestFilePath = Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_function.cpp");
const Utf8String variableTestFilePath = Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_variable.cpp");
const QString unsavedTestFilePath = QStringLiteral(TESTDATA_DIR) + QStringLiteral("/complete_extractor_function_unsaved.cpp");
const QString updatedUnsavedTestFilePath = QStringLiteral(TESTDATA_DIR) + QStringLiteral("/complete_extractor_function_unsaved_2.cpp");
const Utf8String parseErrorTestFilePath = Utf8StringLiteral(TESTDATA_DIR"/complete_translationunit_parse_error.cpp");
};
TEST_F(ClangClangCodeModelServer, GetCodeCompletion)
{
CompleteCodeMessage completeCodeMessage(functionTestFilePath,
20,
1,
projectPartId);
CodeCompletion codeCompletion(Utf8StringLiteral("Function"),
34,
CodeCompletion::FunctionCompletionKind);
EXPECT_CALL(mockClangCodeModelClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion))))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, RequestHighlighting)
{
RequestHighlightingMessage requestHighlightingMessage({variableTestFilePath, projectPartId});
HighlightingTypes types;
types.mainHighlightingType = ClangBackEnd::HighlightingType::Function;
types.mixinHighlightingTypes.push_back(ClangBackEnd::HighlightingType::Declaration);
HighlightingMarkContainer highlightingMarkContainer(1, 6, 8, types);
EXPECT_CALL(mockClangCodeModelClient, highlightingChanged(Property(&HighlightingChangedMessage::highlightingMarks, Contains(highlightingMarkContainer))))
.Times(1);
clangServer.requestHighlighting(requestHighlightingMessage);
}
TEST_F(ClangClangCodeModelServer, GetCodeCompletionDependingOnArgumets)
{
CompleteCodeMessage completeCodeMessage(variableTestFilePath,
35,
1,
projectPartId);
CodeCompletion codeCompletion(Utf8StringLiteral("ArgumentDefinitionVariable"),
34,
CodeCompletion::VariableCompletionKind);
EXPECT_CALL(mockClangCodeModelClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion))))
.Times(1);
changeProjectPartArguments();
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetTranslationUnitDoesNotExistForEditorOnNonExistingTranslationUnit)
{
CompleteCodeMessage completeCodeMessage(Utf8StringLiteral("dontexists.cpp"),
34,
1,
projectPartId);
TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(Utf8StringLiteral("dontexists.cpp"), projectPartId);
EXPECT_CALL(mockClangCodeModelClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetTranslationUnitDoesNotExistForCompletingUnregisteredFile)
{
CompleteCodeMessage completeCodeMessage(parseErrorTestFilePath,
20,
1,
projectPartId);
TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(parseErrorTestFilePath, projectPartId);
EXPECT_CALL(mockClangCodeModelClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetCodeCompletionForUnsavedFile)
{
CompleteCodeMessage completeCodeMessage(functionTestFilePath,
20,
1,
projectPartId);
CodeCompletion codeCompletion(Utf8StringLiteral("Method2"),
34,
CodeCompletion::FunctionCompletionKind);
EXPECT_CALL(mockClangCodeModelClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion))))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetNoCodeCompletionAfterRemovingUnsavedFile)
{
clangServer.updateTranslationUnitsForEditor(UpdateTranslationUnitsForEditorMessage(
{FileContainer(functionTestFilePath, projectPartId, Utf8StringVector(), 74)}));
CompleteCodeMessage completeCodeMessage(functionTestFilePath,
20,
1,
projectPartId);
CodeCompletion codeCompletion(Utf8StringLiteral("Method2"),
34,
CodeCompletion::FunctionCompletionKind);
EXPECT_CALL(mockClangCodeModelClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Not(Contains(codeCompletion)))))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetNewCodeCompletionAfterUpdatingUnsavedFile)
{
clangServer.updateTranslationUnitsForEditor(UpdateTranslationUnitsForEditorMessage({{functionTestFilePath,
projectPartId,
unsavedContent(updatedUnsavedTestFilePath),
true,
74}}));
CompleteCodeMessage completeCodeMessage(functionTestFilePath,
20,
1,
projectPartId);
CodeCompletion codeCompletion(Utf8StringLiteral("Method3"),
34,
CodeCompletion::FunctionCompletionKind);
EXPECT_CALL(mockClangCodeModelClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion))))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetTranslationUnitDoesNotExistForUnregisterTranslationUnitWithWrongFilePath)
{
FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), projectPartId);
UnregisterTranslationUnitsForEditorMessage message({fileContainer});
TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(fileContainer);
EXPECT_CALL(mockClangCodeModelClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage))
.Times(1);
clangServer.unregisterTranslationUnitsForEditor(message);
}
TEST_F(ClangClangCodeModelServer, UnregisterTranslationUnitAndTestFailingCompletion)
{
FileContainer fileContainer(functionTestFilePath, projectPartId);
UnregisterTranslationUnitsForEditorMessage message({fileContainer});
clangServer.unregisterTranslationUnitsForEditor(message);
CompleteCodeMessage completeCodeMessage(functionTestFilePath,
20,
1,
projectPartId);
TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(fileContainer);
EXPECT_CALL(mockClangCodeModelClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetProjectPartDoesNotExistUnregisterProjectPartInexistingProjectPart)
{
Utf8StringVector inexistingProjectPartFilePath = {Utf8StringLiteral("projectpartsdoesnotexist.pro"), Utf8StringLiteral("project2doesnotexists.pro")};
UnregisterProjectPartsForEditorMessage unregisterProjectPartsForEditorMessage(inexistingProjectPartFilePath);
ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage(inexistingProjectPartFilePath);
EXPECT_CALL(mockClangCodeModelClient, projectPartsDoNotExist(projectPartsDoNotExistMessage))
.Times(1);
clangServer.unregisterProjectPartsForEditor(unregisterProjectPartsForEditorMessage);
}
TEST_F(ClangClangCodeModelServer, GetProjectPartDoesNotExistRegisterTranslationUnitWithInexistingProjectPart)
{
Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro");
RegisterTranslationUnitForEditorMessage registerFileForEditorMessage({FileContainer(variableTestFilePath, inexistingProjectPartFilePath)},
variableTestFilePath,
{variableTestFilePath});
ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage({inexistingProjectPartFilePath});
EXPECT_CALL(mockClangCodeModelClient, projectPartsDoNotExist(projectPartsDoNotExistMessage))
.Times(1);
clangServer.registerTranslationUnitsForEditor(registerFileForEditorMessage);
}
TEST_F(ClangClangCodeModelServer, GetProjectPartDoesNotExistUnregisterTranslationUnitWithInexistingProjectPart)
{
Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro");
UnregisterTranslationUnitsForEditorMessage unregisterFileForEditorMessage({FileContainer(variableTestFilePath, inexistingProjectPartFilePath)});
ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage({inexistingProjectPartFilePath});
EXPECT_CALL(mockClangCodeModelClient, projectPartsDoNotExist(projectPartsDoNotExistMessage))
.Times(1);
clangServer.unregisterTranslationUnitsForEditor(unregisterFileForEditorMessage);
}
TEST_F(ClangClangCodeModelServer, GetProjectPartDoesNotExistForCompletingProjectPartFile)
{
Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro");
CompleteCodeMessage completeCodeMessage(variableTestFilePath,
20,
1,
inexistingProjectPartFilePath);
ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage({inexistingProjectPartFilePath});
EXPECT_CALL(mockClangCodeModelClient, projectPartsDoNotExist(projectPartsDoNotExistMessage))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, GetProjectPartDoesNotExistForCompletingUnregisteredFile)
{
CompleteCodeMessage completeCodeMessage(parseErrorTestFilePath,
20,
1,
projectPartId);
TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(parseErrorTestFilePath, projectPartId);
EXPECT_CALL(mockClangCodeModelClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, TicketNumberIsForwarded)
{
CompleteCodeMessage completeCodeMessage(functionTestFilePath,
20,
1,
projectPartId);
CodeCompletion codeCompletion(Utf8StringLiteral("Function"),
34,
CodeCompletion::FunctionCompletionKind);
EXPECT_CALL(mockClangCodeModelClient, codeCompleted(Property(&CodeCompletedMessage::ticketNumber, Eq(completeCodeMessage.ticketNumber()))))
.Times(1);
clangServer.completeCode(completeCodeMessage);
}
TEST_F(ClangClangCodeModelServer, TranslationUnitAfterCreationNeedsNoReparseAndHasNoNewDiagnostics)
{
ASSERT_THAT(clangServer, HasDirtyTranslationUnit(functionTestFilePath, projectPartId, 0U, false, false));
}
TEST_F(ClangClangCodeModelServer, SetCurrentAndVisibleEditor)
{
auto functionTranslationUnit = translationUnits.translationUnit(functionTestFilePath, projectPartId);
auto variableTranslationUnit = translationUnits.translationUnit(variableTestFilePath, projectPartId);
updateVisibilty(functionTestFilePath, variableTestFilePath);
ASSERT_TRUE(functionTranslationUnit.isUsedByCurrentEditor());
ASSERT_TRUE(functionTranslationUnit.isVisibleInEditor());
ASSERT_TRUE(variableTranslationUnit.isVisibleInEditor());
}
TEST_F(ClangClangCodeModelServer, IsNotCurrentCurrentAndVisibleEditorAnymore)
{
auto functionTranslationUnit = translationUnits.translationUnit(functionTestFilePath, projectPartId);
auto variableTranslationUnit = translationUnits.translationUnit(variableTestFilePath, projectPartId);
updateVisibilty(functionTestFilePath, variableTestFilePath);
updateVisibilty(variableTestFilePath, Utf8String());
ASSERT_FALSE(functionTranslationUnit.isUsedByCurrentEditor());
ASSERT_FALSE(functionTranslationUnit.isVisibleInEditor());
ASSERT_TRUE(variableTranslationUnit.isUsedByCurrentEditor());
ASSERT_TRUE(variableTranslationUnit.isVisibleInEditor());
}
void ClangClangCodeModelServer::SetUp()
{
clangServer.addClient(&mockClangCodeModelClient);
registerProjectPart();
registerFiles();
}
void ClangClangCodeModelServer::registerFiles()
{
RegisterTranslationUnitForEditorMessage message({FileContainer(functionTestFilePath, projectPartId, unsavedContent(unsavedTestFilePath), true),
FileContainer(variableTestFilePath, projectPartId)},
functionTestFilePath,
{functionTestFilePath, variableTestFilePath});
EXPECT_CALL(mockClangCodeModelClient, diagnosticsChanged(_)).Times(2);
EXPECT_CALL(mockClangCodeModelClient, highlightingChanged(_)).Times(2);
clangServer.registerTranslationUnitsForEditor(message);
}
void ClangClangCodeModelServer::registerProjectPart()
{
RegisterProjectPartsForEditorMessage message({ProjectPartContainer(projectPartId)});
clangServer.registerProjectPartsForEditor(message);
}
void ClangClangCodeModelServer::changeProjectPartArguments()
{
RegisterProjectPartsForEditorMessage message({ProjectPartContainer(projectPartId, {Utf8StringLiteral("-DArgumentDefinition")})});
clangServer.registerProjectPartsForEditor(message);
}
void ClangClangCodeModelServer::changeProjectPartArgumentsToWrongValues()
{
RegisterProjectPartsForEditorMessage message({ProjectPartContainer(projectPartId, {Utf8StringLiteral("-blah")})});
clangServer.registerProjectPartsForEditor(message);
}
void ClangClangCodeModelServer::updateVisibilty(const Utf8String &currentEditor, const Utf8String &additionalVisibleEditor)
{
UpdateVisibleTranslationUnitsMessage message(currentEditor,
{currentEditor, additionalVisibleEditor});
clangServer.updateVisibleTranslationUnits(message);
}
const Utf8String ClangClangCodeModelServer::unsavedContent(const QString &unsavedFilePath)
{
QFile unsavedFileContentFile(unsavedFilePath);
bool isOpen = unsavedFileContentFile.open(QIODevice::ReadOnly | QIODevice::Text);
if (!isOpen)
ADD_FAILURE() << "File with the unsaved content cannot be opened!";
return Utf8String::fromByteArray(unsavedFileContentFile.readAll());
}
TEST_F(ClangClangCodeModelServer, TranslationUnitAfterUpdateNeedsReparseAndHasNewDiagnostics)
{
const auto fileContainer = FileContainer(functionTestFilePath, projectPartId,unsavedContent(unsavedTestFilePath), true, 1);
clangServer.updateTranslationUnitsForEditor({{fileContainer}});
ASSERT_THAT(clangServer, HasDirtyTranslationUnit(functionTestFilePath, projectPartId, 1U, true, true));
}
}