diff options
Diffstat (limited to 'tests/unit/unittest')
41 files changed, 5382 insertions, 0 deletions
diff --git a/tests/unit/unittest/clangcodecompleteresultstest.cpp b/tests/unit/unittest/clangcodecompleteresultstest.cpp new file mode 100644 index 0000000000..228ec8ce87 --- /dev/null +++ b/tests/unit/unittest/clangcodecompleteresultstest.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" +#include "gtest-qt-printing.h" + +#include <clang-c/Index.h> + +#include <clangcodecompleteresults.h> +#include <translationunit.h> +#include <projectpart.h> +#include <unsavedfiles.h> +#include <utf8string.h> + +namespace { + +using ClangBackEnd::ClangCodeCompleteResults; +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::UnsavedFiles; +using ClangBackEnd::ProjectPart; + +TEST(ClangCodeCompleteResults, GetData) +{ + ProjectPart projectPart(Utf8StringLiteral("projectPartId")); + UnsavedFiles unsavedFiles; + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), unsavedFiles, projectPart); + CXCodeCompleteResults *cxCodeCompleteResults = clang_codeCompleteAt(translationUnit.cxTranslationUnit(), translationUnit.filePath().constData(), 49, 1, 0, 0, 0); + + ClangCodeCompleteResults codeCompleteResults(cxCodeCompleteResults); + + ASSERT_THAT(codeCompleteResults.data(), cxCodeCompleteResults); +} + +TEST(ClangCodeCompleteResults, GetInvalidData) +{ + CXCodeCompleteResults *cxCodeCompleteResults = clang_codeCompleteAt(nullptr, "file.cpp", 49, 1, 0, 0, 0); + + ClangCodeCompleteResults codeCompleteResults(cxCodeCompleteResults); + + ASSERT_THAT(codeCompleteResults.data(), cxCodeCompleteResults); +} + +TEST(ClangCodeCompleteResults, MoveClangCodeCompleteResults) +{ + ProjectPart projectPart(Utf8StringLiteral("projectPartId")); + UnsavedFiles unsavedFiles; + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), unsavedFiles, projectPart); + CXCodeCompleteResults *cxCodeCompleteResults = clang_codeCompleteAt(translationUnit.cxTranslationUnit(), translationUnit.filePath().constData(), 49, 1, 0, 0, 0); + + ClangCodeCompleteResults codeCompleteResults(cxCodeCompleteResults); + + const ClangCodeCompleteResults codeCompleteResults2 = std::move(codeCompleteResults); + + ASSERT_TRUE(codeCompleteResults.isNull()); + ASSERT_FALSE(codeCompleteResults2.isNull()); +} + +} diff --git a/tests/unit/unittest/clangipcservertest.cpp b/tests/unit/unittest/clangipcservertest.cpp new file mode 100644 index 0000000000..4070ad0579 --- /dev/null +++ b/tests/unit/unittest/clangipcservertest.cpp @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" + +#include "gtest-qt-printing.h" + +#include <ipcclientproxy.h> +#include <ipcserverproxy.h> +#include <clangipcserver.h> +#include <translationunitdoesnotexistexception.h> +#include <translationunitparseerrorexception.h> + +#include <cmbcodecompletedcommand.h> +#include <cmbcompletecodecommand.h> +#include <cmbechocommand.h> +#include <cmbregistertranslationunitsforcodecompletioncommand.h> +#include <cmbunregistertranslationunitsforcodecompletioncommand.h> +#include <cmbregisterprojectsforcodecompletioncommand.h> +#include <cmbunregisterprojectsforcodecompletioncommand.h> +#include <translationunitdoesnotexistcommand.h> +#include <projectpartsdonotexistcommand.h> + +#include <QBuffer> +#include <QFile> +#include <projectpartsdonotexistcommand.h> + +#include "mockipclient.h" + +using testing::Property; +using testing::Contains; +using testing::Not; +using testing::Eq; + +namespace { + +using ClangBackEnd::RegisterTranslationUnitForCodeCompletionCommand; +using ClangBackEnd::UnregisterTranslationUnitsForCodeCompletionCommand; +using ClangBackEnd::RegisterProjectPartsForCodeCompletionCommand; +using ClangBackEnd::UnregisterProjectPartsForCodeCompletionCommand; +using ClangBackEnd::CompleteCodeCommand; +using ClangBackEnd::CodeCompletedCommand; +using ClangBackEnd::CodeCompletion; +using ClangBackEnd::FileContainer; +using ClangBackEnd::ProjectPartContainer; +using ClangBackEnd::TranslationUnitDoesNotExistCommand; +using ClangBackEnd::ProjectPartsDoNotExistCommand; + +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() +{ + RegisterTranslationUnitForCodeCompletionCommand command({FileContainer(functionTestFilePath, projectPartId, unsavedContent(unsavedTestFilePath), true), + FileContainer(variableTestFilePath, projectPartId)}); + + clangServer.registerTranslationUnitsForCodeCompletion(command); +} + +void ClangIpcServer::registerProjectPart() +{ + RegisterProjectPartsForCodeCompletionCommand command({ProjectPartContainer(projectPartId)}); + + clangServer.registerProjectPartsForCodeCompletion(command); +} + +void ClangIpcServer::changeProjectPartArguments() +{ + RegisterProjectPartsForCodeCompletionCommand command({ProjectPartContainer(projectPartId, {Utf8StringLiteral("-DArgumentDefinition")})}); + + clangServer.registerProjectPartsForCodeCompletion(command); +} + +void ClangIpcServer::changeProjectPartArgumentsToWrongValues() +{ + RegisterProjectPartsForCodeCompletionCommand command({ProjectPartContainer(projectPartId, {Utf8StringLiteral("-blah")})}); + + clangServer.registerProjectPartsForCodeCompletion(command); +} + +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) +{ + CompleteCodeCommand completeCodeCommand(functionTestFilePath, + 20, + 1, + projectPartId); + CodeCompletion codeCompletion(Utf8StringLiteral("Function"), + Utf8String(), + Utf8String(), + 34, + CodeCompletion::FunctionCompletionKind); + + EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedCommand::codeCompletions, Contains(codeCompletion)))) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetCodeCompletionDependingOnArgumets) +{ + CompleteCodeCommand completeCodeCommand(variableTestFilePath, + 35, + 1, + projectPartId); + CodeCompletion codeCompletion(Utf8StringLiteral("ArgumentDefinitionVariable"), + Utf8String(), + Utf8String(), + 34, + CodeCompletion::VariableCompletionKind); + + EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedCommand::codeCompletions, Contains(codeCompletion)))) + .Times(1); + + changeProjectPartArguments(); + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetTranslationUnitDoesNotExistForCodeCompletionOnNonExistingTranslationUnit) +{ + CompleteCodeCommand completeCodeCommand(Utf8StringLiteral("dontexists.cpp"), + 34, + 1, + projectPartId); + TranslationUnitDoesNotExistCommand translationUnitDoesNotExistCommand(Utf8StringLiteral("dontexists.cpp"), projectPartId); + + EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistCommand)) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetTranslationUnitDoesNotExistForCompletingUnregisteredFile) +{ + CompleteCodeCommand completeCodeCommand(parseErrorTestFilePath, + 20, + 1, + projectPartId); + TranslationUnitDoesNotExistCommand translationUnitDoesNotExistCommand(parseErrorTestFilePath, projectPartId); + + EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistCommand)) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetCodeCompletionForUnsavedFile) +{ + CompleteCodeCommand completeCodeCommand(functionTestFilePath, + 20, + 1, + projectPartId); + CodeCompletion codeCompletion(Utf8StringLiteral("Method2"), + Utf8String(), + Utf8String(), + 34, + CodeCompletion::FunctionCompletionKind); + + EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedCommand::codeCompletions, Contains(codeCompletion)))) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetNoCodeCompletionAfterRemovingUnsavedFile) +{ + clangServer.registerTranslationUnitsForCodeCompletion(RegisterTranslationUnitForCodeCompletionCommand({FileContainer(functionTestFilePath, projectPartId)})); + CompleteCodeCommand completeCodeCommand(functionTestFilePath, + 20, + 1, + projectPartId); + CodeCompletion codeCompletion(Utf8StringLiteral("Method2"), + Utf8String(), + Utf8String(), + 34, + CodeCompletion::FunctionCompletionKind); + + EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedCommand::codeCompletions, Not(Contains(codeCompletion))))) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetNewCodeCompletionAfterUpdatingUnsavedFile) +{ + clangServer.registerTranslationUnitsForCodeCompletion(RegisterTranslationUnitForCodeCompletionCommand({FileContainer(functionTestFilePath, + projectPartId, + unsavedContent(updatedUnsavedTestFilePath), + true)})); + CompleteCodeCommand completeCodeCommand(functionTestFilePath, + 20, + 1, + projectPartId); + CodeCompletion codeCompletion(Utf8StringLiteral("Method3"), + Utf8String(), + Utf8String(), + 34, + CodeCompletion::FunctionCompletionKind); + + EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedCommand::codeCompletions, Contains(codeCompletion)))) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetTranslationUnitDoesNotExistForUnregisterTranslationUnitWithWrongFilePath) +{ + FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), projectPartId); + UnregisterTranslationUnitsForCodeCompletionCommand command({fileContainer}); + TranslationUnitDoesNotExistCommand translationUnitDoesNotExistCommand(fileContainer); + + EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistCommand)) + .Times(1); + + clangServer.unregisterTranslationUnitsForCodeCompletion(command); +} + +TEST_F(ClangIpcServer, UnregisterTranslationUnitAndTestFailingCompletion) +{ + FileContainer fileContainer(functionTestFilePath, projectPartId); + UnregisterTranslationUnitsForCodeCompletionCommand command({fileContainer}); + clangServer.unregisterTranslationUnitsForCodeCompletion(command); + CompleteCodeCommand completeCodeCommand(functionTestFilePath, + 20, + 1, + projectPartId); + TranslationUnitDoesNotExistCommand translationUnitDoesNotExistCommand(fileContainer); + + EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistCommand)) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetProjectPartDoesNotExistUnregisterProjectPartInexistingProjectPart) +{ + Utf8StringVector inexistingProjectPartFilePath = {Utf8StringLiteral("projectpartsdoesnotexist.pro"), Utf8StringLiteral("project2doesnotexists.pro")}; + UnregisterProjectPartsForCodeCompletionCommand unregisterProjectPartsForCodeCompletionCommand(inexistingProjectPartFilePath); + ProjectPartsDoNotExistCommand projectPartsDoNotExistCommand(inexistingProjectPartFilePath); + + EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistCommand)) + .Times(1); + + clangServer.unregisterProjectPartsForCodeCompletion(unregisterProjectPartsForCodeCompletionCommand); +} + +TEST_F(ClangIpcServer, GetProjectPartDoesNotExistRegisterTranslationUnitWithInexistingProjectPart) +{ + Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro"); + RegisterTranslationUnitForCodeCompletionCommand registerFileForCodeCompletionCommand({FileContainer(variableTestFilePath, inexistingProjectPartFilePath)}); + ProjectPartsDoNotExistCommand projectPartsDoNotExistCommand({inexistingProjectPartFilePath}); + + EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistCommand)) + .Times(1); + + clangServer.registerTranslationUnitsForCodeCompletion(registerFileForCodeCompletionCommand); +} + +TEST_F(ClangIpcServer, GetProjectPartDoesNotExistUnregisterTranslationUnitWithInexistingProjectPart) +{ + Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro"); + UnregisterTranslationUnitsForCodeCompletionCommand unregisterFileForCodeCompletionCommand({FileContainer(variableTestFilePath, inexistingProjectPartFilePath)}); + ProjectPartsDoNotExistCommand projectPartsDoNotExistCommand({inexistingProjectPartFilePath}); + + EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistCommand)) + .Times(1); + + clangServer.unregisterTranslationUnitsForCodeCompletion(unregisterFileForCodeCompletionCommand); +} + +TEST_F(ClangIpcServer, GetProjectPartDoesNotExistForCompletingProjectPartFile) +{ + Utf8String inexistingProjectPartFilePath = Utf8StringLiteral("projectpartsdoesnotexist.pro"); + CompleteCodeCommand completeCodeCommand(variableTestFilePath, + 20, + 1, + inexistingProjectPartFilePath); + ProjectPartsDoNotExistCommand projectPartsDoNotExistCommand({inexistingProjectPartFilePath}); + + EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(projectPartsDoNotExistCommand)) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, GetProjectPartDoesNotExistForCompletingUnregisteredFile) +{ + CompleteCodeCommand completeCodeCommand(parseErrorTestFilePath, + 20, + 1, + projectPartId); + TranslationUnitDoesNotExistCommand translationUnitDoesNotExistCommand(parseErrorTestFilePath, projectPartId); + + EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(translationUnitDoesNotExistCommand)) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} + +TEST_F(ClangIpcServer, TicketNumberIsForwarded) +{ + CompleteCodeCommand completeCodeCommand(functionTestFilePath, + 20, + 1, + projectPartId); + CodeCompletion codeCompletion(Utf8StringLiteral("Function"), + Utf8String(), + Utf8String(), + 34, + CodeCompletion::FunctionCompletionKind); + + EXPECT_CALL(mockIpcClient, codeCompleted(Property(&CodeCompletedCommand::ticketNumber, Eq(completeCodeCommand.ticketNumber())))) + .Times(1); + + clangServer.completeCode(completeCodeCommand); +} +} diff --git a/tests/unit/unittest/clangstringtest.cpp b/tests/unit/unittest/clangstringtest.cpp new file mode 100644 index 0000000000..a7b5f86767 --- /dev/null +++ b/tests/unit/unittest/clangstringtest.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" +#include "gtest-qt-printing.h" + +#include <clang-c/CXString.h> +#include <clang-c/Index.h> + +#include <clangstring.h> +#include <utf8string.h> + +namespace { + +using ClangBackEnd::ClangString; + +TEST(ClangString, ConvertToUtf8String) +{ + const CXString cxString = { "text", 0}; + + ASSERT_THAT(Utf8String(ClangString(cxString)), Utf8StringLiteral("text")); +} + +TEST(ClangString, ConvertNullStringToUtf8String) +{ + const CXString cxString = { 0, 0}; + + ASSERT_THAT(Utf8String(ClangString(cxString)), Utf8String()); +} + +TEST(ClangString, MoveClangString) +{ + ClangString text(CXString{ "text", 0}); + + const ClangString text2 = std::move(text); + + ASSERT_TRUE(text.isNull()); + ASSERT_FALSE(text2.isNull()); +} + +} diff --git a/tests/unit/unittest/clientserverinprocesstest.cpp b/tests/unit/unittest/clientserverinprocesstest.cpp new file mode 100644 index 0000000000..3d29e596f0 --- /dev/null +++ b/tests/unit/unittest/clientserverinprocesstest.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" + +#include "gtest-qt-printing.h" +#include "translationunitdoesnotexistcommand.h" + +#include <QString> +#include <QBuffer> +#include <QVariant> +#include <vector> + +#include <cmbendcommand.h> +#include <cmbalivecommand.h> +#include <cmbcommands.h> +#include <cmbechocommand.h> +#include <cmbregistertranslationunitsforcodecompletioncommand.h> +#include <cmbunregistertranslationunitsforcodecompletioncommand.h> +#include <cmbcodecompletedcommand.h> +#include <cmbregisterprojectsforcodecompletioncommand.h> +#include <cmbunregisterprojectsforcodecompletioncommand.h> +#include <cmbcompletecodecommand.h> +#include <writecommandblock.h> +#include <readcommandblock.h> + +#include <ipcserverproxy.h> +#include <ipcclientproxy.h> +#include <projectpartsdonotexistcommand.h> + +#include "mockipclient.h" +#include "mockipcserver.h" + +using namespace ClangBackEnd; + +namespace { + +using ::testing::Args; +using ::testing::Property; +using ::testing::Eq; + +class ClientServerInProcess : public ::testing::Test +{ +protected: + ClientServerInProcess(); + + void SetUp(); + void TearDown(); + + + void scheduleServerCommands(); + void scheduleClientCommands(); + + QBuffer buffer; + MockIpcClient mockIpcClient; + MockIpcServer mockIpcServer; + + ClangBackEnd::IpcServerProxy serverProxy; + ClangBackEnd::IpcClientProxy clientProxy; +}; + +TEST_F(ClientServerInProcess, SendEndCommand) +{ + EXPECT_CALL(mockIpcServer, end()) + .Times(1); + + serverProxy.end(); + scheduleServerCommands(); +} + +TEST_F(ClientServerInProcess, SendAliveCommand) +{ + EXPECT_CALL(mockIpcClient, alive()) + .Times(1); + + clientProxy.alive(); + scheduleClientCommands(); +} + +TEST_F(ClientServerInProcess, SendRegisterTranslationUnitForCodeCompletionCommand) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_function.cpp"), + Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::RegisterTranslationUnitForCodeCompletionCommand command({fileContainer}); + + EXPECT_CALL(mockIpcServer, registerTranslationUnitsForCodeCompletion(command)) + .Times(1); + + serverProxy.registerTranslationUnitsForCodeCompletion(command); + scheduleServerCommands(); +} + +TEST_F(ClientServerInProcess, SendUnregisterTranslationUnitsForCodeCompletionCommand) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::UnregisterTranslationUnitsForCodeCompletionCommand command({fileContainer}); + + EXPECT_CALL(mockIpcServer, unregisterTranslationUnitsForCodeCompletion(command)) + .Times(1); + + serverProxy.unregisterTranslationUnitsForCodeCompletion(command); + scheduleServerCommands(); +} + +TEST_F(ClientServerInProcess, SendCompleteCodeCommand) +{ + ClangBackEnd::CompleteCodeCommand command(Utf8StringLiteral("foo.cpp"), 24, 33, Utf8StringLiteral("do what I want")); + + EXPECT_CALL(mockIpcServer, completeCode(command)) + .Times(1); + + serverProxy.completeCode(command); + scheduleServerCommands(); +} + +TEST_F(ClientServerInProcess, SendCodeCompletedCommand) +{ + QVector<ClangBackEnd::CodeCompletion> codeCompletions({Utf8StringLiteral("newFunction()")}); + ClangBackEnd::CodeCompletedCommand command(codeCompletions, 1); + + EXPECT_CALL(mockIpcClient, codeCompleted(command)) + .Times(1); + + clientProxy.codeCompleted(command); + scheduleClientCommands(); +} + +TEST_F(ClientServerInProcess, SendRegisterProjectPartsForCodeCompletionCommand) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral(TESTDATA_DIR"/complete.pro")); + ClangBackEnd::RegisterProjectPartsForCodeCompletionCommand command({projectContainer}); + + EXPECT_CALL(mockIpcServer, registerProjectPartsForCodeCompletion(command)) + .Times(1); + + serverProxy.registerProjectPartsForCodeCompletion(command); + scheduleServerCommands(); +} + +TEST_F(ClientServerInProcess, SendUnregisterProjectPartsForCodeCompletionCommand) +{ + ClangBackEnd::UnregisterProjectPartsForCodeCompletionCommand command({Utf8StringLiteral(TESTDATA_DIR"/complete.pro")}); + + EXPECT_CALL(mockIpcServer, unregisterProjectPartsForCodeCompletion(command)) + .Times(1); + + serverProxy.unregisterProjectPartsForCodeCompletion(command); + scheduleServerCommands(); +} + +TEST_F(ClientServerInProcess, SendTranslationUnitDoesNotExistCommand) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_function.cpp"), + Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::TranslationUnitDoesNotExistCommand command(fileContainer); + + EXPECT_CALL(mockIpcClient, translationUnitDoesNotExist(command)) + .Times(1); + + clientProxy.translationUnitDoesNotExist(command); + scheduleClientCommands(); +} + + +TEST_F(ClientServerInProcess, SendProjectPartDoesNotExistCommand) +{ + ClangBackEnd::ProjectPartsDoNotExistCommand command({Utf8StringLiteral("pathToProjectPart.pro")}); + + EXPECT_CALL(mockIpcClient, projectPartsDoNotExist(command)) + .Times(1); + + clientProxy.projectPartsDoNotExist(command); + scheduleClientCommands(); +} +ClientServerInProcess::ClientServerInProcess() + : serverProxy(&mockIpcClient, &buffer), + clientProxy(&mockIpcServer, &buffer) +{ +} + +void ClientServerInProcess::SetUp() +{ + buffer.open(QIODevice::ReadWrite); +} + +void ClientServerInProcess::TearDown() +{ + buffer.close(); +} + +void ClientServerInProcess::scheduleServerCommands() +{ + buffer.seek(0); + clientProxy.readCommands(); + buffer.buffer().clear(); +} + +void ClientServerInProcess::scheduleClientCommands() +{ + buffer.seek(0); + serverProxy.readCommands(); + buffer.buffer().clear(); +} + +} diff --git a/tests/unit/unittest/clientserveroutsideprocess.cpp b/tests/unit/unittest/clientserveroutsideprocess.cpp new file mode 100644 index 0000000000..22be5abce8 --- /dev/null +++ b/tests/unit/unittest/clientserveroutsideprocess.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" + +#include <QBuffer> +#include <QProcess> +#include <QSignalSpy> +#include <QString> +#include <QVariant> + +#include <vector> + +#include "gtest-qt-printing.h" + +#include <cmbendcommand.h> +#include <cmbalivecommand.h> +#include <cmbcommands.h> +#include <cmbechocommand.h> +#include <cmbregistertranslationunitsforcodecompletioncommand.h> +#include <cmbunregistertranslationunitsforcodecompletioncommand.h> +#include <cmbregisterprojectsforcodecompletioncommand.h> +#include <cmbunregisterprojectsforcodecompletioncommand.h> +#include <cmbcodecompletedcommand.h> +#include <cmbcompletecodecommand.h> +#include <writecommandblock.h> +#include <readcommandblock.h> +#include <connectionclient.h> +#include <translationunitdoesnotexistcommand.h> +#include <projectpartsdonotexistcommand.h> + +#include <mockipclient.h> + +#ifdef Q_OS_WIN +#define QTC_HOST_EXE_SUFFIX L".exe" +#else +#define QTC_HOST_EXE_SUFFIX "" +#endif + +using namespace ClangBackEnd; + +using ::testing::Eq; + +class ClientServerOutsideProcess : public ::testing::Test +{ +protected: + void SetUp(); + void TearDown(); + + static void SetUpTestCase(); + static void TearDownTestCase(); + + static MockIpcClient mockIpcClient; + static ClangBackEnd::ConnectionClient client; +}; + +MockIpcClient ClientServerOutsideProcess::mockIpcClient; +ClangBackEnd::ConnectionClient ClientServerOutsideProcess::client(&ClientServerOutsideProcess::mockIpcClient); + +TEST_F(ClientServerOutsideProcess, RestartProcess) +{ + client.restartProcess(); + + ASSERT_TRUE(client.isProcessIsRunning()); + ASSERT_TRUE(client.isConnected()); +} + +TEST_F(ClientServerOutsideProcess, RestartProcessAfterAliveTimeout) +{ + QSignalSpy clientSpy(&client, SIGNAL(processRestarted())); + + client.setProcessAliveTimerInterval(1); + + ASSERT_TRUE(clientSpy.wait(100000)); +} + +TEST_F(ClientServerOutsideProcess, RestartProcessAfterTermination) +{ + QSignalSpy clientSpy(&client, SIGNAL(processRestarted())); + + client.processForTestOnly()->kill(); + + ASSERT_TRUE(clientSpy.wait(100000)); +} + +TEST_F(ClientServerOutsideProcess, SendRegisterTranslationUnitForCodeCompletionCommand) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo"), Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::RegisterTranslationUnitForCodeCompletionCommand registerTranslationUnitForCodeCompletionCommand({fileContainer}); + EchoCommand echoCommand(QVariant::fromValue(registerTranslationUnitForCodeCompletionCommand)); + + EXPECT_CALL(mockIpcClient, echo(echoCommand)) + .Times(1); + + client.serverProxy().registerTranslationUnitsForCodeCompletion(registerTranslationUnitForCodeCompletionCommand); + ASSERT_TRUE(client.waitForEcho()); +} + +TEST_F(ClientServerOutsideProcess, SendUnregisterTranslationUnitsForCodeCompletionCommand) +{ + FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), Utf8StringLiteral("bar.pro")); + ClangBackEnd::UnregisterTranslationUnitsForCodeCompletionCommand unregisterTranslationUnitsForCodeCompletionCommand ({fileContainer}); + EchoCommand echoCommand(QVariant::fromValue(unregisterTranslationUnitsForCodeCompletionCommand)); + + EXPECT_CALL(mockIpcClient, echo(echoCommand)) + .Times(1); + + client.serverProxy().unregisterTranslationUnitsForCodeCompletion(unregisterTranslationUnitsForCodeCompletionCommand); + ASSERT_TRUE(client.waitForEcho()); +} + +TEST_F(ClientServerOutsideProcess, SendCompleteCodeCommand) +{ + CompleteCodeCommand codeCompleteCommand(Utf8StringLiteral("foo.cpp"), 24, 33, Utf8StringLiteral("do what I want")); + EchoCommand echoCommand(QVariant::fromValue(codeCompleteCommand)); + + EXPECT_CALL(mockIpcClient, echo(echoCommand)) + .Times(1); + + client.serverProxy().completeCode(codeCompleteCommand); + ASSERT_TRUE(client.waitForEcho()); +} + +TEST_F(ClientServerOutsideProcess, SendRegisterProjectPartsForCodeCompletionCommand) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral(TESTDATA_DIR"/complete.pro")); + ClangBackEnd::RegisterProjectPartsForCodeCompletionCommand registerProjectPartsForCodeCompletionCommand({projectContainer}); + EchoCommand echoCommand(QVariant::fromValue(registerProjectPartsForCodeCompletionCommand)); + + EXPECT_CALL(mockIpcClient, echo(echoCommand)) + .Times(1); + + client.serverProxy().registerProjectPartsForCodeCompletion(registerProjectPartsForCodeCompletionCommand); + ASSERT_TRUE(client.waitForEcho()); +} + +TEST_F(ClientServerOutsideProcess, SendUnregisterProjectPartsForCodeCompletionCommand) +{ + ClangBackEnd::UnregisterProjectPartsForCodeCompletionCommand unregisterProjectPartsForCodeCompletionCommand({Utf8StringLiteral(TESTDATA_DIR"/complete.pro")}); + EchoCommand echoCommand(QVariant::fromValue(unregisterProjectPartsForCodeCompletionCommand)); + + EXPECT_CALL(mockIpcClient, echo(echoCommand)) + .Times(1); + + client.serverProxy().unregisterProjectPartsForCodeCompletion(unregisterProjectPartsForCodeCompletionCommand); + ASSERT_TRUE(client.waitForEcho()); +} + +void ClientServerOutsideProcess::SetUpTestCase() +{ + client.setProcessPath(QStringLiteral(ECHOSERVER QTC_HOST_EXE_SUFFIX)); + + ASSERT_TRUE(client.connectToServer()); +} + +void ClientServerOutsideProcess::TearDownTestCase() +{ + client.finishProcess(); +} +void ClientServerOutsideProcess::SetUp() +{ + client.setProcessAliveTimerInterval(1000000); + + ASSERT_TRUE(client.isConnected()); +} + +void ClientServerOutsideProcess::TearDown() +{ + ASSERT_TRUE(client.isProcessIsRunning()); + ASSERT_TRUE(client.isConnected()); +} diff --git a/tests/unit/unittest/codecompletionsextractortest.cpp b/tests/unit/unittest/codecompletionsextractortest.cpp new file mode 100644 index 0000000000..e9a4f26856 --- /dev/null +++ b/tests/unit/unittest/codecompletionsextractortest.cpp @@ -0,0 +1,663 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" + +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-generated-matchers.h" +#include "gtest-qt-printing.h" + +#include <clang-c/Index.h> + +#include <codecompletionsextractor.h> +#include <clangcodecompleteresults.h> +#include <filecontainer.h> +#include <translationunit.h> +#include <unsavedfiles.h> +#include <projectpart.h> +#include <utf8stringvector.h> + +#include <QFile> + +using ClangBackEnd::CodeCompletionsExtractor; +using ClangBackEnd::ClangCodeCompleteResults; +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::CodeCompletion; +using ClangBackEnd::UnsavedFiles; +using ClangBackEnd::CodeCompletionChunk; + + + +namespace { + +using ::testing::PrintToString; +using ::testing::Not; + +MATCHER_P3(HasCompletion, name, kind, availability, + std::string(negation ? "hasn't" : "has") + " completion of name " + PrintToString(name) + + ", kind " + PrintToString(kind)) +{ + ::CodeCompletionsExtractor &extractor = const_cast<::CodeCompletionsExtractor&>(arg); + while (extractor.next()) { + if (extractor.currentCodeCompletion().text() == name) { + if (extractor.currentCodeCompletion().completionKind() == kind) { + if (extractor.currentCodeCompletion().availability() == availability) { + return true; + } else if (!extractor.peek(name)) { + *result_listener << "availability is " << PrintToString(extractor.currentCodeCompletion().availability()) << " and not " << PrintToString(availability); + return false; + } + } else if (!extractor.peek(name)) { + *result_listener << "kind is " << PrintToString(extractor.currentCodeCompletion().completionKind()) << " and not " << PrintToString(kind); + return false; + } + } + } + + return false; +} + +MATCHER_P2(HasCompletionChunks, name, chunks, + std::string(negation ? "hasn't" : "has") + " completion of name " + PrintToString(name) + + " with the chunks " + PrintToString(chunks)) +{ + ::CodeCompletionsExtractor &extractor = const_cast<::CodeCompletionsExtractor&>(arg); + while (extractor.next()) { + if (extractor.currentCodeCompletion().text() == name) { + if (extractor.currentCodeCompletion().chunks() == chunks) { + return true; + } else if (!extractor.peek(name)) { + *result_listener << "chunks are " << PrintToString(arg.currentCodeCompletion().chunks()) << " and not " << PrintToString(chunks); + return false; + } + } + } + + return false; +} + +const Utf8String unsavedFileContent(const char *unsavedFilePath) +{ + QFile unsavedFileContentFile(QString::fromUtf8(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()); +} + +const ClangBackEnd::FileContainer unsavedDataFileContainer(const char *filePath, + const char *unsavedFilePath) +{ + return ClangBackEnd::FileContainer(Utf8String::fromUtf8(filePath), + Utf8String(), + unsavedFileContent(unsavedFilePath), + true); +} + +ClangCodeCompleteResults getResults(const TranslationUnit &translationUnit, uint line, uint column = 1) +{ + return ClangCodeCompleteResults(clang_codeCompleteAt(translationUnit.cxTranslationUnit(), + translationUnit.filePath().constData(), + line, + column, + translationUnit.cxUnsavedFiles(), + translationUnit.unsavedFilesCount(), + CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns)); +} + +class CodeCompletionsExtractor : public ::testing::Test +{ +public: + static void TearDownTestCase(); + +protected: + static ClangBackEnd::ProjectPart project; + static ClangBackEnd::UnsavedFiles unsavedFiles; + static TranslationUnit functionTranslationUnit; + static TranslationUnit variableTranslationUnit; + static TranslationUnit classTranslationUnit ; + static TranslationUnit namespaceTranslationUnit; + static TranslationUnit enumerationTranslationUnit; + static TranslationUnit constructorTranslationUnit; +}; + +ClangBackEnd::ProjectPart CodeCompletionsExtractor::project(Utf8StringLiteral("/path/to/projectfile")); +ClangBackEnd::UnsavedFiles CodeCompletionsExtractor::unsavedFiles; +TranslationUnit CodeCompletionsExtractor::functionTranslationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_function.cpp"), unsavedFiles, project); +TranslationUnit CodeCompletionsExtractor::variableTranslationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_variable.cpp"), unsavedFiles, project); +TranslationUnit CodeCompletionsExtractor::classTranslationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_class.cpp"), unsavedFiles, project); +TranslationUnit CodeCompletionsExtractor::namespaceTranslationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_namespace.cpp"), unsavedFiles, project); +TranslationUnit CodeCompletionsExtractor::enumerationTranslationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_enumeration.cpp"), unsavedFiles, project); +TranslationUnit CodeCompletionsExtractor::constructorTranslationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_constructor.cpp"), unsavedFiles, project); + +void CodeCompletionsExtractor::TearDownTestCase() +{ + functionTranslationUnit.reset(); + variableTranslationUnit.reset(); + classTranslationUnit.reset(); + namespaceTranslationUnit.reset(); + enumerationTranslationUnit.reset(); + constructorTranslationUnit.reset(); +} + +TEST_F(CodeCompletionsExtractor, Function) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Function"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, TemplateFunction) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("TemplateFunction"), + CodeCompletion::TemplateFunctionCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Variable) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 4)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Var"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available)); +} + + +TEST_F(CodeCompletionsExtractor, NonTypeTemplateParameter) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 25, 19)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("NonTypeTemplateParameter"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available)); +} + + +TEST_F(CodeCompletionsExtractor, VariableReference) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 12)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Var"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Parameter) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 4)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Parameter"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Field) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Field"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Class) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Class"), + CodeCompletion::ClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Struct) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Struct"), + CodeCompletion::ClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Union) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Union"), + CodeCompletion::ClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, TemplateTypeParameter) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("TemplateTypeParameter"), + CodeCompletion::ClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, TemplateClass) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("TemplateClass"), + CodeCompletion::TemplateClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, TemplateTemplateParameter) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("TemplateTemplateParameter"), + CodeCompletion::TemplateClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, ClassTemplatePartialSpecialization) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("ClassTemplatePartialSpecialization"), + CodeCompletion::TemplateClassCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Namespace) +{ + ClangCodeCompleteResults completeResults(getResults(namespaceTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Namespace"), + CodeCompletion::NamespaceCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, NamespaceAlias) +{ + ClangCodeCompleteResults completeResults(getResults(namespaceTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("NamespaceAlias"), + CodeCompletion::NamespaceCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Enumeration) +{ + ClangCodeCompleteResults completeResults(getResults(enumerationTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Enumeration"), + CodeCompletion::EnumerationCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Enumerator) +{ + ClangCodeCompleteResults completeResults(getResults(enumerationTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Enumerator"), + CodeCompletion::EnumeratorCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Constructor) +{ + ClangCodeCompleteResults completeResults(getResults(constructorTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Constructor"), + CodeCompletion::ConstructorCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Destructor) +{ + ClangCodeCompleteResults completeResults(getResults(constructorTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("~Constructor"), + CodeCompletion::DestructorCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Method) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Method"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Available)); + ASSERT_FALSE(extractor.currentCodeCompletion().hasParameters()); +} + +TEST_F(CodeCompletionsExtractor, MethodWithParameters) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("MethodWithParameters"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Available)); + ASSERT_TRUE(extractor.currentCodeCompletion().hasParameters()); +} + +TEST_F(CodeCompletionsExtractor, Slot) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Slot"), + CodeCompletion::SlotCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, Signal) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Signal"), + CodeCompletion::SignalCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, MacroDefinition) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 35)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("MacroDefinition"), + CodeCompletion::PreProcessorCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, FunctionMacro) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("FunctionMacro"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, IntKeyword) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("int"), + CodeCompletion::KeywordCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, SwitchKeyword) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("switch"), + CodeCompletion::KeywordCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, ClassKeyword) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("class"), + CodeCompletion::KeywordCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, DeprecatedFunction) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("DeprecatedFunction"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Deprecated)); +} + +TEST_F(CodeCompletionsExtractor, NotAccessibleFunction) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("NotAccessibleFunction"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::NotAccessible)); +} + +TEST_F(CodeCompletionsExtractor, NotAvailableFunction) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("NotAvailableFunction"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::NotAvailable)); +} + +TEST_F(CodeCompletionsExtractor, UnsavedFile) +{ + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::ProjectPart project(Utf8StringLiteral("/path/to/projectfile")); + TranslationUnit translationUnit(Utf8String::fromUtf8(TESTDATA_DIR"/complete_extractor_function.cpp"), unsavedFiles, project); + unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", + TESTDATA_DIR"/complete_extractor_function_unsaved.cpp")}); + ClangCodeCompleteResults completeResults(getResults(translationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Method2"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, ChangeUnsavedFile) +{ + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::ProjectPart project(Utf8StringLiteral("/path/to/projectfile")); + TranslationUnit translationUnit(Utf8String::fromUtf8(TESTDATA_DIR"/complete_extractor_function.cpp"), unsavedFiles, project); + unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", + TESTDATA_DIR"/complete_extractor_function_unsaved.cpp")}); + ClangCodeCompleteResults completeResults(getResults(translationUnit, 20)); + unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", + TESTDATA_DIR"/complete_extractor_function_unsaved_2.cpp")}); + completeResults = getResults(translationUnit, 20); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("Method3"), + CodeCompletion::FunctionCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, ArgumentDefinition) +{ + variableTranslationUnit.cxTranslationUnit(); + project.setArguments({Utf8StringLiteral("-DArgumentDefinition")}); + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 35)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletion(Utf8StringLiteral("ArgumentDefinitionVariable"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available)); +} + +TEST_F(CodeCompletionsExtractor, NoArgumentDefinition) +{ + variableTranslationUnit.cxTranslationUnit(); + project.setArguments(Utf8StringVector()); + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 35)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, Not(HasCompletion(Utf8StringLiteral("ArgumentDefinitionVariable"), + CodeCompletion::VariableCompletionKind, + CodeCompletion::Available))); +} + +TEST_F(CodeCompletionsExtractor, CompletionChunksFunction) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletionChunks(Utf8StringLiteral("Function"), + QVector<CodeCompletionChunk>({{CodeCompletionChunk::ResultType, Utf8StringLiteral("void")}, + {CodeCompletionChunk::TypedText, Utf8StringLiteral("Function")}, + {CodeCompletionChunk::LeftParen, Utf8StringLiteral("(")}, + {CodeCompletionChunk::RightParen, Utf8StringLiteral(")")}}))); +} + +TEST_F(CodeCompletionsExtractor, CompletionChunksFunctionWithOptionalChunks) +{ + ClangCodeCompleteResults completeResults(getResults(functionTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletionChunks(Utf8StringLiteral("FunctionWithOptional"), + QVector<CodeCompletionChunk>({{CodeCompletionChunk::ResultType, Utf8StringLiteral("void")}, + {CodeCompletionChunk::TypedText, Utf8StringLiteral("FunctionWithOptional")}, + {CodeCompletionChunk::LeftParen, Utf8StringLiteral("(")}, + {CodeCompletionChunk::Placeholder, Utf8StringLiteral("int x")}, + {CodeCompletionChunk::Optional, Utf8String(), QVector<CodeCompletionChunk>({ + {CodeCompletionChunk::Comma, Utf8StringLiteral(", ")}, + {CodeCompletionChunk::Placeholder, Utf8StringLiteral("char y")}, + {CodeCompletionChunk::Comma, Utf8StringLiteral(", ")}, + {CodeCompletionChunk::Placeholder, Utf8StringLiteral("int z")}})}, + {CodeCompletionChunk::RightParen, Utf8StringLiteral(")")}}))); +} + +TEST_F(CodeCompletionsExtractor, CompletionChunksField) +{ + ClangCodeCompleteResults completeResults(getResults(variableTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletionChunks(Utf8StringLiteral("Field"), + QVector<CodeCompletionChunk>({{CodeCompletionChunk::ResultType, Utf8StringLiteral("int")}, + {CodeCompletionChunk::TypedText, Utf8StringLiteral("Field")}}))); +} + +TEST_F(CodeCompletionsExtractor, CompletionChunksEnumerator) +{ + ClangCodeCompleteResults completeResults(getResults(enumerationTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletionChunks(Utf8StringLiteral("Enumerator"), + QVector<CodeCompletionChunk>({{CodeCompletionChunk::ResultType, Utf8StringLiteral("Enumeration")}, + {CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumerator")}}))); +} + +TEST_F(CodeCompletionsExtractor, CompletionChunksEnumeration) +{ + ClangCodeCompleteResults completeResults(getResults(enumerationTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletionChunks(Utf8StringLiteral("Enumeration"), + QVector<CodeCompletionChunk>({{CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumeration")}}))); +} + +TEST_F(CodeCompletionsExtractor, CompletionChunksClass) +{ + ClangCodeCompleteResults completeResults(getResults(classTranslationUnit, 20)); + + ::CodeCompletionsExtractor extractor(completeResults.data()); + + ASSERT_THAT(extractor, HasCompletionChunks(Utf8StringLiteral("Class"), + QVector<CodeCompletionChunk>({{CodeCompletionChunk::TypedText, Utf8StringLiteral("Class")}}))); +} + + +} diff --git a/tests/unit/unittest/codecompletiontest.cpp b/tests/unit/unittest/codecompletiontest.cpp new file mode 100644 index 0000000000..efb4551a0a --- /dev/null +++ b/tests/unit/unittest/codecompletiontest.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <codecompleter.h> +#include <filecontainer.h> +#include <translationunit.h> +#include <unsavedfiles.h> +#include <utf8stringvector.h> +#include <projectpart.h> + +#include <QFile> + + +using ::testing::ElementsAreArray; +using ::testing::Contains; +using ::testing::AllOf; +using ::testing::Not; + +using ClangBackEnd::CodeCompletion; +using ClangBackEnd::CodeCompleter; + +namespace { + +class CodeCompleter : public ::testing::Test +{ +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + +protected: + static ClangBackEnd::ProjectPart projectPart; + static ClangBackEnd::UnsavedFiles unsavedFiles; + static ClangBackEnd::TranslationUnit translationUnit; + static ClangBackEnd::CodeCompleter completer; +}; + +ClangBackEnd::ProjectPart CodeCompleter::projectPart(Utf8StringLiteral("projectPartId")); +ClangBackEnd::UnsavedFiles CodeCompleter::unsavedFiles; +ClangBackEnd::TranslationUnit CodeCompleter::translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_completer.cpp"), unsavedFiles, projectPart); +ClangBackEnd::CodeCompleter CodeCompleter::completer = translationUnit; + +void CodeCompleter::SetUpTestCase() +{ + QFile unsavedFileContentFile(QStringLiteral(TESTDATA_DIR) + QStringLiteral("/complete_completer_unsaved.cpp")); + unsavedFileContentFile.open(QIODevice::ReadOnly); + + const Utf8String unsavedFileContent = Utf8String::fromByteArray(unsavedFileContentFile.readAll()); + const ClangBackEnd::FileContainer unsavedDataFileContainer(translationUnit.filePath(), + projectPart.projectPartId(), + unsavedFileContent, + true); + + unsavedFiles.createOrUpdate({unsavedDataFileContainer}); +} + +void CodeCompleter::TearDownTestCase() +{ + translationUnit.reset(); + completer = ClangBackEnd::CodeCompleter(); +} + + +TEST_F(CodeCompleter, FunctionInUnsavedFile) +{ + ASSERT_THAT(completer.complete(100, 1), + AllOf(Contains(CodeCompletion(Utf8StringLiteral("functionWithArguments"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::FunctionCompletionKind)), + Contains(CodeCompletion(Utf8StringLiteral("function"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::FunctionCompletionKind)), + Contains(CodeCompletion(Utf8StringLiteral("newFunction"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::FunctionCompletionKind)), + Contains(CodeCompletion(Utf8StringLiteral("f"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::FunctionCompletionKind)), + Not(Contains(CodeCompletion(Utf8StringLiteral("otherFunction"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::FunctionCompletionKind))))); +} + + +TEST_F(CodeCompleter, Macro) +{ + ASSERT_THAT(completer.complete(100, 1), + Contains(CodeCompletion(Utf8StringLiteral("Macro"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::PreProcessorCompletionKind))); +} + +TEST_F(CodeCompleter, Keyword) +{ + ASSERT_THAT(completer.complete(100, 1), + Contains(CodeCompletion(Utf8StringLiteral("switch"), + Utf8String(), + Utf8String(), + 0, + CodeCompletion::KeywordCompletionKind))); +} + + +} + + diff --git a/tests/unit/unittest/completionchunkstotextconvertertest.cpp b/tests/unit/unittest/completionchunkstotextconvertertest.cpp new file mode 100644 index 0000000000..c5c7f596e9 --- /dev/null +++ b/tests/unit/unittest/completionchunkstotextconvertertest.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-generated-matchers.h" +#include "gtest-qt-printing.h" + +#include <codecompletionchunk.h> +#include <completionchunkstotextconverter.h> + +namespace { + +using ClangBackEnd::CodeCompletionChunk; + +class CompletionChunksToTextConverter : public ::testing::Test +{ +protected: + ClangCodeModel::Internal::CompletionChunksToTextConverter converter; + CodeCompletionChunk integerResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("int")}; + CodeCompletionChunk enumerationResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("Enumeration")}; + CodeCompletionChunk functionName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Function")}; + CodeCompletionChunk variableName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Variable")}; + CodeCompletionChunk enumeratorName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumerator")}; + CodeCompletionChunk enumerationName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumeration")}; + CodeCompletionChunk className{CodeCompletionChunk::TypedText, Utf8StringLiteral("Class")}; + CodeCompletionChunk leftParen{CodeCompletionChunk::LeftParen, Utf8StringLiteral("(")}; + CodeCompletionChunk rightParen{CodeCompletionChunk::LeftParen, Utf8StringLiteral(")")}; + CodeCompletionChunk comma{CodeCompletionChunk::Comma, Utf8StringLiteral(", ")}; + CodeCompletionChunk functionArgumentX{CodeCompletionChunk::Placeholder, Utf8StringLiteral("char x")}; + CodeCompletionChunk functionArgumentY{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int y")}; + CodeCompletionChunk functionArgumentZ{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int z")}; + CodeCompletionChunk optional{CodeCompletionChunk::Optional, Utf8String(), {comma, functionArgumentY, comma, functionArgumentZ}}; +}; + +TEST_F(CompletionChunksToTextConverter, ParseIsClearingText) +{ + QVector<CodeCompletionChunk> completionChunks({integerResultType, functionName, leftParen, rightParen}); + converter.parseChunks(completionChunks); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("int Function()")); +} + +TEST_F(CompletionChunksToTextConverter, ConvertFunction) +{ + QVector<CodeCompletionChunk> completionChunks({integerResultType, functionName, leftParen, rightParen}); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("int Function()")); +} + +TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithParameters) +{ + QVector<CodeCompletionChunk> completionChunks({integerResultType, functionName, leftParen, functionArgumentX,rightParen}); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("int Function(char x)")); +} + +TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithOptionalParameter) +{ + QVector<CodeCompletionChunk> completionChunks({integerResultType, functionName, leftParen, functionArgumentX, optional,rightParen}); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("int Function(char x<i>, int y, int z</i>)")); +} + +TEST_F(CompletionChunksToTextConverter, ConvertVariable) +{ + QVector<CodeCompletionChunk> completionChunks({integerResultType, variableName}); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("int Variable")); +} + +TEST_F(CompletionChunksToTextConverter, Enumerator) +{ + QVector<CodeCompletionChunk> completionChunks({enumerationResultType, enumeratorName}); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("Enumeration Enumerator")); +} + +TEST_F(CompletionChunksToTextConverter, Enumeration) +{ + QVector<CodeCompletionChunk> completionChunks({className}); + + converter.parseChunks(completionChunks); + + ASSERT_THAT(converter.text(), QStringLiteral("Class")); +} + +} diff --git a/tests/unit/unittest/createtablesqlstatementbuildertest.cpp b/tests/unit/unittest/createtablesqlstatementbuildertest.cpp new file mode 100644 index 0000000000..87dbb84189 --- /dev/null +++ b/tests/unit/unittest/createtablesqlstatementbuildertest.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QString> + +#include <createtablesqlstatementbuilder.h> +#include <sqlstatementbuilderexception.h> + +#include <utf8stringvector.h> + +class CreateTableSqlStatementBuilder : public ::testing::Test +{ +protected: + void SetUp() override; + + void bindValues(); + static const QVector<Internal::ColumnDefinition> createColumnDefintions(); + static const Internal::ColumnDefinition createColumnDefintion(const Utf8String &name, + ColumnType type, + bool isPrimaryKey = false); + + Internal::CreateTableSqlStatementBuilder builder; +}; + +TEST_F(CreateTableSqlStatementBuilder, IsNotValidAfterCreation) +{ + ASSERT_FALSE(builder.isValid()); +} + +TEST_F(CreateTableSqlStatementBuilder, IsValidAfterBinding) +{ + bindValues(); + + ASSERT_TRUE(builder.isValid()); +} + +TEST_F(CreateTableSqlStatementBuilder, InvalidAfterClear) +{ + bindValues(); + + builder.clear(); + + ASSERT_TRUE(!builder.isValid()); +} + +TEST_F(CreateTableSqlStatementBuilder, NoSqlStatementAfterClear) +{ + bindValues(); + builder.sqlStatement(); + + builder.clear(); + + ASSERT_THROW(builder.sqlStatement(), SqlStatementBuilderException); +} + +TEST_F(CreateTableSqlStatementBuilder, SqlStatement) +{ + bindValues(); + + ASSERT_THAT(builder.sqlStatement(), + Utf8StringLiteral("CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)"));} + +TEST_F(CreateTableSqlStatementBuilder, AddColumnToExistingColumns) +{ + bindValues(); + + builder.addColumnDefinition(Utf8StringLiteral("number2"), ColumnType::Real); + + ASSERT_THAT(builder.sqlStatement(), + Utf8StringLiteral("CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC, number2 REAL)"));} + +TEST_F(CreateTableSqlStatementBuilder, ChangeTable) +{ + bindValues(); + + builder.setTable(Utf8StringLiteral("test2")); + + ASSERT_THAT(builder.sqlStatement(), + Utf8StringLiteral("CREATE TABLE IF NOT EXISTS test2(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)")); +} + +TEST_F(CreateTableSqlStatementBuilder, IsInvalidAfterClearColumsOnly) +{ + bindValues(); + builder.sqlStatement(); + + builder.clearColumns(); + + ASSERT_THROW(builder.sqlStatement(), SqlStatementBuilderException); +} + +TEST_F(CreateTableSqlStatementBuilder, ClearColumnsAndAddColumnNewColumns) +{ + bindValues(); + builder.clearColumns(); + + builder.addColumnDefinition(Utf8StringLiteral("name3"), ColumnType::Text); + builder.addColumnDefinition(Utf8StringLiteral("number3"), ColumnType::Real); + + ASSERT_THAT(builder.sqlStatement(), + Utf8StringLiteral("CREATE TABLE IF NOT EXISTS test(name3 TEXT, number3 REAL)")); +} + +TEST_F(CreateTableSqlStatementBuilder, SetWitoutRowId) +{ + bindValues(); + + builder.setUseWithoutRowId(true); + + ASSERT_THAT(builder.sqlStatement(), + Utf8StringLiteral("CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC) WITHOUT ROWID")); +} + +TEST_F(CreateTableSqlStatementBuilder, SetColumnDefinitions) +{ + builder.clear(); + builder.setTable(Utf8StringLiteral("test")); + + builder.setColumnDefinitions(createColumnDefintions()); + + ASSERT_THAT(builder.sqlStatement(), + Utf8StringLiteral("CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, name TEXT, number NUMERIC)")); +} + +void CreateTableSqlStatementBuilder::SetUp() +{ + builder = Internal::CreateTableSqlStatementBuilder(); +} + +void CreateTableSqlStatementBuilder::bindValues() +{ + builder.clear(); + builder.setTable(Utf8StringLiteral("test")); + builder.addColumnDefinition(Utf8StringLiteral("id"), ColumnType::Integer, true); + builder.addColumnDefinition(Utf8StringLiteral("name"), ColumnType::Text); + builder.addColumnDefinition(Utf8StringLiteral("number"),ColumnType:: Numeric); +} + +const QVector<Internal::ColumnDefinition> CreateTableSqlStatementBuilder::createColumnDefintions() +{ + QVector<Internal::ColumnDefinition> columnDefinitions; + columnDefinitions.append(createColumnDefintion(Utf8StringLiteral("id"), ColumnType::Integer, true)); + columnDefinitions.append(createColumnDefintion(Utf8StringLiteral("name"), ColumnType::Text)); + columnDefinitions.append(createColumnDefintion(Utf8StringLiteral("number"), ColumnType::Numeric)); + + return columnDefinitions; +} + +const Internal::ColumnDefinition CreateTableSqlStatementBuilder::createColumnDefintion(const Utf8String &name, ColumnType type, bool isPrimaryKey) +{ + Internal::ColumnDefinition columnDefinition; + + columnDefinition.setName(name); + columnDefinition.setType(type); + columnDefinition.setIsPrimaryKey(isPrimaryKey); + + return columnDefinition; +} diff --git a/tests/unit/unittest/data/complete_completer.cpp b/tests/unit/unittest/data/complete_completer.cpp new file mode 100644 index 0000000000..a764b7adb4 --- /dev/null +++ b/tests/unit/unittest/data/complete_completer.cpp @@ -0,0 +1,101 @@ +void function() +{ + +} + +class Foo; +void functionWithArguments(int i, char *c, const Foo &ref) +{ + +} + +void otherFunction() +{ + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void f() +{ + +} diff --git a/tests/unit/unittest/data/complete_completer_unsaved.cpp b/tests/unit/unittest/data/complete_completer_unsaved.cpp new file mode 100644 index 0000000000..9ec3fed8f4 --- /dev/null +++ b/tests/unit/unittest/data/complete_completer_unsaved.cpp @@ -0,0 +1,100 @@ +void function() +{ + +} + +class Foo; +void functionWithArguments(int i, char *c, const Foo &ref) +{ + +} + +void newFunction() +{ + +} + +#define Macro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void f() +{ + +} diff --git a/tests/unit/unittest/data/complete_extractor_class.cpp b/tests/unit/unittest/data/complete_extractor_class.cpp new file mode 100644 index 0000000000..becfdc3292 --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_class.cpp @@ -0,0 +1,21 @@ +class Class {}; +struct Struct{}; +union Union{}; +typedef Class TypeDef; +template<class T> class TemplateClass{}; +template<class T> class ClassTemplatePartialSpecialization; +template<class T> class ClassTemplatePartialSpecialization<T*>; + + + + + + + + + +template<class TemplateTypeParameter, template<class> class TemplateTemplateParameter> +void function() +{ + +} diff --git a/tests/unit/unittest/data/complete_extractor_constructor.cpp b/tests/unit/unittest/data/complete_extractor_constructor.cpp new file mode 100644 index 0000000000..e28ccaea82 --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_constructor.cpp @@ -0,0 +1,22 @@ +class Constructor { + Constructor(); + ~Constructor(); + + + + + + + + + + + + + + + void function() + { + + } +}; diff --git a/tests/unit/unittest/data/complete_extractor_enumeration.cpp b/tests/unit/unittest/data/complete_extractor_enumeration.cpp new file mode 100644 index 0000000000..79973a616b --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_enumeration.cpp @@ -0,0 +1,22 @@ +enum Enumeration { + Enumerator +}; + + + + + + + + + + + + + + +void function() +{ + +} + diff --git a/tests/unit/unittest/data/complete_extractor_function.cpp b/tests/unit/unittest/data/complete_extractor_function.cpp new file mode 100644 index 0000000000..a4d34fb04a --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_function.cpp @@ -0,0 +1,22 @@ +void Function(); +template<class T> void TemplateFunction(); +void FunctionWithOptional(int x, char y = 1, int z = 5); +#define FunctionMacro(X, Y) X + Y + +class base { + void NotAccessibleFunction(); +}; +class Class : public base { + void Method(); + void MethodWithParameters(int x = 30); + __attribute__((annotate("qt_slot"))) void Slot(); + __attribute__((annotate("qt_signal"))) void Signal(); + __attribute__ ((deprecated)) void DeprecatedFunction(); + void NotAvailableFunction() = delete; + +public: + void function() + { + + } +}; diff --git a/tests/unit/unittest/data/complete_extractor_function_unsaved.cpp b/tests/unit/unittest/data/complete_extractor_function_unsaved.cpp new file mode 100644 index 0000000000..adab53527b --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_function_unsaved.cpp @@ -0,0 +1,22 @@ +void Function(); +template<class T> void TemplateFunction(); + +#define FunctionMacro(X, Y) X + Y + +class base { + void NotAccessibleFunction(); +}; +class Class : public base { + void Method2(); + void MethodWithParameters(int x = 30); + __attribute__((annotate("qt_slot"))) void Slot(); + __attribute__((annotate("qt_signal"))) void Signal(); + __attribute__ ((deprecated)) void DeprecatedFunction(); + void NotAvailableFunction() = delete; + +public: + void function() + { + + } +}; diff --git a/tests/unit/unittest/data/complete_extractor_function_unsaved_2.cpp b/tests/unit/unittest/data/complete_extractor_function_unsaved_2.cpp new file mode 100644 index 0000000000..08153184f4 --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_function_unsaved_2.cpp @@ -0,0 +1,22 @@ +void Function(); +template<class T> void TemplateFunction(); + +#define FunctionMacro(X, Y) X + Y + +class base { + void NotAccessibleFunction(); +}; +class Class : public base { + void Method3(); + void MethodWithParameters(int x = 30); + __attribute__((annotate("qt_slot"))) void Slot(); + __attribute__((annotate("qt_signal"))) void Signal(); + __attribute__ ((deprecated)) void DeprecatedFunction(); + void NotAvailableFunction() = delete; + +public: + void function() + { + + } +}; diff --git a/tests/unit/unittest/data/complete_extractor_namespace.cpp b/tests/unit/unittest/data/complete_extractor_namespace.cpp new file mode 100644 index 0000000000..46d273da9d --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_namespace.cpp @@ -0,0 +1,22 @@ +namespace Namespace {} +namespace NamespaceAlias = Namespace; + + + + + + + + + + + + + + + +void function() +{ + +} + diff --git a/tests/unit/unittest/data/complete_extractor_variable.cpp b/tests/unit/unittest/data/complete_extractor_variable.cpp new file mode 100644 index 0000000000..e8a67f9c6c --- /dev/null +++ b/tests/unit/unittest/data/complete_extractor_variable.cpp @@ -0,0 +1,36 @@ +void function(int Parameter) +{ + int Var = 0; + +} + +void function2() +{ + int Var = 0; + auto Lambda = [&Var]() + { + + }; +} + +class Class { + int Field; + + void function() { + + } +}; + +template <int NonTypeTemplateParameter> +void function3() {} + +#define MacroDefinition + + +void function4() +{ +#ifdef ArgumentDefinition + int ArgumentDefinitionVariable; +#endif + +} diff --git a/tests/unit/unittest/data/complete_testfile_1.cpp b/tests/unit/unittest/data/complete_testfile_1.cpp new file mode 100644 index 0000000000..7804bc5fd6 --- /dev/null +++ b/tests/unit/unittest/data/complete_testfile_1.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +void function() +{ + +} + +class Foo; +void functionWithArguments(int i, char *c, const Foo &ref) +{ + +} + +void otherFunction() +{ + +} + +void f() +{ + +} diff --git a/tests/unit/unittest/data/complete_translationunit_parse_error.cpp b/tests/unit/unittest/data/complete_translationunit_parse_error.cpp new file mode 100644 index 0000000000..288cef2a33 --- /dev/null +++ b/tests/unit/unittest/data/complete_translationunit_parse_error.cpp @@ -0,0 +1,2 @@ +NAMESPACE { + diff --git a/tests/unit/unittest/gtest-qt-printing.cpp b/tests/unit/unittest/gtest-qt-printing.cpp new file mode 100644 index 0000000000..da82cd2595 --- /dev/null +++ b/tests/unit/unittest/gtest-qt-printing.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** 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 "gtest-qt-printing.h" diff --git a/tests/unit/unittest/gtest-qt-printing.h b/tests/unit/unittest/gtest-qt-printing.h new file mode 100644 index 0000000000..353efad9ea --- /dev/null +++ b/tests/unit/unittest/gtest-qt-printing.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 <QString> +#include <QDebug> + +#include <iostream> + +#include <gtest/gtest-printers.h> + +#ifndef GTESTQTPRINTING_H +#define GTESTQTPRINTING_H + +QT_BEGIN_NAMESPACE +class QVariant; +inline void PrintTo(const QVariant &variant, ::std::ostream *os) +{ + QString output; + QDebug debug(&output); + + debug << variant; + + *os << output.toUtf8().constData(); +} + +inline void PrintTo(const QString &text, ::std::ostream *os) +{ + *os << text.toUtf8().constData(); +} + +QT_END_NAMESPACE + +//namespace testing { +//namespace internal { + +// void PrintTo(const QVariant &variant, ::std::ostream *os); + +//} +//} +#endif // GTESTQTPRINTING_H + diff --git a/tests/unit/unittest/main.cpp b/tests/unit/unittest/main.cpp new file mode 100644 index 0000000000..a1b332e7d7 --- /dev/null +++ b/tests/unit/unittest/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" + +#include <cmbcommands.h> +#include <sqliteglobal.h> + +#include <QCoreApplication> + +int main(int argc, char *argv[]) +{ + ClangBackEnd::Commands::registerCommands(); + Sqlite::registerTypes(); + + QCoreApplication application(argc, argv); + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit/unittest/mockipclient.h b/tests/unit/unittest/mockipclient.h new file mode 100644 index 0000000000..9a09a06cc2 --- /dev/null +++ b/tests/unit/unittest/mockipclient.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef MOCKIPCLIENT_H +#define MOCKIPCLIENT_H + +#include <ipcclientinterface.h> + + +class MockIpcClient : public ClangBackEnd::IpcClientInterface { + public: + MOCK_METHOD0(alive, + void()); + MOCK_METHOD1(echo, + void(const ClangBackEnd::EchoCommand &command)); + MOCK_METHOD1(codeCompleted, + void(const ClangBackEnd::CodeCompletedCommand &command)); + MOCK_METHOD1(translationUnitDoesNotExist, + void(const ClangBackEnd::TranslationUnitDoesNotExistCommand &command)); + MOCK_METHOD1(projectPartsDoNotExist, + void(const ClangBackEnd::ProjectPartsDoNotExistCommand &command)); +}; + +#endif // MOCKIPCLIENT_H + diff --git a/tests/unit/unittest/mockipcserver.h b/tests/unit/unittest/mockipcserver.h new file mode 100644 index 0000000000..276ee1aa8a --- /dev/null +++ b/tests/unit/unittest/mockipcserver.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** 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. +** +****************************************************************************/ + +#ifndef MOCKIPCSERVER_H +#define MOCKIPCSERVER_H + +#include <ipcserverinterface.h> + +class MockIpcServer : public ClangBackEnd::IpcServerInterface { + public: + MOCK_METHOD0(end, + void()); + MOCK_METHOD1(registerTranslationUnitsForCodeCompletion, + void(const ClangBackEnd::RegisterTranslationUnitForCodeCompletionCommand &command)); + MOCK_METHOD1(unregisterTranslationUnitsForCodeCompletion, + void(const ClangBackEnd::UnregisterTranslationUnitsForCodeCompletionCommand &command)); + MOCK_METHOD1(registerProjectPartsForCodeCompletion, + void(const ClangBackEnd::RegisterProjectPartsForCodeCompletionCommand &command)); + MOCK_METHOD1(unregisterProjectPartsForCodeCompletion, + void(const ClangBackEnd::UnregisterProjectPartsForCodeCompletionCommand &command)); + MOCK_METHOD1(completeCode, + void(const ClangBackEnd::CompleteCodeCommand &command)); +}; + +#endif // MOCKIPCSERVER_H + diff --git a/tests/unit/unittest/projecttest.cpp b/tests/unit/unittest/projecttest.cpp new file mode 100644 index 0000000000..daab4e8816 --- /dev/null +++ b/tests/unit/unittest/projecttest.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** 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 "gtest/gtest.h" + +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-generated-matchers.h" +#include "gtest-qt-printing.h" + +#include <projectpart.h> +#include <utf8stringvector.h> +#include <projects.h> +#include <projectpartsdonotexistexception.h> + +#include <chrono> +#include <thread> + +using testing::ElementsAre; +using testing::StrEq; +using testing::Pointwise; +using testing::Contains; +using testing::Gt; +using testing::Not; + +namespace { + +TEST(ProjectPart, CreateProjectPart) +{ + Utf8String projectPath(Utf8StringLiteral("/tmp/blah.pro")); + + ClangBackEnd::ProjectPart project(projectPath); + + ASSERT_THAT(project.projectPartId(), projectPath); +} + +TEST(ProjectPart, CreateProjectPartWithProjectPartContainer) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro"), {Utf8StringLiteral("-O")}); + + ClangBackEnd::ProjectPart project(projectContainer); + + ASSERT_THAT(project.projectPartId(), Utf8StringLiteral("pathToProjectPart.pro")); + ASSERT_THAT(project.arguments(), Contains(StrEq("-O"))); +} + +TEST(ProjectPart, SetArguments) +{ + ClangBackEnd::ProjectPart project(Utf8StringLiteral("/tmp/blah.pro")); + + project.setArguments(Utf8StringVector({Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")})); + + ASSERT_THAT(project.arguments(), ElementsAre(StrEq("-O"), StrEq("-fast"))); +} + +TEST(ProjectPart, ArgumentCount) +{ + ClangBackEnd::ProjectPart project(Utf8StringLiteral("/tmp/blah.pro")); + + project.setArguments(Utf8StringVector({Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")})); + + ASSERT_THAT(project.argumentCount(), 2); +} + +TEST(ProjectPart, TimeStampIsUpdatedAsArgumentChanged) +{ + ClangBackEnd::ProjectPart project(Utf8StringLiteral("/tmp/blah.pro")); + auto lastChangeTimePoint = project.lastChangeTimePoint(); + std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + + project.setArguments(Utf8StringVector({Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")})); + + ASSERT_THAT(project.lastChangeTimePoint(), Gt(lastChangeTimePoint)); + +} + +TEST(ProjectPart, GetNonExistingPoject) +{ + ClangBackEnd::ProjectParts projects; + + ASSERT_THROW(projects.project(Utf8StringLiteral("pathToProjectPart.pro")), ClangBackEnd::ProjectPartDoNotExistException); +} + +TEST(ProjectPart, AddProjectParts) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro"), {Utf8StringLiteral("-O")}); + ClangBackEnd::ProjectParts projects; + + projects.createOrUpdate({projectContainer}); + + ASSERT_THAT(projects.project(projectContainer.projectPartId()), ClangBackEnd::ProjectPart(projectContainer)); + ASSERT_THAT(projects.project(projectContainer.projectPartId()).arguments(), ElementsAre(StrEq("-O"))); +} + +TEST(ProjectPart, UpdateProjectParts) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro"), {Utf8StringLiteral("-O")}); + ClangBackEnd::ProjectPartContainer projectContainerWithNewArguments(Utf8StringLiteral("pathToProjectPart.pro"), {Utf8StringLiteral("-fast")}); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + + projects.createOrUpdate({projectContainerWithNewArguments}); + + ASSERT_THAT(projects.project(projectContainer.projectPartId()), ClangBackEnd::ProjectPart(projectContainer)); + ASSERT_THAT(projects.project(projectContainer.projectPartId()).arguments(), ElementsAre(StrEq("-fast"))); +} + +TEST(ProjectPart, ThrowExceptionForAccesingRemovedProjectParts) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro"), {Utf8StringLiteral("-O")}); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + + projects.remove({projectContainer.projectPartId()}); + + ASSERT_THROW(projects.project(projectContainer.projectPartId()), ClangBackEnd::ProjectPartDoNotExistException); +} + +TEST(ProjectPart, ProjectPartProjectPartIdIsEmptyfterRemoving) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro"), {Utf8StringLiteral("-O")}); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + ClangBackEnd::ProjectPart project(projects.project(projectContainer.projectPartId())); + + projects.remove({projectContainer.projectPartId()}); + + ASSERT_TRUE(project.projectPartId().isEmpty()); +} + +TEST(Project, ThrowsForNotExistingProjectPartButRemovesAllExistingProject) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + ClangBackEnd::ProjectPart project = *projects.findProjectPart(Utf8StringLiteral("pathToProjectPart.pro")); + + EXPECT_THROW(projects.remove({Utf8StringLiteral("doesnotexist.pro"), projectContainer.projectPartId()}), ClangBackEnd::ProjectPartDoNotExistException); + + ASSERT_THAT(projects.projects(), Not(Contains(project))); +} + +TEST(ProjectPart, HasProjectPart) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + + ASSERT_TRUE(projects.hasProjectPart(projectContainer.projectPartId())); +} + +TEST(ProjectPart, DoNotHasProjectPart) +{ + ClangBackEnd::ProjectPartContainer projectContainer(Utf8StringLiteral("pathToProjectPart.pro")); + ClangBackEnd::ProjectParts projects; + projects.createOrUpdate({projectContainer}); + + ASSERT_FALSE(projects.hasProjectPart(Utf8StringLiteral("doesnotexist.pro"))); +} + + +} diff --git a/tests/unit/unittest/readandwritecommandblocktest.cpp b/tests/unit/unittest/readandwritecommandblocktest.cpp new file mode 100644 index 0000000000..d9de87b529 --- /dev/null +++ b/tests/unit/unittest/readandwritecommandblocktest.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QString> +#include <QBuffer> +#include <QVariant> +#include <vector> + +#include <cmbendcommand.h> +#include <cmbalivecommand.h> +#include <cmbcommands.h> +#include <cmbregistertranslationunitsforcodecompletioncommand.h> +#include <cmbunregistertranslationunitsforcodecompletioncommand.h> +#include <cmbcodecompletedcommand.h> +#include <cmbcompletecodecommand.h> +#include <writecommandblock.h> +#include <readcommandblock.h> + +#include "gtest-qt-printing.h" + +using namespace testing; +namespace CodeModelBackeEndTest { + +class ReadAndWriteCommandBlockTest : public ::testing::Test +{ +protected: + ReadAndWriteCommandBlockTest(); + + virtual void SetUp() override; + virtual void TearDown() override; + + template<class Type> + void CompareCommand(const Type &command); + + QVariant writeCodeCompletedCommand(); + void popLastCharacterFromBuffer(); + void pushLastCharacterToBuffer(); + void readPartialCommand(); + +protected: + QBuffer buffer; + ClangBackEnd::WriteCommandBlock writeCommandBlock; + ClangBackEnd::ReadCommandBlock readCommandBlock; + char lastCharacter = 0; +}; + +ReadAndWriteCommandBlockTest::ReadAndWriteCommandBlockTest() + : writeCommandBlock(&buffer), + readCommandBlock(&buffer) +{ +} + +void ReadAndWriteCommandBlockTest::SetUp() +{ + buffer.open(QIODevice::ReadWrite); + writeCommandBlock = ClangBackEnd::WriteCommandBlock(&buffer); + readCommandBlock = ClangBackEnd::ReadCommandBlock(&buffer); +} + +void ReadAndWriteCommandBlockTest::TearDown() +{ + buffer.close(); +} + +TEST_F(ReadAndWriteCommandBlockTest, WriteCommandAndTestSize) +{ + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + + ASSERT_EQ(46, buffer.size()); +} + +TEST_F(ReadAndWriteCommandBlockTest, WriteSecondCommandAndTestSize) +{ + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + + ASSERT_EQ(46, buffer.size()); +} + +TEST_F(ReadAndWriteCommandBlockTest, WriteTwoCommandsAndTestCount) +{ + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + + ASSERT_EQ(2, writeCommandBlock.counter()); +} + +TEST_F(ReadAndWriteCommandBlockTest, ReadThreeCommandsAndTestCount) +{ + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + writeCommandBlock.write(QVariant::fromValue(ClangBackEnd::EndCommand())); + buffer.seek(0); + + ASSERT_EQ(3, readCommandBlock.readAll().count()); +} + +TEST_F(ReadAndWriteCommandBlockTest, CompareEndCommand) +{ + CompareCommand(ClangBackEnd::EndCommand()); +} + +TEST_F(ReadAndWriteCommandBlockTest, CompareAliveCommand) +{ + CompareCommand(ClangBackEnd::AliveCommand()); +} + +TEST_F(ReadAndWriteCommandBlockTest, CompareRegisterTranslationUnitForCodeCompletionCommand) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), Utf8StringLiteral("pathToProject.pro")); + QVector<ClangBackEnd::FileContainer> fileContainers({fileContainer}); + + CompareCommand(ClangBackEnd::RegisterTranslationUnitForCodeCompletionCommand(fileContainers)); +} + +TEST_F(ReadAndWriteCommandBlockTest, CompareUnregisterFileForCodeCompletionCommand) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), Utf8StringLiteral("pathToProject.pro")); + + CompareCommand(ClangBackEnd::UnregisterTranslationUnitsForCodeCompletionCommand({fileContainer})); +} + +TEST_F(ReadAndWriteCommandBlockTest, CompareCompleteCodeCommand) +{ + CompareCommand(ClangBackEnd::CompleteCodeCommand(Utf8StringLiteral("foo.cpp"), 24, 33, Utf8StringLiteral("do what I want"))); +} + +TEST_F(ReadAndWriteCommandBlockTest, CompareCodeCompletedCommand) +{ + QVector<ClangBackEnd::CodeCompletion> codeCompletions({Utf8StringLiteral("newFunction()")}); + + CompareCommand(ClangBackEnd::CodeCompletedCommand(codeCompletions, 1)); +} + +TEST_F(ReadAndWriteCommandBlockTest, GetInvalidCommandForAPartialBuffer) +{ + writeCodeCompletedCommand(); + popLastCharacterFromBuffer(); + buffer.seek(0); + + readPartialCommand(); +} + +TEST_F(ReadAndWriteCommandBlockTest, ReadCommandAfterInterruption) +{ + const QVariant writeCommand = writeCodeCompletedCommand(); + popLastCharacterFromBuffer(); + buffer.seek(0); + readPartialCommand(); + pushLastCharacterToBuffer(); + + ASSERT_EQ(readCommandBlock.read(), writeCommand); +} + +QVariant ReadAndWriteCommandBlockTest::writeCodeCompletedCommand() +{ + ClangBackEnd::CodeCompletedCommand command(QVector<ClangBackEnd::CodeCompletion>({Utf8StringLiteral("newFunction()")}), 1); + const QVariant writeCommand = QVariant::fromValue(command); + writeCommandBlock.write(writeCommand); + + return writeCommand; +} + +void ReadAndWriteCommandBlockTest::popLastCharacterFromBuffer() +{ + auto &internalBuffer = buffer.buffer(); + lastCharacter = internalBuffer.at(internalBuffer.size() - 1); + internalBuffer.chop(1); +} + +void ReadAndWriteCommandBlockTest::pushLastCharacterToBuffer() +{ + buffer.buffer().push_back(lastCharacter); +} + +void ReadAndWriteCommandBlockTest::readPartialCommand() +{ + QVariant readCommand = readCommandBlock.read(); + + ASSERT_FALSE(readCommand.isValid()); +} + +template<class Type> +void ReadAndWriteCommandBlockTest::CompareCommand(const Type &command) +{ + const QVariant writeCommand = QVariant::fromValue(command); + writeCommandBlock.write(writeCommand); + buffer.seek(0); + + const QVariant readCommand = readCommandBlock.read(); + + ASSERT_EQ(writeCommand, readCommand); +} + +} diff --git a/tests/unit/unittest/spydummy.cpp b/tests/unit/unittest/spydummy.cpp new file mode 100644 index 0000000000..758c8b0762 --- /dev/null +++ b/tests/unit/unittest/spydummy.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** 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 "spydummy.h" + +SpyDummy::SpyDummy(QObject *parent) : QObject(parent) +{ + +} + +SpyDummy::~SpyDummy() +{ + +} + diff --git a/tests/unit/unittest/spydummy.h b/tests/unit/unittest/spydummy.h new file mode 100644 index 0000000000..6d17f49472 --- /dev/null +++ b/tests/unit/unittest/spydummy.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#ifndef SPYDUMMY_H +#define SPYDUMMY_H + +#include <QObject> + +class SpyDummy : public QObject +{ + Q_OBJECT +public: + explicit SpyDummy(QObject *parent = 0); + ~SpyDummy(); + +signals: + void tableIsReady(); + void databaseIsOpened(); + void databaseIsClosed(); +}; + +#endif // SPYDUMMY_H diff --git a/tests/unit/unittest/sqlitecolumntest.cpp b/tests/unit/unittest/sqlitecolumntest.cpp new file mode 100644 index 0000000000..a716dc5f3d --- /dev/null +++ b/tests/unit/unittest/sqlitecolumntest.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 <gtest/gtest.h> +#include <gmock/gmock-matchers.h> +#include "gtest-qt-printing.h" + +#include <sqlitecolumn.h> + +namespace { + +class SqliteColumn : public ::testing::Test +{ +protected: + void SetUp() override; + + ::SqliteColumn column; +}; + +TEST_F(SqliteColumn, ChangeName) +{ + column.setName(Utf8StringLiteral("Claudia")); + + ASSERT_THAT(column.name(), Utf8StringLiteral("Claudia")); +} + +TEST_F(SqliteColumn, DefaultType) +{ + ASSERT_THAT(column.type(), ColumnType::Numeric); +} + +TEST_F(SqliteColumn, ChangeType) +{ + column.setType(ColumnType::Text); + + ASSERT_THAT(column.type(), ColumnType::Text); +} + +TEST_F(SqliteColumn, DefaultPrimaryKey) +{ + ASSERT_FALSE(column.isPrimaryKey()); +} + +TEST_F(SqliteColumn, SetPrimaryKey) +{ + column.setIsPrimaryKey(true); + + ASSERT_TRUE(column.isPrimaryKey()); +} + +TEST_F(SqliteColumn, GetColumnDefinition) +{ + column.setName(Utf8StringLiteral("Claudia")); + + Internal::ColumnDefinition columnDefintion = column.columnDefintion(); + + ASSERT_THAT(columnDefintion.name(), Utf8StringLiteral("Claudia")); + ASSERT_THAT(columnDefintion.type(), ColumnType::Numeric); + ASSERT_FALSE(columnDefintion.isPrimaryKey()); +} + +void SqliteColumn::SetUp() +{ + column.clear(); +} + +} diff --git a/tests/unit/unittest/sqlitedatabasebackendtest.cpp b/tests/unit/unittest/sqlitedatabasebackendtest.cpp new file mode 100644 index 0000000000..c023d85268 --- /dev/null +++ b/tests/unit/unittest/sqlitedatabasebackendtest.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QDir> + +#include "sqlitedatabasebackend.h" +#include "sqliteexception.h" +#include "sqlitewritestatement.h" + +namespace { + +class SqliteDatabaseBackend : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + QString databaseFilePath = QDir::tempPath() + QStringLiteral("/SqliteDatabaseBackendTest.db"); + ::SqliteDatabaseBackend databaseBackend; +}; + +TEST_F(SqliteDatabaseBackend, OpenAlreadyOpenDatabase) +{ + ASSERT_THROW(databaseBackend.open(databaseFilePath), SqliteException); +} + +TEST_F(SqliteDatabaseBackend, CloseAlreadyClosedDatabase) +{ + databaseBackend.close(); + ASSERT_THROW(databaseBackend.close(), SqliteException); +} + +TEST_F(SqliteDatabaseBackend, OpenWithWrongPath) +{ + ASSERT_THROW(databaseBackend.open(QStringLiteral("/xxx/SqliteDatabaseBackendTest.db")), SqliteException); +} + +TEST_F(SqliteDatabaseBackend, DefaultJournalMode) +{ + ASSERT_THAT(databaseBackend.journalMode(), JournalMode::Delete); +} + +TEST_F(SqliteDatabaseBackend, WalJournalMode) +{ + databaseBackend.setJournalMode(JournalMode::Wal); + + ASSERT_THAT(databaseBackend.journalMode(), JournalMode::Wal); +} + +TEST_F(SqliteDatabaseBackend, TruncateJournalMode) +{ + databaseBackend.setJournalMode(JournalMode::Truncate); + + ASSERT_THAT(databaseBackend.journalMode(), JournalMode::Truncate); +} + +TEST_F(SqliteDatabaseBackend, MemoryJournalMode) +{ + databaseBackend.setJournalMode(JournalMode::Memory); + + ASSERT_THAT(databaseBackend.journalMode(), JournalMode::Memory); +} + +TEST_F(SqliteDatabaseBackend, PersistJournalMode) +{ + databaseBackend.setJournalMode(JournalMode::Persist); + + ASSERT_THAT(databaseBackend.journalMode(), JournalMode::Persist); +} + +TEST_F(SqliteDatabaseBackend, DefaultTextEncoding) +{ + ASSERT_THAT(databaseBackend.textEncoding(), Utf8); +} + +TEST_F(SqliteDatabaseBackend, Utf16TextEncoding) +{ + databaseBackend.setTextEncoding(Utf16); + + ASSERT_THAT(databaseBackend.textEncoding(), Utf16); +} + +TEST_F(SqliteDatabaseBackend, Utf16beTextEncoding) +{ + databaseBackend.setTextEncoding(Utf16be); + + ASSERT_THAT(databaseBackend.textEncoding(), Utf16be); +} + +TEST_F(SqliteDatabaseBackend, Utf16leTextEncoding) +{ + databaseBackend.setTextEncoding(Utf16le); + + ASSERT_THAT(databaseBackend.textEncoding(), Utf16le); +} + +TEST_F(SqliteDatabaseBackend, Utf8TextEncoding) +{ + databaseBackend.setTextEncoding(Utf8); + + ASSERT_THAT(databaseBackend.textEncoding(), Utf8); +} + +TEST_F(SqliteDatabaseBackend, TextEncodingCannotBeChangedAfterTouchingDatabase) +{ + databaseBackend.setJournalMode(JournalMode::Memory); + + SqliteWriteStatement::execute(Utf8StringLiteral("CREATE TABLE text(name, number)")); + + ASSERT_THROW(databaseBackend.setTextEncoding(Utf16), SqliteException); +} + +void SqliteDatabaseBackend::SetUp() +{ + QDir::temp().remove(QStringLiteral("SqliteDatabaseBackendTest.db")); + databaseBackend.open(databaseFilePath); +} + +void SqliteDatabaseBackend::TearDown() +{ + databaseBackend.closeWithoutException(); +} +} diff --git a/tests/unit/unittest/sqlitedatabasetest.cpp b/tests/unit/unittest/sqlitedatabasetest.cpp new file mode 100644 index 0000000000..b4c979fe17 --- /dev/null +++ b/tests/unit/unittest/sqlitedatabasetest.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QVariant> +#include <QSignalSpy> + +#include <sqlitedatabase.h> +#include <sqlitetable.h> +#include <utf8string.h> + +#include "spydummy.h" + +namespace { + +class SqliteDatabase : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + SpyDummy spyDummy; + QString databaseFilePath = QStringLiteral(":memory:"); + ::SqliteDatabase database; +}; + +TEST_F(SqliteDatabase, SetDatabaseFilePath) +{ + ASSERT_THAT(database.databaseFilePath(), databaseFilePath); +} + +TEST_F(SqliteDatabase, SetJournalMode) +{ + database.setJournalMode(JournalMode::Memory); + + ASSERT_THAT(database.journalMode(), JournalMode::Memory); +} + +TEST_F(SqliteDatabase, OpenDatabase) +{ + database.close(); + QSignalSpy signalSpy(&spyDummy, &SpyDummy::databaseIsOpened); + database.open(); + + ASSERT_TRUE(signalSpy.wait(100000)); + ASSERT_TRUE(database.isOpen()); +} + +TEST_F(SqliteDatabase, CloseDatabase) +{ + QSignalSpy signalSpy(&spyDummy, &SpyDummy::databaseIsClosed); + + database.close(); + + ASSERT_TRUE(signalSpy.wait(100000)); + ASSERT_FALSE(database.isOpen()); +} + +TEST_F(SqliteDatabase, AddTable) +{ + SqliteTable *sqliteTable = new SqliteTable; + + database.addTable(sqliteTable); + + ASSERT_THAT(database.tables().first(), sqliteTable); +} + +void SqliteDatabase::SetUp() +{ + QObject::connect(&database, &::SqliteDatabase::databaseIsOpened, &spyDummy, &SpyDummy::databaseIsOpened); + QObject::connect(&database, &::SqliteDatabase::databaseIsClosed, &spyDummy, &SpyDummy::databaseIsClosed); + + database.setJournalMode(JournalMode::Memory); + database.setDatabaseFilePath(databaseFilePath); +} + +void SqliteDatabase::TearDown() +{ + database.close(); +} +} diff --git a/tests/unit/unittest/sqlitestatementtest.cpp b/tests/unit/unittest/sqlitestatementtest.cpp new file mode 100644 index 0000000000..5e218168ee --- /dev/null +++ b/tests/unit/unittest/sqlitestatementtest.cpp @@ -0,0 +1,570 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QDir> +#include <QVariant> +#include <QStringList> +#include <QByteArray> +#include <QMap> +#include <QString> + +#include <sqlitereadwritestatement.h> +#include <sqlitereadstatement.h> +#include <sqlitewritestatement.h> +#include <sqlitedatabasebackend.h> +#include <utf8string.h> + +namespace { +class SqliteStatement : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + SqliteDatabaseBackend databaseBackend; +}; + +TEST_F(SqliteStatement, PrepareFailure) +{ + ASSERT_THROW(SqliteReadStatement(Utf8StringLiteral("blah blah blah")), SqliteException); + ASSERT_THROW(SqliteWriteStatement(Utf8StringLiteral("blah blah blah")), SqliteException); + ASSERT_THROW(SqliteReadStatement(Utf8StringLiteral("INSERT INTO test(name, number) VALUES (?, ?)")), SqliteException); + ASSERT_THROW(SqliteWriteStatement(Utf8StringLiteral("SELECT name, number FROM test '")), SqliteException); +} + +TEST_F(SqliteStatement, CountRows) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT * FROM test")); + int nextCount = 0; + while (statement.next()) + ++nextCount; + + int sqlCount = SqliteReadStatement::toValue<int>(Utf8StringLiteral("SELECT count(*) FROM test")); + + ASSERT_THAT(nextCount, sqlCount); +} + +TEST_F(SqliteStatement, Value) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test ORDER BY name")); + + statement.next(); + + ASSERT_THAT(statement.value<QVariant>(1).type(), QVariant::ByteArray); + + statement.next(); + + ASSERT_THAT(statement.value<int>(0), 0); + ASSERT_THAT(statement.value<qint64>(0), 0); + ASSERT_THAT(statement.value<double>(0), 0.0); + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("foo")); + ASSERT_THAT(statement.value<Utf8String>(0), Utf8StringLiteral("foo")); + ASSERT_THAT(statement.value<QVariant>(0), QVariant::fromValue(QStringLiteral("foo"))); + ASSERT_THAT(statement.value<QVariant>(0).type(), QVariant::String); + + ASSERT_THAT(statement.value<int>(1), 23); + ASSERT_THAT(statement.value<qint64>(1), 23); + ASSERT_THAT(statement.value<double>(1), 23.3); + ASSERT_THAT(statement.value<QString>(1), QStringLiteral("23.3")); + ASSERT_THAT(statement.value<Utf8String>(1), Utf8StringLiteral("23.3")); + ASSERT_THAT(statement.value<QVariant>(1), QVariant::fromValue(23.3)); + ASSERT_THAT(statement.value<QVariant>(1).type(), QVariant::Double); + + statement.next(); + + ASSERT_THAT(statement.value<QVariant>(1).type(), QVariant::LongLong); +} + +TEST_F(SqliteStatement, ValueFailure) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test")); + ASSERT_THROW(statement.value<int>(0), SqliteException); + + statement.reset(); + + while (statement.next()) {} + ASSERT_THROW(statement.value<int>(0), SqliteException); + + statement.reset(); + + statement.next(); + ASSERT_THROW(statement.value<int>(-1), SqliteException); + ASSERT_THROW(statement.value<int>(2), SqliteException); +} + +TEST_F(SqliteStatement, ToIntergerValue) +{ + ASSERT_THAT(SqliteReadStatement::toValue<int>(Utf8StringLiteral("SELECT number FROM test WHERE name='foo'")), 23); +} + +TEST_F(SqliteStatement, ToLongIntergerValue) +{ + ASSERT_THAT(SqliteReadStatement::toValue<qint64>(Utf8StringLiteral("SELECT number FROM test WHERE name='foo'")), 23LL); +} + +TEST_F(SqliteStatement, ToDoubleValue) +{ + ASSERT_THAT(SqliteReadStatement::toValue<double>(Utf8StringLiteral("SELECT number FROM test WHERE name='foo'")), 23.3); +} + +TEST_F(SqliteStatement, ToQStringValue) +{ + ASSERT_THAT(SqliteReadStatement::toValue<QString>(Utf8StringLiteral("SELECT name FROM test WHERE name='foo'")), QStringLiteral("foo")); +} + +TEST_F(SqliteStatement, ToUtf8StringValue) +{ + ASSERT_THAT(SqliteReadStatement::toValue<Utf8String>(Utf8StringLiteral("SELECT name FROM test WHERE name='foo'")), Utf8StringLiteral("foo")); +} + +TEST_F(SqliteStatement, ToQByteArrayValueIsNull) +{ + ASSERT_TRUE(SqliteReadStatement::toValue<QByteArray>(Utf8StringLiteral("SELECT name FROM test WHERE name='foo'")).isNull()); +} + +TEST_F(SqliteStatement, ToQVariantValue) +{ + ASSERT_THAT(SqliteReadStatement::toValue<QVariant>(Utf8StringLiteral("SELECT name FROM test WHERE name='foo'")), QVariant::fromValue(QStringLiteral("foo"))); +} + +TEST_F(SqliteStatement, Utf8Values) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test ORDER by name")); + + Utf8StringVector values = statement.values<Utf8StringVector>(); + ASSERT_THAT(values.count(), 3); + ASSERT_THAT(values.at(0), Utf8StringLiteral("bar")); + ASSERT_THAT(values.at(1), Utf8StringLiteral("foo")); + ASSERT_THAT(values.at(2), Utf8StringLiteral("poo")); +} +TEST_F(SqliteStatement, DoubleValues) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test ORDER by name")); + + QVector<double> values = statement.values<QVector<double>>(1); + + ASSERT_THAT(values.count(), 3); + ASSERT_THAT(values.at(0), 0.0); + ASSERT_THAT(values.at(1), 23.3); + ASSERT_THAT(values.at(2), 40.0); +} + +TEST_F(SqliteStatement, QVariantValues) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test ORDER by name")); + + QVector<QVariant> values = statement.values<QVector<QVariant>>(QVector<int>() << 0 << 1); + + ASSERT_THAT(values.count(), 6); + ASSERT_THAT(values.at(0), QVariant::fromValue(QStringLiteral("bar"))); + ASSERT_THAT(values.at(1), QVariant::fromValue(QByteArray::fromHex("0500"))); + ASSERT_THAT(values.at(2), QVariant::fromValue(QStringLiteral("foo"))); + ASSERT_THAT(values.at(3), QVariant::fromValue(23.3)); + ASSERT_THAT(values.at(4), QVariant::fromValue(QStringLiteral("poo"))); + ASSERT_THAT(values.at(5), QVariant::fromValue(40)); +} + +TEST_F(SqliteStatement, RowColumnValueMapCountForValidRow) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE rowid=1")); + + QMap<QString, QVariant> values = statement.rowColumnValueMap(); + + ASSERT_THAT(values.count(), 2); +} + +TEST_F(SqliteStatement, RowColumnValueMapCountForInvalidRow) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE rowid=100")); + + QMap<QString, QVariant> values = statement.rowColumnValueMap(); + + ASSERT_THAT(values.count(), 0); +} + +TEST_F(SqliteStatement, RowColumnValueMapValues) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE rowid=2")); + + QMap<QString, QVariant> values = statement.rowColumnValueMap(); + + ASSERT_THAT(values.value(QStringLiteral("name")).toString(), QStringLiteral("foo")); + ASSERT_THAT(values.value(QStringLiteral("number")).toDouble(), 23.3); +} + +TEST_F(SqliteStatement, TwoColumnValueMapCount) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test")); + + QMap<QString, QVariant> values = statement.twoColumnValueMap(); + + ASSERT_THAT(values.count(), 3); +} + +TEST_F(SqliteStatement, TwoColumnValueMapValues) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test")); + + QMap<QString, QVariant> values = statement.twoColumnValueMap(); + + ASSERT_THAT(values.value(QStringLiteral("foo")).toDouble(), 23.3); +} + +TEST_F(SqliteStatement, ValuesFailure) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test")); + + ASSERT_THROW(statement.values<QVector<QVariant>>(QVector<int>() << 1 << 2);, SqliteException); + ASSERT_THROW(statement.values<QVector<QVariant>>(QVector<int>() << -1 << 1);, SqliteException); +} + +TEST_F(SqliteStatement, ColumnNames) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test")); + + Utf8StringVector columnNames = statement.columnNames(); + + ASSERT_THAT(columnNames.count(), statement.columnCount()); + + ASSERT_THAT(columnNames.at(0), Utf8StringLiteral("name")); + ASSERT_THAT(columnNames.at(1), Utf8StringLiteral("number")); +} + +TEST_F(SqliteStatement, BindQString) +{ + + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE name=?")); + + statement.bind(1, QStringLiteral("foo")); + + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("foo")); + ASSERT_THAT(statement.value<double>(1), 23.3); +} + +TEST_F(SqliteStatement, BindInteger) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, 40); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); +} + +TEST_F(SqliteStatement, BindLongInteger) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, qint64(40)); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); +} + +TEST_F(SqliteStatement, BindByteArray) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, QByteArray::fromHex("0500")); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("bar")); +} + +TEST_F(SqliteStatement, BindDouble) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, 23.3); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("foo")); +} + +TEST_F(SqliteStatement, BindIntergerQVariant) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, QVariant::fromValue(40)); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); +} + +TEST_F(SqliteStatement, BindLongIntergerQVariant) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, QVariant::fromValue(qint64(40))); + statement.next(); + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); +} + +TEST_F(SqliteStatement, BindDoubleQVariant) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, QVariant::fromValue(23.3)); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("foo")); + } + +TEST_F(SqliteStatement, BindByteArrayQVariant) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=?")); + + statement.bind(1, QVariant::fromValue(QByteArray::fromHex("0500"))); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("bar")); +} + +TEST_F(SqliteStatement, BindIntegerByParameter) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=@number")); + + statement.bind(Utf8StringLiteral("@number"), 40); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); +} + +TEST_F(SqliteStatement, BindLongIntegerByParameter) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=@number")); + + statement.bind(Utf8StringLiteral("@number"), qint64(40)); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); +} + +TEST_F(SqliteStatement, BindByteArrayByParameter) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=@number")); + + statement.bind(Utf8StringLiteral("@number"), QByteArray::fromHex("0500")); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("bar")); +} + +TEST_F(SqliteStatement, BindDoubleByIndex) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=@number")); + + statement.bind(statement.bindingIndexForName(Utf8StringLiteral("@number")), 23.3); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("foo")); +} + +TEST_F(SqliteStatement, BindQVariantByIndex) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=@number")); + + statement.bind(statement.bindingIndexForName(Utf8StringLiteral("@number")), QVariant::fromValue((40))); + statement.next(); + + ASSERT_THAT(statement.value<QString>(0), QStringLiteral("poo")); + +} + +TEST_F(SqliteStatement, BindFailure) +{ + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE number=@number")); + + ASSERT_THROW(statement.bind(0, 40), SqliteException); + ASSERT_THROW(statement.bind(2, 40), SqliteException); + ASSERT_THROW(statement.bind(Utf8StringLiteral("@name"), 40), SqliteException); +} + +TEST_F(SqliteStatement, RequestBindingNamesFromStatement) +{ + Utf8StringVector expectedValues({Utf8StringLiteral("name"), Utf8StringLiteral("number"), Utf8StringLiteral("id")}); + + SqliteWriteStatement statement(Utf8StringLiteral("UPDATE test SET name=@name, number=@number WHERE rowid=@id")); + + ASSERT_THAT(statement.bindingColumnNames(), expectedValues); +} + +TEST_F(SqliteStatement, WriteUpdateWidthUnamedParameter) +{ + { + int startTotalCount = databaseBackend.totalChangesCount(); + RowDictionary firstValueMap; + firstValueMap.insert(Utf8StringLiteral("name"), QStringLiteral("foo")); + firstValueMap.insert(Utf8StringLiteral("number"), 66.6); + + RowDictionary secondValueMap; + secondValueMap.insert(Utf8StringLiteral("name"), QStringLiteral("bar")); + secondValueMap.insert(Utf8StringLiteral("number"), 77.7); + + SqliteWriteStatement statement(Utf8StringLiteral("UPDATE test SET number=? WHERE name=?")); + statement.setBindingColumnNames(Utf8StringVector() << Utf8StringLiteral("number") << Utf8StringLiteral("name")); + + statement.write(firstValueMap); + + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 1); + + statement.write(firstValueMap); + + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 2); + + statement.write(secondValueMap); + + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 3); + } + + { + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE name='foo'")); + + statement.next(); + + ASSERT_THAT(statement.value<double>(1), 66.6); + } +} + +TEST_F(SqliteStatement, WriteUpdateWidthNamedParameter) +{ + { + int startTotalCount = databaseBackend.totalChangesCount(); + RowDictionary firstValueMap; + firstValueMap.insert(Utf8StringLiteral("name"), QStringLiteral("foo")); + firstValueMap.insert(Utf8StringLiteral("number"), 99.9); + + SqliteWriteStatement statement(Utf8StringLiteral("UPDATE test SET number=@number WHERE name=@name")); + statement.write(firstValueMap); + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 1); + } + + { + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE name='foo'")); + statement.next(); + ASSERT_THAT(statement.value<double>(1), 99.9); + } +} + +TEST_F(SqliteStatement, WriteUpdateWidthNamedParameterAndBindNotAllParameter) +{ + { + int startTotalCount = databaseBackend.totalChangesCount(); + RowDictionary firstValueMap; + firstValueMap.insert(Utf8StringLiteral("name"), QStringLiteral("foo")); + + SqliteWriteStatement statement(Utf8StringLiteral("UPDATE test SET number=@number WHERE name=@name")); + statement.writeUnchecked(firstValueMap); + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 1); + } + + { + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE name='foo'")); + statement.next(); + ASSERT_THAT(statement.value<double>(1), 0.0); + } +} + +TEST_F(SqliteStatement, WriteInsert) +{ + { + int startTotalCount = databaseBackend.totalChangesCount(); + + RowDictionary valueMap; + valueMap.insert(Utf8StringLiteral("name"), QStringLiteral("jane")); + valueMap.insert(Utf8StringLiteral("number"), 232.3); + + SqliteWriteStatement statement(Utf8StringLiteral("INSERT OR IGNORE INTO test(name, number) VALUES (?, ?)")); + statement.setBindingColumnNames(Utf8StringVector() << Utf8StringLiteral("name") << Utf8StringLiteral("number")); + statement.write(valueMap); + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 1); + statement.write(valueMap); + ASSERT_THAT(databaseBackend.totalChangesCount(), startTotalCount + 1); + } + + { + SqliteReadStatement statement(Utf8StringLiteral("SELECT name, number FROM test WHERE name='jane'")); + statement.next(); + ASSERT_THAT(statement.value<double>(1), 232.3); + } +} + +TEST_F(SqliteStatement, WriteFailure) +{ + { + RowDictionary valueMap; + valueMap.insert(Utf8StringLiteral("name"), QStringLiteral("foo")); + valueMap.insert(Utf8StringLiteral("number"), 323.3); + + SqliteWriteStatement statement(Utf8StringLiteral("INSERT INTO test(name, number) VALUES (?, ?)")); + statement.setBindingColumnNames(Utf8StringVector() << Utf8StringLiteral("name") << Utf8StringLiteral("number")); + ASSERT_THROW(statement.write(valueMap), SqliteException); + } + + { + RowDictionary valueMap; + valueMap.insert(Utf8StringLiteral("name"), QStringLiteral("bar")); + + SqliteWriteStatement statement(Utf8StringLiteral("INSERT OR IGNORE INTO test(name, number) VALUES (?, ?)")); + statement.setBindingColumnNames(Utf8StringVector() << Utf8StringLiteral("name") << Utf8StringLiteral("number")); + ASSERT_THROW(statement.write(valueMap), SqliteException); + } +} + +TEST_F(SqliteStatement, ClosedDatabase) +{ + databaseBackend.close(); + ASSERT_THROW(SqliteWriteStatement(Utf8StringLiteral("INSERT INTO test(name, number) VALUES (?, ?)")), SqliteException); + ASSERT_THROW(SqliteReadStatement(Utf8StringLiteral("SELECT * FROM test")), SqliteException); + ASSERT_THROW(SqliteReadWriteStatement(Utf8StringLiteral("INSERT INTO test(name, number) VALUES (?, ?)")), SqliteException); + databaseBackend.open(QDir::tempPath() + QStringLiteral("/SqliteStatementTest.db")); +} + +void SqliteStatement::SetUp() +{ + databaseBackend.open(QStringLiteral(":memory:")); + SqliteWriteStatement::execute(Utf8StringLiteral("CREATE TABLE test(name TEXT UNIQUE, number NUMERIC)")); + SqliteWriteStatement::execute(Utf8StringLiteral("INSERT INTO test VALUES ('bar', x'0500')")); + SqliteWriteStatement::execute(Utf8StringLiteral("INSERT INTO test VALUES ('foo', 23.3)")); + SqliteWriteStatement::execute(Utf8StringLiteral("INSERT INTO test VALUES ('poo', 40)")); +} + +void SqliteStatement::TearDown() +{ + databaseBackend.close(); +} + +} diff --git a/tests/unit/unittest/sqlitetabletest.cpp b/tests/unit/unittest/sqlitetabletest.cpp new file mode 100644 index 0000000000..fa5e671163 --- /dev/null +++ b/tests/unit/unittest/sqlitetabletest.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QVariant> +#include <QSignalSpy> + +#include <sqlitedatabase.h> +#include <sqlitetable.h> +#include <sqlitecolumn.h> +#include <utf8string.h> + +#include "spydummy.h" + +namespace { + +class SqliteTable : public ::testing::Test +{ +protected: + void SetUp() override; + void TearDown() override; + + SqliteColumn *addColumn(const Utf8String &columnName); + + SpyDummy spyDummy; + SqliteDatabase *database = nullptr; + ::SqliteTable *table = nullptr; + Utf8String tableName = Utf8StringLiteral("testTable"); +}; + + +TEST_F(SqliteTable, ColumnIsAddedToTable) +{ + table->setUseWithoutRowId(true); + + ASSERT_TRUE(table->useWithoutRowId()); +} + +TEST_F(SqliteTable, SetTableName) +{ + table->setName(tableName); + + ASSERT_THAT(table->name(), tableName); +} + +TEST_F(SqliteTable, SetUseWithoutRowid) +{ + table->setUseWithoutRowId(true); + + ASSERT_TRUE(table->useWithoutRowId()); +} + +TEST_F(SqliteTable, TableIsReadyAfterOpenDatabase) +{ + QSignalSpy signalSpy(&spyDummy, &SpyDummy::tableIsReady); + table->setName(tableName); + addColumn(Utf8StringLiteral("name")); + + database->open(); + + ASSERT_TRUE(signalSpy.wait(100000)); +} + +void SqliteTable::SetUp() +{ + table = new ::SqliteTable; + QObject::connect(table, &::SqliteTable::tableIsReady, &spyDummy, &SpyDummy::tableIsReady); + + database = new SqliteDatabase; + database->setJournalMode(JournalMode::Memory); + database->setDatabaseFilePath( QStringLiteral(":memory:")); + database->addTable(table); +} + +void SqliteTable::TearDown() +{ + database->close(); + delete database; + database = nullptr; + table = nullptr; +} + +SqliteColumn *SqliteTable::addColumn(const Utf8String &columnName) +{ + SqliteColumn *newSqliteColum = new SqliteColumn; + + newSqliteColum->setName(columnName); + + table->addColumn(newSqliteColum); + + return newSqliteColum; +} +} diff --git a/tests/unit/unittest/sqlstatementbuildertest.cpp b/tests/unit/unittest/sqlstatementbuildertest.cpp new file mode 100644 index 0000000000..1f04c64f5f --- /dev/null +++ b/tests/unit/unittest/sqlstatementbuildertest.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QString> + +#include <sqlstatementbuilder.h> +#include <sqlstatementbuilderexception.h> +#include <utf8stringvector.h> + +using namespace ::testing; + +TEST(SqlStatementBuilder, Bind) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table WHERE $column = 'foo' AND rowid=$row AND rowid IN ($rows)")); + + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringVector() << Utf8StringLiteral("name") << Utf8StringLiteral("number")); + sqlStatementBuilder.bind(Utf8StringLiteral("$column"), Utf8StringLiteral("name")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + sqlStatementBuilder.bind(Utf8StringLiteral("$row"), 20); + sqlStatementBuilder.bind(Utf8StringLiteral("$rows"), QVector<int>() << 1 << 2 << 3); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("SELECT name, number FROM test WHERE name = 'foo' AND rowid=20 AND rowid IN (1, 2, 3)")); +} + +TEST(SqlStatementBuilder, BindEmpty) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table$emptyPart")); + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringVector() << Utf8StringLiteral("name") << Utf8StringLiteral("number")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + + sqlStatementBuilder.bindEmptyText(Utf8StringLiteral("$emptyPart")); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("SELECT name, number FROM test")); +} + +TEST(SqlStatementBuilder, BindFailure) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table")); + + Utf8StringVector columns; + + ASSERT_THROW(sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringLiteral("")), SqlStatementBuilderException); + ASSERT_THROW(sqlStatementBuilder.bind(Utf8StringLiteral("columns"), Utf8StringLiteral("test")), SqlStatementBuilderException); + ASSERT_THROW(sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), columns), SqlStatementBuilderException); + ASSERT_THROW(sqlStatementBuilder.bindWithInsertTemplateParameters(Utf8StringLiteral("$columns"), columns), SqlStatementBuilderException); + ASSERT_THROW(sqlStatementBuilder.bindWithUpdateTemplateParameters(Utf8StringLiteral("$columns"), columns), SqlStatementBuilderException); +} + +TEST(SqlStatementBuilder, BindWithInsertTemplateParameters) +{ + Utf8StringVector columns({Utf8StringLiteral("name"), Utf8StringLiteral("number")}); + + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("INSERT OR IGNORE INTO $table ($columns) VALUES ($values)")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), columns); + sqlStatementBuilder.bindWithInsertTemplateParameters(Utf8StringLiteral("$values"), columns); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("INSERT OR IGNORE INTO test (name, number) VALUES (?, ?)")); +} + +TEST(SqlStatementBuilder, BindWithUpdateTemplateParameters) +{ + Utf8StringVector columns({Utf8StringLiteral("name"), Utf8StringLiteral("number")}); + + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("UPDATE $table SET $columnValues WHERE id=?")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + sqlStatementBuilder.bindWithUpdateTemplateParameters(Utf8StringLiteral("$columnValues"), columns); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("UPDATE test SET name=?, number=? WHERE id=?")); +} + +TEST(SqlStatementBuilder, BindWithUpdateTemplateNames) +{ + Utf8StringVector columns({Utf8StringLiteral("name"), Utf8StringLiteral("number")}); + + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("UPDATE $table SET $columnValues WHERE id=@id")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + sqlStatementBuilder.bindWithUpdateTemplateNames(Utf8StringLiteral("$columnValues"), columns); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("UPDATE test SET name=@name, number=@number WHERE id=@id")); +} + +TEST(SqlStatementBuilder, ClearOnRebinding) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table")); + + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringLiteral("name, number")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("SELECT name, number FROM test")); + + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test2")); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("SELECT name, number FROM test2")); +} + +TEST(SqlStatementBuilder, ClearBinding) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table")); + + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringLiteral("name, number")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("SELECT name, number FROM test")); + + sqlStatementBuilder.clear(); + + ASSERT_THROW(sqlStatementBuilder.sqlStatement(), SqlStatementBuilderException); +} + +TEST(SqlStatementBuilder, ColumnType) +{ + ASSERT_THAT(SqlStatementBuilder::columnTypeToString(ColumnType::Numeric), Utf8StringLiteral("NUMERIC")); + ASSERT_THAT(SqlStatementBuilder::columnTypeToString(ColumnType::Integer), Utf8StringLiteral("INTEGER")); + ASSERT_THAT(SqlStatementBuilder::columnTypeToString(ColumnType::Real), Utf8StringLiteral("REAL")); + ASSERT_THAT(SqlStatementBuilder::columnTypeToString(ColumnType::Text), Utf8StringLiteral("TEXT")); + ASSERT_TRUE(SqlStatementBuilder::columnTypeToString(ColumnType::None).isEmpty()); +} + +TEST(SqlStatementBuilder, SqlStatementFailure) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table")); + + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringLiteral("name, number")); + + ASSERT_THROW(sqlStatementBuilder.sqlStatement(), SqlStatementBuilderException); +} + +TEST(SqlStatementBuilder, IsBuild) +{ + SqlStatementBuilder sqlStatementBuilder(Utf8StringLiteral("SELECT $columns FROM $table")); + + sqlStatementBuilder.bind(Utf8StringLiteral("$columns"), Utf8StringLiteral("name, number")); + sqlStatementBuilder.bind(Utf8StringLiteral("$table"), Utf8StringLiteral("test")); + + ASSERT_FALSE(sqlStatementBuilder.isBuild()); + + ASSERT_THAT(sqlStatementBuilder.sqlStatement(), Utf8StringLiteral("SELECT name, number FROM test")); + + ASSERT_TRUE(sqlStatementBuilder.isBuild()); + + sqlStatementBuilder.clear(); + + ASSERT_FALSE(sqlStatementBuilder.isBuild()); +} diff --git a/tests/unit/unittest/translationunitstest.cpp b/tests/unit/unittest/translationunitstest.cpp new file mode 100644 index 0000000000..3b74c5a166 --- /dev/null +++ b/tests/unit/unittest/translationunitstest.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-generated-matchers.h" +#include "gtest-qt-printing.h" + +#include <clang-c/Index.h> + +#include <translationunit.h> +#include <unsavedfiles.h> +#include <utf8string.h> +#include <projectpart.h> +#include <translationunits.h> +#include <filecontainer.h> +#include <projectpartcontainer.h> +#include <projects.h> +#include <translationunitdoesnotexistexception.h> +#include <translationunitisnullexception.h> +#include <translationunitfilenotexitexception.h> +#include <projectpartsdonotexistexception.h> + +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::UnsavedFiles; +using ClangBackEnd::ProjectPart; + +using testing::IsNull; +using testing::NotNull; +using testing::Gt; +using testing::Not; +using testing::Contains; + +namespace { + +using ::testing::PrintToString; + +MATCHER_P2(IsTranslationUnit, filePath, projectPartId, + std::string(negation ? "isn't" : "is") + " translation unit with file path " + + PrintToString(filePath) + " and project " + PrintToString(projectPartId) + ) +{ + if (arg.filePath() != filePath) { + *result_listener << "file path is " + PrintToString(arg.filePath()) + " and not " + PrintToString(filePath); + return false; + } + + if (arg.projectPartId() != projectPartId) { + *result_listener << "file path is " + PrintToString(arg.projectPartId()) + " and not " + PrintToString(projectPartId); + return false; + } + + return true; +} + +class TranslationUnits : public ::testing::Test +{ +protected: + void SetUp() override; + + ClangBackEnd::ProjectParts projects; + ClangBackEnd::UnsavedFiles unsavedFiles; + ClangBackEnd::TranslationUnits translationUnits = ClangBackEnd::TranslationUnits(projects, unsavedFiles); + const Utf8String filePath = Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"); + const Utf8String projectPartId = Utf8StringLiteral("/path/to/projectfile"); + +}; + +void TranslationUnits::SetUp() +{ + projects.createOrUpdate({ClangBackEnd::ProjectPartContainer(projectPartId)}); +} + + +TEST_F(TranslationUnits, ThrowForGettingWithWrongFilePath) +{ + ASSERT_THROW(translationUnits.translationUnit(Utf8StringLiteral("foo.cpp"), projectPartId), + ClangBackEnd::TranslationUnitDoesNotExistException); + +} + +TEST_F(TranslationUnits, ThrowForGettingWithWrongProjectPartFilePath) +{ + ASSERT_THROW(translationUnits.translationUnit(filePath, Utf8StringLiteral("foo.pro")), + ClangBackEnd::ProjectPartDoNotExistException); + +} + +TEST_F(TranslationUnits, ThrowForAddingNonExistingFile) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), projectPartId); + + ASSERT_THROW(translationUnits.createOrUpdate({fileContainer}), + ClangBackEnd::TranslationUnitFileNotExitsException); +} + +TEST_F(TranslationUnits, DoNotThrowForAddingNonExistingFileWithUnsavedContent) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), projectPartId, Utf8String(), true); + + ASSERT_NO_THROW(translationUnits.createOrUpdate({fileContainer})); +} + +TEST_F(TranslationUnits, Add) +{ + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId); + + translationUnits.createOrUpdate({fileContainer}); + + ASSERT_THAT(translationUnits.translationUnit(filePath, projectPartId), + IsTranslationUnit(filePath, projectPartId)); +} + +TEST_F(TranslationUnits, ThrowForRemovingWithWrongFilePath) +{ + ClangBackEnd::FileContainer fileContainer(Utf8StringLiteral("foo.cpp"), projectPartId); + + ASSERT_THROW(translationUnits.remove({fileContainer}), + ClangBackEnd::TranslationUnitDoesNotExistException); +} + +TEST_F(TranslationUnits, ThrowForRemovingWithWrongProjectPartFilePath) +{ + ClangBackEnd::FileContainer fileContainer(filePath, Utf8StringLiteral("foo.pro")); + + ASSERT_THROW(translationUnits.remove({fileContainer}), + ClangBackEnd::ProjectPartDoNotExistException); +} + +TEST_F(TranslationUnits, Remove) +{ + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId); + translationUnits.createOrUpdate({fileContainer}); + + translationUnits.remove({fileContainer}); + + ASSERT_THROW(translationUnits.translationUnit(filePath, projectPartId), + ClangBackEnd::TranslationUnitDoesNotExistException); +} + +TEST_F(TranslationUnits, RemoveAllValidIfExceptionIsThrown) +{ + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId); + translationUnits.createOrUpdate({fileContainer}); + + ASSERT_THROW(translationUnits.remove({ClangBackEnd::FileContainer(Utf8StringLiteral("dontextist.pro"), projectPartId), fileContainer}), + ClangBackEnd::TranslationUnitDoesNotExistException); + + ASSERT_THAT(translationUnits.translationUnits(), + Not(Contains(TranslationUnit(filePath, unsavedFiles, projects.project(projectPartId))))); +} + +} + + diff --git a/tests/unit/unittest/translationunittest.cpp b/tests/unit/unittest/translationunittest.cpp new file mode 100644 index 0000000000..72cd39cf29 --- /dev/null +++ b/tests/unit/unittest/translationunittest.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-generated-matchers.h" +#include "gtest-qt-printing.h" + +#include <clang-c/Index.h> + +#include <translationunit.h> +#include <unsavedfiles.h> +#include <utf8string.h> +#include <projectpart.h> +#include <translationunits.h> +#include <filecontainer.h> +#include <projects.h> +#include <translationunitdoesnotexistexception.h> +#include <translationunitisnullexception.h> +#include <translationunitfilenotexitexception.h> +#include <translationunitparseerrorexception.h> + +#include <chrono> +#include <thread> + +using ClangBackEnd::TranslationUnit; +using ClangBackEnd::UnsavedFiles; +using ClangBackEnd::ProjectPart; + +using testing::IsNull; +using testing::NotNull; +using testing::Gt; + +namespace { + +TEST(TranslationUnit, DefaultTranslationUnitIsInvalid) +{ + TranslationUnit translationUnit; + + ASSERT_TRUE(translationUnit.isNull()); +} + +TEST(TranslationUnit, ThrowExceptionForNonExistingFilePath) +{ + ASSERT_THROW(TranslationUnit(Utf8StringLiteral("file.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile"))), + ClangBackEnd::TranslationUnitFileNotExitsException); +} + +TEST(TranslationUnit, ThrowNoExceptionForNonExistingFilePathIfDoNotCheckIfFileExistsIsSet) +{ + ASSERT_NO_THROW(TranslationUnit(Utf8StringLiteral("file.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile")), TranslationUnit::DoNotCheckIfFileExists)); +} + +TEST(TranslationUnit, TranslationUnitIsValid) +{ + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + + ASSERT_FALSE(translationUnit.isNull()); +} + + +TEST(TranslationUnit, ThrowExceptionForGettingIndexForInvalidUnit) +{ + TranslationUnit translationUnit; + + ASSERT_THROW(translationUnit.index(), ClangBackEnd::TranslationUnitIsNullException); +} + +TEST(TranslationUnit, IndexGetterIsNonNullForValidUnit) +{ + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + + ASSERT_THAT(translationUnit.index(), NotNull()); +} + +TEST(TranslationUnit, ThrowExceptionForGettingCxTranslationUnitForInvalidUnit) +{ + TranslationUnit translationUnit; + + ASSERT_THROW(translationUnit.cxTranslationUnit(), ClangBackEnd::TranslationUnitIsNullException); +} + +TEST(TranslationUnit, CxTranslationUnitGetterIsNonNullForValidUnit) +{ + UnsavedFiles unsavedFiles; + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), unsavedFiles, ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + + ASSERT_THAT(translationUnit.cxTranslationUnit(), NotNull()); +} + +TEST(TranslationUnit, ThrowExceptionIfGettingFilePathForNullUnit) +{ + TranslationUnit translationUnit; + + ASSERT_THROW(translationUnit.filePath(), ClangBackEnd::TranslationUnitIsNullException); +} + +TEST(TranslationUnit, ResetedTranslationUnitIsNull) +{ + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + + translationUnit.reset(); + + ASSERT_TRUE(translationUnit.isNull()); +} + +TEST(TranslationUnit, TimeStampIsUpdatedAsNewCxTranslationUnitIsGenerated) +{ + TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), ProjectPart(Utf8StringLiteral("/path/to/projectfile"))); + auto lastChangeTimePoint = translationUnit.lastChangeTimePoint(); + std::this_thread::sleep_for(std::chrono::steady_clock::duration(1)); + + translationUnit.cxTranslationUnit(); + + ASSERT_THAT(translationUnit.lastChangeTimePoint(), Gt(lastChangeTimePoint)); +} + + +//TEST(TranslationUnit, ThrowParseErrorForWrongArguments) +//{ +// ProjectPart project(Utf8StringLiteral("/path/to/projectfile")); +// project.setArguments({Utf8StringLiteral("-fblah")}); +// TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), UnsavedFiles(), project); + +// ASSERT_THROW(translationUnit.cxTranslationUnit(), ClangBackEnd::TranslationUnitParseErrorException); +//} + +} diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro new file mode 100644 index 0000000000..330a4f3d5b --- /dev/null +++ b/tests/unit/unittest/unittest.pro @@ -0,0 +1,89 @@ +QT += core network testlib +QT -= gui + +TARGET = unittest +CONFIG += console c++14 testcase +CONFIG -= app_bundle + +TEMPLATE = app + +GMOCK_DIR = $$(GMOCK_DIR) +GTEST_DIR = $$GMOCK_DIR/gtest + +requires(exists($$GMOCK_DIR)) +!exists($$GMOCK_DIR):message("No gmock is found! To enabe unit tests set GMOCK_DIR") + +INCLUDEPATH += $$GTEST_DIR $$GTEST_DIR/include $$GMOCK_DIR $$GMOCK_DIR/include + +include(../../../src/libs/sqlite/sqlite-lib.pri) +include(../../../src/libs/clangbackendipc/clangbackendipc-lib.pri) +include(../../../src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri) +include(../../../src/shared/clang/clang_installation.pri) +include(../../../src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri) + +INCLUDEPATH += $$PWD/../../../src/libs $$PWD/../../../src/plugins + +requires(!isEmpty(LLVM_LIBS)) + +LIBS += $$LLVM_LIBS +INCLUDEPATH += $$LLVM_INCLUDEPATH +INCLUDEPATH += ../../../../src/libs/utils + +osx:QMAKE_CXXFLAGS = -stdlib=libc++ + +SOURCES += main.cpp \ + $$GTEST_DIR/src/gtest-all.cc \ + $$GMOCK_DIR/src/gmock-all.cc \ + utf8test.cpp \ + sqlstatementbuildertest.cpp \ + createtablesqlstatementbuildertest.cpp \ + sqlitecolumntest.cpp \ + sqlitestatementtest.cpp \ + sqlitetabletest.cpp \ + spydummy.cpp \ + sqlitedatabasetest.cpp \ + sqlitedatabasebackendtest.cpp \ + readandwritecommandblocktest.cpp \ + clientserverinprocesstest.cpp \ + clientserveroutsideprocess.cpp \ + gtest-qt-printing.cpp \ + codecompletiontest.cpp \ + ../../../src/libs/utils/qtcassert.cpp \ + clangstringtest.cpp \ + translationunittest.cpp \ + clangcodecompleteresultstest.cpp \ + codecompletionsextractortest.cpp \ + unsavedfilestest.cpp \ + projecttest.cpp \ + clangipcservertest.cpp \ + translationunitstest.cpp \ + completionchunkstotextconvertertest.cpp + +HEADERS += \ + gtest-qt-printing.h \ + spydummy.h \ + ../../../src/libs/utils/qtcassert.h \ + mockipclient.h \ + mockipcserver.h + +OTHER_FILES += data/complete_testfile_1.cpp \ + data/complete_completer.cpp \ + data/complete_completer_unsaved.cpp \ + data/complete_extractor_function.cpp \ + data/complete_extractor_function_unsaved.cpp \ + data/complete_extractor_function_unsaved_2.cpp \ + data/complete_extractor_variable.cpp \ + data/complete_extractor_class.cpp \ + data/complete_extractor_namespace.cpp \ + data/complete_extractor_enumeration.cpp \ + data/complete_extractor_constructor.cpp \ + data/complete_translationunit_parse_error.cpp + +DEFINES += QT_NO_CAST_FROM_ASCII +DEFINES += CLANGBACKEND_TESTS +DEFINES += DONT_CHECK_COMMAND_COUNTER +DEFINES += GTEST_HAS_STD_INITIALIZER_LIST_ GTEST_LANG_CXX11 + +DEFINES += TESTDATA_DIR=\"R\\\"xxx($$PWD/data)xxx\\\"\" +win32:DEFINES += ECHOSERVER=\"R\\\"xxx($$OUT_PWD/../echo)xxx\\\"\" +unix:DEFINES += ECHOSERVER=\"R\\\"xxx($$OUT_PWD/../echoserver/echo)xxx\\\"\" diff --git a/tests/unit/unittest/unsavedfilestest.cpp b/tests/unit/unittest/unsavedfilestest.cpp new file mode 100644 index 0000000000..0b843c6011 --- /dev/null +++ b/tests/unit/unittest/unsavedfilestest.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" +#include "gtest-qt-printing.h" + +#include <unsavedfiles.h> +#include <filecontainer.h> + +#include <QVector> + +using ClangBackEnd::UnsavedFiles; +using ClangBackEnd::FileContainer; + +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::Gt; + +namespace { + +bool operator==(const ClangBackEnd::FileContainer &fileContainer, const CXUnsavedFile &cxUnsavedFile) +{ + return fileContainer.filePath() == Utf8String::fromUtf8(cxUnsavedFile.Filename) + && fileContainer.unsavedFileContent() == Utf8String(cxUnsavedFile.Contents, cxUnsavedFile.Length); +} + +bool fileContainersContainsItemMatchingToCxUnsavedFile(const QVector<FileContainer> &fileContainers, const CXUnsavedFile &cxUnsavedFile) +{ + for (const FileContainer &fileContainer : fileContainers) + if (fileContainer == cxUnsavedFile) + return true; + + return false; +} + +MATCHER_P(HasUnsavedFiles, fileContainers, "") +{ + ClangBackEnd::UnsavedFiles unsavedFiles = arg; + if (unsavedFiles.count() != fileContainers.size()) { + *result_listener << "unsaved count is " << unsavedFiles.count() << " and not " << fileContainers.size(); + return false; + } + + for (const CXUnsavedFile &cxUnsavedFile : unsavedFiles.cxUnsavedFileVector()) { + if (!fileContainersContainsItemMatchingToCxUnsavedFile(fileContainers, cxUnsavedFile)) + return false; + } + + return true; +} + +class UnsavedFiles : public ::testing::Test +{ +protected: + void TearDown() override; + +protected: + ::UnsavedFiles unsavedFiles; +}; + +void UnsavedFiles::TearDown() +{ + unsavedFiles.clear(); +} + +TEST_F(UnsavedFiles, DoNothingForUpdateIfFileHasNoUnsavedContent) +{ + QVector<FileContainer> fileContainers({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"))}); + + unsavedFiles.createOrUpdate(fileContainers); + + ASSERT_THAT(unsavedFiles, HasUnsavedFiles(QVector<FileContainer>())); +} + +TEST_F(UnsavedFiles, AddUnsavedFileForUpdateWithUnsavedContent) +{ + QVector<FileContainer> fileContainers({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro")), + FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo"), true)}); + unsavedFiles.createOrUpdate(fileContainers); + + ASSERT_THAT(unsavedFiles, HasUnsavedFiles(QVector<FileContainer>({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo"), true)}))); +} + +TEST_F(UnsavedFiles, RemoveUnsavedFileForUpdateWithUnsavedContent) +{ + QVector<FileContainer> fileContainers({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo"), true), + FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"))}); + + unsavedFiles.createOrUpdate(fileContainers); + + ASSERT_THAT(unsavedFiles, HasUnsavedFiles(QVector<FileContainer>())); +} + +TEST_F(UnsavedFiles, ExchangeUnsavedFileForUpdateWithUnsavedContent) +{ + QVector<FileContainer> fileContainers({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo"), true), + FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo2"), true)}); + unsavedFiles.createOrUpdate(fileContainers); + + ASSERT_THAT(unsavedFiles, HasUnsavedFiles(QVector<FileContainer>({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo2"), true)}))); +} + +TEST_F(UnsavedFiles, TimeStampIsUpdatedAsUnsavedFilesChanged) +{ + QVector<FileContainer> fileContainers({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo"), true), + FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo2"), true)}); + auto lastChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + + unsavedFiles.createOrUpdate(fileContainers); + + ASSERT_THAT(unsavedFiles.lastChangeTimePoint(), Gt(lastChangeTimePoint)); +} + +TEST_F(UnsavedFiles, RemoveUnsavedFiles) +{ + QVector<FileContainer> fileContainers({FileContainer(Utf8StringLiteral("file.cpp"), Utf8StringLiteral("pathToProject.pro"), Utf8StringLiteral("foo"), true)}); + unsavedFiles.createOrUpdate(fileContainers); + + unsavedFiles.remove(fileContainers); + + ASSERT_THAT(unsavedFiles, HasUnsavedFiles(QVector<FileContainer>())); +} +} + + diff --git a/tests/unit/unittest/utf8test.cpp b/tests/unit/unittest/utf8test.cpp new file mode 100644 index 0000000000..db4dcb0e81 --- /dev/null +++ b/tests/unit/unittest/utf8test.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** 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 "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gtest-qt-printing.h" + +#include <QString> + +#include <utf8stringvector.h> + +using namespace ::testing; + +TEST(Utf8, QStringConversionConstructor) +{ + ASSERT_THAT(Utf8String(QStringLiteral("text")), Utf8StringLiteral("text")); +} + +TEST(Utf8, QByteArrayConversionFunction) +{ + ASSERT_THAT(Utf8String::fromByteArray("text"), Utf8StringLiteral("text")); +} + +TEST(Utf8, QStringConversionFunction) +{ + ASSERT_THAT(Utf8String::fromString(QStringLiteral("text")), Utf8StringLiteral("text")); +} + +TEST(Utf8, Utf8ConversationFunction) +{ + ASSERT_THAT(Utf8String::fromUtf8("text"), Utf8StringLiteral("text")); +} + +TEST(Utf8, Mid) +{ + Utf8String text(Utf8StringLiteral("some text")); + + ASSERT_THAT(text.mid(5, 4), Utf8StringLiteral("text")); + ASSERT_THAT(text.mid(5), Utf8StringLiteral("text")); +} + +TEST(Utf8, ByteSize) +{ + ASSERT_THAT(Utf8StringLiteral("text").byteSize(), 4); +} + +TEST(Utf8, Append) +{ + Utf8String text(Utf8StringLiteral("some ")); + text.append(Utf8StringLiteral("text")); + + ASSERT_THAT(text, Utf8StringLiteral("some text")); +} + +TEST(Utf8, ToByteArray) +{ + Utf8String text(Utf8StringLiteral("some text")); + + ASSERT_THAT(text.toByteArray(), QByteArrayLiteral("some text")); +} + +TEST(Utf8, ToString) +{ + Utf8String text(Utf8StringLiteral("some text")); + + ASSERT_THAT(text.toString(), QStringLiteral("some text")); +} + + +TEST(Utf8, Contains) +{ + Utf8String text(Utf8StringLiteral("some text")); + + ASSERT_TRUE(text.contains(Utf8StringLiteral("text"))); + ASSERT_TRUE(text.contains("text")); + ASSERT_TRUE(text.contains('x')); +} + +TEST(Utf8, EqualOperator) +{ + ASSERT_TRUE(Utf8StringLiteral("text") == Utf8StringLiteral("text")); + ASSERT_FALSE(Utf8StringLiteral("text") == Utf8StringLiteral("text2")); +} + +TEST(Utf8, SmallerOperator) +{ + ASSERT_TRUE(Utf8StringLiteral("some") < Utf8StringLiteral("text")); + ASSERT_TRUE(Utf8StringLiteral("text") < Utf8StringLiteral("texta")); + ASSERT_FALSE(Utf8StringLiteral("text") < Utf8StringLiteral("some")); + ASSERT_FALSE(Utf8StringLiteral("text") < Utf8StringLiteral("text")); +} + +TEST(Utf8, UnequalOperator) +{ + ASSERT_FALSE(Utf8StringLiteral("text") != Utf8StringLiteral("text")); + ASSERT_TRUE(Utf8StringLiteral("text") != Utf8StringLiteral("text2")); +} + +TEST(Utf8, Join) +{ + Utf8StringVector vector; + + vector.append(Utf8StringLiteral("some")); + vector.append(Utf8StringLiteral("text")); + + ASSERT_THAT(Utf8StringLiteral("some, text"), vector.join(Utf8StringLiteral(", "))); +} + +TEST(Utf8, Split) +{ + Utf8String test(Utf8StringLiteral("some text")); + + Utf8StringVector splittedText = test.split(' '); + + ASSERT_THAT(splittedText.at(0), Utf8StringLiteral("some")); + ASSERT_THAT(splittedText.at(1), Utf8StringLiteral("text")); +} + +TEST(Utf8, IsEmpty) +{ + ASSERT_FALSE(Utf8StringLiteral("text").isEmpty()); + ASSERT_TRUE(Utf8String().isEmpty()); +} + +TEST(Utf8, HasContent) +{ + ASSERT_TRUE(Utf8StringLiteral("text").hasContent()); + ASSERT_FALSE(Utf8String().hasContent()); +} + +TEST(Utf8, Replace) +{ + Utf8String text(Utf8StringLiteral("some text")); + + text.replace(Utf8StringLiteral("some"), Utf8StringLiteral("any")); + + ASSERT_THAT(text, Utf8StringLiteral("any text")); +} + +TEST(Utf8, StartsWith) +{ + Utf8String text(Utf8StringLiteral("$column")); + + ASSERT_TRUE(text.startsWith(Utf8StringLiteral("$col"))); + ASSERT_FALSE(text.startsWith(Utf8StringLiteral("col"))); + ASSERT_TRUE(text.startsWith("$col")); + ASSERT_FALSE(text.startsWith("col")); + ASSERT_TRUE(text.startsWith('$')); + ASSERT_FALSE(text.startsWith('@')); +} + +TEST(Utf8, Clear) +{ + Utf8String text(Utf8StringLiteral("$column")); + + text.clear(); + + ASSERT_TRUE(text.isEmpty()); +} + +TEST(Utf8, Number) +{ + ASSERT_THAT(Utf8String::number(20), Utf8StringLiteral("20")); +} + +TEST(Utf8, FromIntegerVector) +{ + QVector<int> integers({1, 2, 3}); + + ASSERT_THAT(Utf8StringVector::fromIntegerVector(integers).join(Utf8StringLiteral(", ")), Utf8StringLiteral("1, 2, 3")); +} + +TEST(Utf8, PlusOperator) +{ + auto text = Utf8StringLiteral("foo") + Utf8StringLiteral("bar"); + + ASSERT_THAT(text, Utf8StringLiteral("foobar")); +} + +TEST(Utf8, PlusAssignmentOperator) +{ + Utf8String text("foo", 3); + + text += Utf8StringLiteral("bar"); + + ASSERT_THAT(text, Utf8StringLiteral("foobar")); +} + +TEST(Utf8, CompareCharPointer) +{ + Utf8String text("foo", 3); + + ASSERT_TRUE(text == "foo"); + ASSERT_FALSE(text == "foo2"); +} + +TEST(Utf8, RemoveFastFromVectorFailed) +{ + Utf8StringVector values({Utf8StringLiteral("a"), + Utf8StringLiteral("b"), + Utf8StringLiteral("c"), + Utf8StringLiteral("d")}); + + ASSERT_FALSE(values.removeFast(Utf8StringLiteral("e"))); +} + +TEST(Utf8, RemoveFastFromVector) +{ + Utf8StringVector values({Utf8StringLiteral("a"), + Utf8StringLiteral("b"), + Utf8StringLiteral("c"), + Utf8StringLiteral("d")}); + + bool removed = values.removeFast(Utf8StringLiteral("b")); + + ASSERT_TRUE(removed); + ASSERT_THAT(values, Not(Contains(Utf8StringLiteral("b")))); +} + +TEST(Utf8, ConverteAutomaticallyFromQString) +{ + QString text(QStringLiteral("foo")); + + Utf8String utf8Text(text); + + ASSERT_THAT(utf8Text, Utf8StringLiteral("foo")); +} + +TEST(Utf8, ConverteAutomaticallyToQString) +{ + Utf8String utf8Text(Utf8StringLiteral("foo")); + + QString text = utf8Text; + + ASSERT_THAT(text, QStringLiteral("foo")); +} + +TEST(Utf8, ConverteAutomaticallyToQByteArray) +{ + Utf8String utf8Text(Utf8StringLiteral("foo")); + + QByteArray bytes = utf8Text; + + ASSERT_THAT(bytes, QByteArrayLiteral("foo")); +} + |