/**************************************************************************** ** ** 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 Digia. For licensing terms and ** conditions see http://www.qt.io/licensing. 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, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "mockipclient.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gtest-qt-printing.h" using testing::Property; using testing::Contains; using testing::Not; using testing::Eq; using testing::PrintToString; 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; MATCHER_P3(HasDirtyTranslationUnit, filePath, projectPartId, documentRevision, std::string(negation ? "isn't" : "is") + " translation unit with file path "+ PrintToString(filePath) + " and project " + PrintToString(projectPartId) + " and document revision " + PrintToString(documentRevision) ) { auto &&translationUnits = arg.translationUnitsForTestOnly(); try { auto translationUnit = translationUnits.translationUnit(filePath, projectPartId); if (translationUnit.documentRevision() == documentRevision) { if (translationUnit.hasNewDiagnostics()) { if (translationUnit.isNeedingReparse()) return true; *result_listener << "isNeedingReparse is false"; return false; } *result_listener << "hasNewDiagnostics is false"; return false; } *result_listener << "revision number is " << PrintToString(translationUnit.documentRevision()); return false; } catch (...) { *result_listener << "has no translation unit"; return false; } } class ClangIpcServer : public ::testing::Test { protected: void SetUp() override; void registerFiles(); void registerProjectPart(); void changeProjectPartArguments(); void changeProjectPartArgumentsToWrongValues(); static const Utf8String unsavedContent(const QString &unsavedFilePath); protected: MockIpcClient mockIpcClient; ClangBackEnd::ClangIpcServer clangServer; 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"); }; void ClangIpcServer::SetUp() { clangServer.addClient(&mockIpcClient); registerProjectPart(); registerFiles(); } void ClangIpcServer::registerFiles() { RegisterTranslationUnitForEditorMessage message({FileContainer(functionTestFilePath, projectPartId, unsavedContent(unsavedTestFilePath), true), FileContainer(variableTestFilePath, projectPartId)}); clangServer.registerTranslationUnitsForEditor(message); } void ClangIpcServer::registerProjectPart() { RegisterProjectPartsForEditorMessage message({ProjectPartContainer(projectPartId)}); clangServer.registerProjectPartsForEditor(message); } void ClangIpcServer::changeProjectPartArguments() { RegisterProjectPartsForEditorMessage message({ProjectPartContainer(projectPartId, {Utf8StringLiteral("-DArgumentDefinition")})}); clangServer.registerProjectPartsForEditor(message); } void ClangIpcServer::changeProjectPartArgumentsToWrongValues() { RegisterProjectPartsForEditorMessage message({ProjectPartContainer(projectPartId, {Utf8StringLiteral("-blah")})}); clangServer.registerProjectPartsForEditor(message); } const Utf8String ClangIpcServer::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(ClangIpcServer, GetCodeCompletion) { CompleteCodeMessage completeCodeMessage(functionTestFilePath, 20, 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Function"), 34, CodeCompletion::FunctionCompletionKind); EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion)))) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetCodeCompletionDependingOnArgumets) { CompleteCodeMessage completeCodeMessage(variableTestFilePath, 35, 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("ArgumentDefinitionVariable"), 34, CodeCompletion::VariableCompletionKind); EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion)))) .Times(1); changeProjectPartArguments(); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetTranslationUnitDoesNotExistForEditorOnNonExistingTranslationUnit) { CompleteCodeMessage completeCodeMessage(Utf8StringLiteral("dontexists.cpp"), 34, 1, projectPartId); TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(Utf8StringLiteral("dontexists.cpp"), projectPartId); EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage)) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetTranslationUnitDoesNotExistForCompletingUnregisteredFile) { CompleteCodeMessage completeCodeMessage(parseErrorTestFilePath, 20, 1, projectPartId); TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(parseErrorTestFilePath, projectPartId); EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage)) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetCodeCompletionForUnsavedFile) { CompleteCodeMessage completeCodeMessage(functionTestFilePath, 20, 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Method2"), 34, CodeCompletion::FunctionCompletionKind); EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion)))) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetNoCodeCompletionAfterRemovingUnsavedFile) { clangServer.updateTranslationUnitsForEditor(UpdateTranslationUnitsForEditorMessage( {FileContainer(functionTestFilePath, projectPartId, Utf8StringVector(), 74)})); CompleteCodeMessage completeCodeMessage(functionTestFilePath, 20, 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Method2"), 34, CodeCompletion::FunctionCompletionKind); EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Not(Contains(codeCompletion))))) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, 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(mockIpcClient, codeCompleted(Property(&CodeCompletedMessage::codeCompletions, Contains(codeCompletion)))) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetTranslationUnitDoesNotExistForUnregisterTranslationUnitWithWrongFilePath) { FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), projectPartId); UnregisterTranslationUnitsForEditorMessage message({fileContainer}); TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(fileContainer); EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage)) .Times(1); clangServer.unregisterTranslationUnitsForEditor(message); } TEST_F(ClangIpcServer, UnregisterTranslationUnitAndTestFailingCompletion) { FileContainer fileContainer(functionTestFilePath, projectPartId); UnregisterTranslationUnitsForEditorMessage message({fileContainer}); clangServer.unregisterTranslationUnitsForEditor(message); CompleteCodeMessage completeCodeMessage(functionTestFilePath, 20, 1, projectPartId); TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(fileContainer); EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage)) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetProjectPartDoesNotExistUnregisterProjectPartInexistingProjectPart) { Utf8StringVector inexistingProjectPartFilePath = {Utf8StringLiteral("projectpartsdoesnotexist.pro"), Utf8StringLiteral("project2doesnotexists.pro")}; UnregisterProjectPartsForEditorMessage unregisterProjectPartsForEditorMessage(inexistingProjectPartFilePath); ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage(inexistingProjectPartFilePath); EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistMessage)) .Times(1); clangServer.unregisterProjectPartsForEditor(unregisterProjectPartsForEditorMessage); } TEST_F(ClangIpcServer, GetProjectPartDoesNotExistRegisterTranslationUnitWithInexistingProjectPart) { Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro"); RegisterTranslationUnitForEditorMessage registerFileForEditorMessage({FileContainer(variableTestFilePath, inexistingProjectPartFilePath)}); ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage({inexistingProjectPartFilePath}); EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistMessage)) .Times(1); clangServer.registerTranslationUnitsForEditor(registerFileForEditorMessage); } TEST_F(ClangIpcServer, GetProjectPartDoesNotExistUnregisterTranslationUnitWithInexistingProjectPart) { Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro"); UnregisterTranslationUnitsForEditorMessage unregisterFileForEditorMessage({FileContainer(variableTestFilePath, inexistingProjectPartFilePath)}); ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage({inexistingProjectPartFilePath}); EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistMessage)) .Times(1); clangServer.unregisterTranslationUnitsForEditor(unregisterFileForEditorMessage); } TEST_F(ClangIpcServer, GetProjectPartDoesNotExistForCompletingProjectPartFile) { Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro"); CompleteCodeMessage completeCodeMessage(variableTestFilePath, 20, 1, inexistingProjectPartFilePath); ProjectPartsDoNotExistMessage projectPartsDoNotExistMessage({inexistingProjectPartFilePath}); EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistMessage)) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, GetProjectPartDoesNotExistForCompletingUnregisteredFile) { CompleteCodeMessage completeCodeMessage(parseErrorTestFilePath, 20, 1, projectPartId); TranslationUnitDoesNotExistMessage translationUnitDoesNotExistMessage(parseErrorTestFilePath, projectPartId); EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistMessage)) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, TicketNumberIsForwarded) { CompleteCodeMessage completeCodeMessage(functionTestFilePath, 20, 1, projectPartId); CodeCompletion codeCompletion(Utf8StringLiteral("Function"), 34, CodeCompletion::FunctionCompletionKind); EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedMessage::ticketNumber, Eq(completeCodeMessage.ticketNumber())))) .Times(1); clangServer.completeCode(completeCodeMessage); } TEST_F(ClangIpcServer, TranslationUnitIsDirtyAfterCreation) { ASSERT_THAT(clangServer, HasDirtyTranslationUnit(functionTestFilePath, projectPartId, 0)); } }