From da27ea4d42864472f102943e89a0cd82f9ffe230 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 28 Jan 2016 18:31:05 +0100 Subject: [PATCH] Clang: Introduce UnsavedFile wrapper This simplifies UnsavedFiles and makes TemporaryModifiedUnsavedFiles useless. Change-Id: I1896f971215ed22ce7aa7bf21b16381862b7469d Reviewed-by: Marco Bubke --- .../ipcsource/clangbackendclangipc-source.pri | 8 +- .../ipcsource/clangtranslationunit.cpp | 10 +- .../ipcsource/clangtranslationunit.h | 4 +- .../clangbackend/ipcsource/codecompleter.cpp | 20 +-- .../temporarymodifiedunsavedfiles.cpp | 87 ---------- .../clangbackend/ipcsource/unsavedfile.cpp | 104 ++++++++++++ ...rymodifiedunsavedfiles.h => unsavedfile.h} | 44 +++-- .../clangbackend/ipcsource/unsavedfiles.cpp | 102 +++++------- .../clangbackend/ipcsource/unsavedfiles.h | 15 +- .../unittest/codecompletionsextractortest.cpp | 2 - .../temporarymodifiedunsavedfilestest.cpp | 106 ------------ tests/unit/unittest/unittest.pro | 2 +- tests/unit/unittest/unsavedfilestest.cpp | 52 ++++-- tests/unit/unittest/unsavedfiletest.cpp | 153 ++++++++++++++++++ 14 files changed, 380 insertions(+), 329 deletions(-) delete mode 100644 src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp create mode 100644 src/tools/clangbackend/ipcsource/unsavedfile.cpp rename src/tools/clangbackend/ipcsource/{temporarymodifiedunsavedfiles.h => unsavedfile.h} (55%) delete mode 100644 tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp create mode 100644 tests/unit/unittest/unsavedfiletest.cpp diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index 632d687ccbd..344a0372076 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -7,6 +7,7 @@ HEADERS += $$PWD/clangipcserver.h \ $$PWD/codecompletefailedexception.h \ $$PWD/clangcodecompleteresults.h \ $$PWD/codecompletionsextractor.h \ + $$PWD/unsavedfile.h \ $$PWD/unsavedfiles.h \ $$PWD/projects.h \ $$PWD/translationunits.h \ @@ -31,8 +32,7 @@ HEADERS += $$PWD/clangipcserver.h \ $$PWD/highlightinginformationsiterator.h \ $$PWD/skippedsourceranges.h \ $$PWD/clangtranslationunit.h \ - $$PWD/clangtype.h \ - $$PWD/temporarymodifiedunsavedfiles.h + $$PWD/clangtype.h SOURCES += $$PWD/clangipcserver.cpp \ $$PWD/codecompleter.cpp \ @@ -41,6 +41,7 @@ SOURCES += $$PWD/clangipcserver.cpp \ $$PWD/codecompletefailedexception.cpp \ $$PWD/clangcodecompleteresults.cpp \ $$PWD/codecompletionsextractor.cpp \ + $$PWD/unsavedfile.cpp \ $$PWD/unsavedfiles.cpp \ $$PWD/projects.cpp \ $$PWD/translationunits.cpp \ @@ -63,5 +64,4 @@ SOURCES += $$PWD/clangipcserver.cpp \ $$PWD/highlightinginformation.cpp \ $$PWD/skippedsourceranges.cpp \ $$PWD/clangtranslationunit.cpp \ - $$PWD/clangtype.cpp \ - $$PWD/temporarymodifiedunsavedfiles.cpp + $$PWD/clangtype.cpp diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp index aa1d39d9155..38315719d1c 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp @@ -185,6 +185,11 @@ CXTranslationUnit TranslationUnit::cxTranslationUnitWithoutReparsing() const return d->translationUnit; } +UnsavedFile &TranslationUnit::unsavedFile() const +{ + return unsavedFiles().unsavedFile(filePath()); +} + const Utf8String &TranslationUnit::filePath() const { checkIfNull(); @@ -498,11 +503,6 @@ CXUnsavedFile *TranslationUnit::cxUnsavedFiles() const return unsavedFiles().cxUnsavedFiles(); } -const std::vector &TranslationUnit::cxUnsavedFilesVector() const -{ - return unsavedFiles().cxUnsavedFileVector(); -} - TranslationUnit::~TranslationUnit() = default; TranslationUnit::TranslationUnit(const TranslationUnit &) = default; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h index 96898fcc0d7..467c9346523 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h @@ -42,6 +42,7 @@ namespace ClangBackEnd { class TranslationUnitData; class CodeCompleter; +class UnsavedFile; class UnsavedFiles; class ProjectPart; class DiagnosticContainer; @@ -93,8 +94,9 @@ public: CXIndex index() const; CXTranslationUnit cxTranslationUnit() const; CXTranslationUnit cxTranslationUnitWithoutReparsing() const; + + UnsavedFile &unsavedFile() const; CXUnsavedFile * cxUnsavedFiles() const; - const std::vector &cxUnsavedFilesVector() const; uint unsavedFilesCount() const; diff --git a/src/tools/clangbackend/ipcsource/codecompleter.cpp b/src/tools/clangbackend/ipcsource/codecompleter.cpp index 015d485f7f3..a139e02edf7 100644 --- a/src/tools/clangbackend/ipcsource/codecompleter.cpp +++ b/src/tools/clangbackend/ipcsource/codecompleter.cpp @@ -31,7 +31,7 @@ #include "codecompletefailedexception.h" #include "codecompletionsextractor.h" #include "sourcelocation.h" -#include "temporarymodifiedunsavedfiles.h" +#include "unsavedfile.h" #include "clangtranslationunit.h" #include "sourcerange.h" @@ -97,22 +97,18 @@ ClangCodeCompleteResults CodeCompleter::complete(uint line, ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line, uint column) { - TemporaryModifiedUnsavedFiles modifiedUnsavedFiles(translationUnit.cxUnsavedFilesVector()); - const SourceLocation location = translationUnit.sourceLocationAtWithoutReparsing(line, - column - 1); - - const bool replaced = modifiedUnsavedFiles.replaceInFile(filePath(), - location.offset(), - 1, - Utf8StringLiteral("->")); - ClangCodeCompleteResults results; + const SourceLocation location = translationUnit.sourceLocationAtWithoutReparsing(line, column - 1); + const bool replaced = translationUnit.unsavedFile().replaceAt(location.offset(), + 1, + Utf8StringLiteral("->")); + if (replaced) { results = complete(line, column + 1, - modifiedUnsavedFiles.cxUnsavedFiles(), - modifiedUnsavedFiles.count()); + translationUnit.cxUnsavedFiles(), + translationUnit.unsavedFilesCount()); if (results.hasResults()) neededCorrection_ = CompletionCorrection::DotToArrowCorrection; diff --git a/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp b/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp deleted file mode 100644 index a22a04e57c1..00000000000 --- a/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "temporarymodifiedunsavedfiles.h" - -#include - -namespace ClangBackEnd { - -TemporaryModifiedUnsavedFiles::TemporaryModifiedUnsavedFiles( - const std::vector &unsavedFilesVector) - : m_unsavedFileVector(unsavedFilesVector) -{ -} - -bool TemporaryModifiedUnsavedFiles::replaceInFile(const Utf8String &filePath, - uint offset, - uint length, - const Utf8String &replacement) -{ - const auto isMatchingFile = [filePath] (const CXUnsavedFile &unsavedFile) { - return std::strcmp(unsavedFile.Filename, filePath.constData()) == 0; - }; - const auto unsavedFileIterator = std::find_if(m_unsavedFileVector.begin(), - m_unsavedFileVector.end(), - isMatchingFile); - - if (unsavedFileIterator == m_unsavedFileVector.end()) - return false; - - return replaceInFile_internal(*unsavedFileIterator, offset, length, replacement); -} - -CXUnsavedFile TemporaryModifiedUnsavedFiles::cxUnsavedFileAt(uint index) -{ - return m_unsavedFileVector[index]; -} - -CXUnsavedFile *TemporaryModifiedUnsavedFiles::cxUnsavedFiles() -{ - return m_unsavedFileVector.data(); -} - -uint TemporaryModifiedUnsavedFiles::count() -{ - return uint(m_unsavedFileVector.size()); -} - -bool TemporaryModifiedUnsavedFiles::replaceInFile_internal(CXUnsavedFile &unsavedFile, - uint offset, - uint length, - const Utf8String &replacement) -{ - auto modifiedContent = Utf8String::fromUtf8(unsavedFile.Contents); - modifiedContent.replace(int(offset), int(length), replacement); - - unsavedFile.Contents = modifiedContent.constData(); - unsavedFile.Length = uint(modifiedContent.byteSize() + 1); - - m_modifiedContents.push_back(modifiedContent); // Keep the modified copy. - - return true; -} - -} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.cpp b/src/tools/clangbackend/ipcsource/unsavedfile.cpp new file mode 100644 index 00000000000..1db76d22cfa --- /dev/null +++ b/src/tools/clangbackend/ipcsource/unsavedfile.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "unsavedfile.h" + +#include "utf8string.h" + +#include +#include + +namespace ClangBackEnd { + +UnsavedFile::UnsavedFile(const Utf8String &filePath, const Utf8String &fileContent) +{ + char *cxUnsavedFilePath = new char[filePath.byteSize() + 1]; + char *cxUnsavedFileContent = new char[fileContent.byteSize() + 1]; + + std::memcpy(cxUnsavedFilePath, filePath.constData(), filePath.byteSize() + 1); + std::memcpy(cxUnsavedFileContent, fileContent.constData(), fileContent.byteSize() + 1); + + cxUnsavedFile = CXUnsavedFile{cxUnsavedFilePath, + cxUnsavedFileContent, + ulong(fileContent.byteSize())}; +} + +UnsavedFile::UnsavedFile(UnsavedFile &&other) noexcept + : cxUnsavedFile(other.cxUnsavedFile) +{ + other.cxUnsavedFile = { nullptr, nullptr, 0UL }; +} + +UnsavedFile &UnsavedFile::operator=(UnsavedFile &&other) noexcept +{ + using std::swap; + + swap(this->cxUnsavedFile, other.cxUnsavedFile); + + return *this; +} + +const char *UnsavedFile::filePath() const +{ + return cxUnsavedFile.Filename; +} + +bool UnsavedFile::replaceAt(uint position, uint length, const Utf8String &replacement) +{ + if (position < cxUnsavedFile.Length) { + Utf8String modifiedContent(cxUnsavedFile.Contents, cxUnsavedFile.Length); + modifiedContent.replace(int(position), int(length), replacement); + + *this = UnsavedFile(Utf8String::fromUtf8(filePath()), modifiedContent); + + return true; + } + + return false; +} + +CXUnsavedFile *UnsavedFile::data() +{ + return &cxUnsavedFile; +} + +UnsavedFile::~UnsavedFile() +{ + delete [] cxUnsavedFile.Contents; + delete [] cxUnsavedFile.Filename; + cxUnsavedFile.Contents = nullptr; + cxUnsavedFile.Filename = nullptr; + cxUnsavedFile.Length = 0; +} + +void PrintTo(const UnsavedFile &unsavedFile, std::ostream *os) +{ + *os << "UnsavedFile(" + << unsavedFile.cxUnsavedFile.Filename << ", " + << unsavedFile.cxUnsavedFile.Contents << ", " + << unsavedFile.cxUnsavedFile.Length << ")"; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h b/src/tools/clangbackend/ipcsource/unsavedfile.h similarity index 55% rename from src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h rename to src/tools/clangbackend/ipcsource/unsavedfile.h index 959b9431505..fe738bb262e 100644 --- a/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h +++ b/src/tools/clangbackend/ipcsource/unsavedfile.h @@ -23,44 +23,40 @@ ** ****************************************************************************/ -#ifndef CLANGBACKEND_TEMPORARYMODIFIEDUNSAVEDFILES_H -#define CLANGBACKEND_TEMPORARYMODIFIEDUNSAVEDFILES_H +#pragma once #include -#include +#include -#include +class Utf8String; namespace ClangBackEnd { -class TemporaryModifiedUnsavedFiles +using uint = unsigned int; + +class UnsavedFile { public: - TemporaryModifiedUnsavedFiles(const std::vector &unsavedFilesVector); + friend void PrintTo(const UnsavedFile &unsavedFile, std::ostream *os); - TemporaryModifiedUnsavedFiles(const TemporaryModifiedUnsavedFiles &) = delete; + UnsavedFile(const Utf8String &filePath, const Utf8String &fileContent); + ~UnsavedFile(); - bool replaceInFile(const Utf8String &filePath, - uint offset, - uint length, - const Utf8String &replacement); + UnsavedFile(const UnsavedFile &other) = delete; + bool operator=(const UnsavedFile &other) = delete; - CXUnsavedFile cxUnsavedFileAt(uint index); - CXUnsavedFile *cxUnsavedFiles(); - uint count(); + UnsavedFile(UnsavedFile &&other) noexcept; + UnsavedFile &operator=(UnsavedFile &&other) noexcept; -private: - bool replaceInFile_internal(CXUnsavedFile &unsavedFile, - uint offset, - uint length, - const Utf8String &replacement); + const char *filePath() const; -private: - std::vector m_unsavedFileVector; - std::vector m_modifiedContents; + bool replaceAt(uint position, uint length, const Utf8String &replacement); + + CXUnsavedFile *data(); + +public: // for tests + CXUnsavedFile cxUnsavedFile = { nullptr, nullptr, 0UL }; }; } // namespace ClangBackEnd - -#endif // CLANGBACKEND_TEMPORARYMODIFIEDUNSAVEDFILES_H diff --git a/src/tools/clangbackend/ipcsource/unsavedfiles.cpp b/src/tools/clangbackend/ipcsource/unsavedfiles.cpp index 9f8f9dd9f90..a7b1b590053 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfiles.cpp +++ b/src/tools/clangbackend/ipcsource/unsavedfiles.cpp @@ -25,8 +25,9 @@ #include "unsavedfiles.h" +#include "unsavedfile.h" + #include -#include namespace ClangBackEnd { @@ -34,11 +35,10 @@ class UnsavedFilesData { public: UnsavedFilesData(); - ~UnsavedFilesData(); public: time_point lastChangeTimePoint; - std::vector cxUnsavedFiles; + std::vector unsavedFiles; }; UnsavedFilesData::UnsavedFilesData() @@ -46,14 +46,6 @@ UnsavedFilesData::UnsavedFilesData() { } -UnsavedFilesData::~UnsavedFilesData() -{ - for (CXUnsavedFile &cxUnsavedFile : cxUnsavedFiles) - UnsavedFiles::deleteCXUnsavedFile(cxUnsavedFile); - - cxUnsavedFiles.clear(); -} - UnsavedFiles::UnsavedFiles() : d(std::make_shared()) { @@ -79,7 +71,7 @@ UnsavedFiles &UnsavedFiles::operator=(UnsavedFiles &&other) void UnsavedFiles::createOrUpdate(const QVector &fileContainers) { for (const FileContainer &fileContainer : fileContainers) - updateCXUnsavedFileWithFileContainer(fileContainer); + updateUnsavedFileWithFileContainer(fileContainer); updateLastChangeTimePoint(); } @@ -87,33 +79,35 @@ void UnsavedFiles::createOrUpdate(const QVector &fileContainers) void UnsavedFiles::remove(const QVector &fileContainers) { for (const FileContainer &fileContainer : fileContainers) - removeCXUnsavedFile(fileContainer); + removeUnsavedFile(fileContainer); updateLastChangeTimePoint(); } -void UnsavedFiles::clear() +UnsavedFile &UnsavedFiles::unsavedFile(const Utf8String &filePath) { - for (CXUnsavedFile &cxUnsavedFile : d->cxUnsavedFiles) - deleteCXUnsavedFile(cxUnsavedFile); + const auto isMatchingFile = [filePath] (const UnsavedFile &unsavedFile) { + return unsavedFile.filePath() == filePath; + }; + const auto unsavedFileIterator = std::find_if(d->unsavedFiles.begin(), + d->unsavedFiles.end(), + isMatchingFile); - d->cxUnsavedFiles.clear(); - updateLastChangeTimePoint(); + if (unsavedFileIterator != d->unsavedFiles.end()) + return *unsavedFileIterator; + + static UnsavedFile defaultUnsavedFile = UnsavedFile(Utf8String(), Utf8String()); + return defaultUnsavedFile; } uint UnsavedFiles::count() const { - return uint(d->cxUnsavedFiles.size()); + return uint(d->unsavedFiles.size()); } CXUnsavedFile *UnsavedFiles::cxUnsavedFiles() const { - return d->cxUnsavedFiles.data(); -} - -const std::vector &UnsavedFiles::cxUnsavedFileVector() const -{ - return d->cxUnsavedFiles; + return d->unsavedFiles.data()->data(); } const time_point &UnsavedFiles::lastChangeTimePoint() const @@ -121,59 +115,35 @@ const time_point &UnsavedFiles::lastChangeTimePoint() const return d->lastChangeTimePoint; } -CXUnsavedFile UnsavedFiles::createCxUnsavedFile(const Utf8String &filePath, const Utf8String &fileContent) +void UnsavedFiles::updateUnsavedFileWithFileContainer(const FileContainer &fileContainer) { - char *cxUnsavedFilePath = new char[filePath.byteSize() + 1]; - char *cxUnsavedFileContent = new char[fileContent.byteSize() + 1]; - - std::memcpy(cxUnsavedFilePath, filePath.constData(), filePath.byteSize() + 1); - std::memcpy(cxUnsavedFileContent, fileContent.constData(), fileContent.byteSize() + 1); - - return CXUnsavedFile { cxUnsavedFilePath, cxUnsavedFileContent, ulong(fileContent.byteSize())}; + if (fileContainer.hasUnsavedFileContent()) + addOrUpdateUnsavedFile(fileContainer); + else + removeUnsavedFile(fileContainer); } -void UnsavedFiles::deleteCXUnsavedFile(CXUnsavedFile &cxUnsavedFile) -{ - delete [] cxUnsavedFile.Contents; - delete [] cxUnsavedFile.Filename; - cxUnsavedFile.Contents = nullptr; - cxUnsavedFile.Filename = nullptr; - cxUnsavedFile.Length = 0; -} - -void UnsavedFiles::updateCXUnsavedFileWithFileContainer(const FileContainer &fileContainer) -{ - if (fileContainer.hasUnsavedFileContent()) { - addOrUpdateCXUnsavedFile(fileContainer); - } else { - removeCXUnsavedFile(fileContainer); - } -} - -void UnsavedFiles::removeCXUnsavedFile(const FileContainer &fileContainer) +void UnsavedFiles::removeUnsavedFile(const FileContainer &fileContainer) { const Utf8String filePath = fileContainer.filePath(); - auto removeBeginIterator = std::partition(d->cxUnsavedFiles.begin(), - d->cxUnsavedFiles.end(), - [filePath] (const CXUnsavedFile &cxUnsavedFile) { return filePath != cxUnsavedFile.Filename; }); + auto removeBeginIterator = std::partition(d->unsavedFiles.begin(), + d->unsavedFiles.end(), + [filePath] (const UnsavedFile &unsavedFile) { return filePath != unsavedFile.filePath(); }); - std::for_each(removeBeginIterator, d->cxUnsavedFiles.end(), UnsavedFiles::deleteCXUnsavedFile); - d->cxUnsavedFiles.erase(removeBeginIterator, d->cxUnsavedFiles.end()); + d->unsavedFiles.erase(removeBeginIterator, d->unsavedFiles.end()); } -void UnsavedFiles::addOrUpdateCXUnsavedFile(const FileContainer &fileContainer) +void UnsavedFiles::addOrUpdateUnsavedFile(const FileContainer &fileContainer) { const Utf8String filePath = fileContainer.filePath(); const Utf8String fileContent = fileContainer.unsavedFileContent(); - auto isSameFile = [filePath] (const CXUnsavedFile &cxUnsavedFile) { return filePath == cxUnsavedFile.Filename; }; + auto isSameFile = [filePath] (const UnsavedFile &unsavedFile) { return filePath == unsavedFile.filePath(); }; - auto cxUnsavedFileIterator = std::find_if(d->cxUnsavedFiles.begin(), d->cxUnsavedFiles.end(), isSameFile); - if (cxUnsavedFileIterator == d->cxUnsavedFiles.end()) - d->cxUnsavedFiles.push_back(createCxUnsavedFile(filePath, fileContent)); - else { - deleteCXUnsavedFile(*cxUnsavedFileIterator); - *cxUnsavedFileIterator = createCxUnsavedFile(filePath, fileContent); - } + auto unsavedFileIterator = std::find_if(d->unsavedFiles.begin(), d->unsavedFiles.end(), isSameFile); + if (unsavedFileIterator == d->unsavedFiles.end()) + d->unsavedFiles.emplace_back(filePath, fileContent); + else + *unsavedFileIterator = UnsavedFile(filePath, fileContent); } void UnsavedFiles::updateLastChangeTimePoint() diff --git a/src/tools/clangbackend/ipcsource/unsavedfiles.h b/src/tools/clangbackend/ipcsource/unsavedfiles.h index 9b33a587f3b..f5617df9b8d 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfiles.h +++ b/src/tools/clangbackend/ipcsource/unsavedfiles.h @@ -33,13 +33,13 @@ #include #include -#include #include namespace ClangBackEnd { using time_point = std::chrono::steady_clock::time_point; +class UnsavedFile; class UnsavedFilesData; class UnsavedFiles @@ -57,21 +57,18 @@ public: void createOrUpdate(const QVector &fileContainers); void remove(const QVector &fileContainers); - void clear(); + + UnsavedFile &unsavedFile(const Utf8String &filePath); uint count() const; - CXUnsavedFile *cxUnsavedFiles() const; - const std::vector &cxUnsavedFileVector() const; const time_point &lastChangeTimePoint() const; private: - CXUnsavedFile createCxUnsavedFile(const Utf8String &filePath, const Utf8String &fileContent); - static void deleteCXUnsavedFile(CXUnsavedFile &cxUnsavedFile); - void updateCXUnsavedFileWithFileContainer(const FileContainer &fileContainer); - void removeCXUnsavedFile(const FileContainer &fileContainer); - void addOrUpdateCXUnsavedFile(const FileContainer &fileContainer); + void updateUnsavedFileWithFileContainer(const FileContainer &fileContainer); + void removeUnsavedFile(const FileContainer &fileContainer); + void addOrUpdateUnsavedFile(const FileContainer &fileContainer); void updateLastChangeTimePoint(); private: diff --git a/tests/unit/unittest/codecompletionsextractortest.cpp b/tests/unit/unittest/codecompletionsextractortest.cpp index 4cc8810d2af..1b3d0eb567b 100644 --- a/tests/unit/unittest/codecompletionsextractortest.cpp +++ b/tests/unit/unittest/codecompletionsextractortest.cpp @@ -548,7 +548,6 @@ TEST_F(CodeCompletionsExtractor, UnsavedFile) unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", TESTDATA_DIR"/complete_extractor_function_unsaved.cpp")}); ClangCodeCompleteResults completeResults(getResults(translationUnit, 20)); - unsavedFiles.clear(); ::CodeCompletionsExtractor extractor(completeResults.data()); @@ -566,7 +565,6 @@ TEST_F(CodeCompletionsExtractor, ChangeUnsavedFile) unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", TESTDATA_DIR"/complete_extractor_function_unsaved_2.cpp")}); completeResults = getResults(translationUnit, 20); - unsavedFiles.clear(); ::CodeCompletionsExtractor extractor(completeResults.data()); diff --git a/tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp b/tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp deleted file mode 100644 index 7fdd977e50d..00000000000 --- a/tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include "gtest-qt-printing.h" - -using ClangBackEnd::FileContainer; -using ClangBackEnd::TemporaryModifiedUnsavedFiles; -using ClangBackEnd::UnsavedFiles; - -using testing::Eq; - -namespace { - -class TemporaryModifiedUnsavedFiles : public ::testing::Test -{ -protected: - ClangBackEnd::UnsavedFiles unsavedFiles; - - FileContainer fileContainer{Utf8StringLiteral("file1.h"), - Utf8StringLiteral("projectPartId"), - Utf8StringLiteral("void f() { foo. }"), - true}; - - Utf8String someNotExistingFilePath{Utf8StringLiteral("nonExistingFile.cpp")}; -}; - -TEST_F(TemporaryModifiedUnsavedFiles, HasZeroFilesOnCreation) -{ - ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector()); - - ASSERT_THAT(files.count(), Eq(0L)); -} - -TEST_F(TemporaryModifiedUnsavedFiles, HasOneFileAfterCreatingOne) -{ - unsavedFiles.createOrUpdate({fileContainer}); - - ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector()); - - ASSERT_THAT(files.count(), Eq(1L)); -} - -TEST_F(TemporaryModifiedUnsavedFiles, ReplaceIndicatesFailureOnNonExistingUnsavedFile) -{ - ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector()); - const uint someOffset = 0; - const uint someLength = 1; - const auto someReplacement = Utf8StringLiteral("->"); - - const bool replaced = files.replaceInFile(someNotExistingFilePath, - someOffset, - someLength, - someReplacement); - - ASSERT_FALSE(replaced); -} - -TEST_F(TemporaryModifiedUnsavedFiles, ReplaceDotWithArrow) -{ - unsavedFiles.createOrUpdate({fileContainer}); - ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector()); - const uint offset = 14; - const uint length = 1; - const auto replacement = Utf8StringLiteral("->"); - - const bool replacedSuccessfully = files.replaceInFile(fileContainer.filePath(), - offset, - length, - replacement); - - CXUnsavedFile cxUnsavedFile = files.cxUnsavedFileAt(0); - ASSERT_TRUE(replacedSuccessfully); - ASSERT_THAT(Utf8String::fromUtf8(cxUnsavedFile.Contents), - Eq(Utf8StringLiteral("void f() { foo-> }"))); -} - -} // anonymous diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 69d0e8c8aab..89c101a0632 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -60,7 +60,7 @@ SOURCES += \ skippedsourcerangestest.cpp \ highlightingmarksreportertest.cpp \ chunksreportedmonitor.cpp \ - temporarymodifiedunsavedfilestest.cpp + unsavedfiletest.cpp HEADERS += \ gtest-qt-printing.h \ diff --git a/tests/unit/unittest/unsavedfilestest.cpp b/tests/unit/unittest/unsavedfilestest.cpp index 316acc2746c..5301733babd 100644 --- a/tests/unit/unittest/unsavedfilestest.cpp +++ b/tests/unit/unittest/unsavedfilestest.cpp @@ -28,17 +28,20 @@ #include "gmock/gmock.h" #include "gtest-qt-printing.h" -#include #include +#include +#include #include +using ClangBackEnd::UnsavedFile; using ClangBackEnd::UnsavedFiles; using ClangBackEnd::FileContainer; +using ::testing::Gt; using ::testing::IsNull; using ::testing::NotNull; -using ::testing::Gt; +using ::testing::PrintToString; namespace { @@ -65,19 +68,30 @@ MATCHER_P(HasUnsavedFiles, fileContainers, "") return false; } - for (const CXUnsavedFile &cxUnsavedFile : unsavedFiles.cxUnsavedFileVector()) { - if (!fileContainersContainsItemMatchingToCxUnsavedFile(fileContainers, cxUnsavedFile)) + for (uint i = 0, to = unsavedFiles.count(); i < to; ++i) { + CXUnsavedFile *cxUnsavedFile = unsavedFiles.cxUnsavedFiles() + i; + if (!fileContainersContainsItemMatchingToCxUnsavedFile(fileContainers, *cxUnsavedFile)) return false; } return true; } +MATCHER_P3(IsUnsavedFile, fileName, contents, contentsLength, + std::string(negation ? "isn't" : "is") + + " file name " + PrintToString(fileName) + + ", contents " + PrintToString(contents) + + ", contents length " + PrintToString(contentsLength)) +{ + CXUnsavedFile unsavedFile = arg.cxUnsavedFile; + + return fileName == unsavedFile.Filename + && contents == unsavedFile.Contents + && size_t(contentsLength) == unsavedFile.Length; +} + class UnsavedFiles : public ::testing::Test { -protected: - void TearDown() override; - protected: ::UnsavedFiles unsavedFiles; Utf8String filePath{Utf8StringLiteral("file.cpp")}; @@ -87,11 +101,6 @@ protected: Utf8String unsavedContent2{Utf8StringLiteral("bar")}; }; -void UnsavedFiles::TearDown() -{ - unsavedFiles.clear(); -} - TEST_F(UnsavedFiles, DoNothingForUpdateIfFileHasNoUnsavedContent) { QVector fileContainers({FileContainer(filePath, projectPartId)}); @@ -149,6 +158,25 @@ TEST_F(UnsavedFiles, RemoveUnsavedFiles) ASSERT_THAT(unsavedFiles, HasUnsavedFiles(QVector())); } + +TEST_F(UnsavedFiles, FindUnsavedFile) +{ + QVector fileContainers({FileContainer(filePath, projectPartId, unsavedContent1, true)}); + unsavedFiles.createOrUpdate(fileContainers); + + UnsavedFile &unsavedFile = unsavedFiles.unsavedFile(filePath); + + ASSERT_THAT(unsavedFile, IsUnsavedFile(filePath, unsavedContent1, unsavedContent1.byteSize())); } +TEST_F(UnsavedFiles, FindNoUnsavedFile) +{ + QVector fileContainers({FileContainer(filePath, projectPartId, unsavedContent1, true)}); + unsavedFiles.createOrUpdate(fileContainers); + UnsavedFile &unsavedFile = unsavedFiles.unsavedFile(Utf8StringLiteral("nonExistingFilePath.cpp")); + + ASSERT_THAT(unsavedFile, IsUnsavedFile(Utf8String(), Utf8String(), 0UL)); +} + +} diff --git a/tests/unit/unittest/unsavedfiletest.cpp b/tests/unit/unittest/unsavedfiletest.cpp new file mode 100644 index 00000000000..aa54550f2d1 --- /dev/null +++ b/tests/unit/unittest/unsavedfiletest.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "gtest/gtest.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" +#include "gtest-qt-printing.h" + +#include +#include + +using ClangBackEnd::UnsavedFile; +using ClangBackEnd::UnsavedFiles; + +using ::testing::PrintToString; + +namespace { + +MATCHER_P3(IsUnsavedFile, fileName, contents, contentsLength, + std::string(negation ? "isn't" : "is") + + " file name " + PrintToString(fileName) + + ", contents " + PrintToString(contents) + + ", contents length " + PrintToString(contentsLength)) +{ + CXUnsavedFile unsavedFile = arg.cxUnsavedFile; + + return fileName == unsavedFile.Filename + && contents == unsavedFile.Contents + && size_t(contentsLength) == unsavedFile.Length; +} + +class UnsavedFile : public ::testing::Test +{ +protected: + Utf8String filePath = Utf8StringLiteral("path"); + Utf8String fileContent = Utf8StringLiteral("content"); + + Utf8String otherFilePath = Utf8StringLiteral("otherpath"); + Utf8String otherFileContent = Utf8StringLiteral("othercontent"); + + uint aLength = 2; + Utf8String aReplacement = Utf8StringLiteral("replacement"); +}; + +TEST_F(UnsavedFile, Create) +{ + ::UnsavedFile unsavedFile(filePath, fileContent); + + ASSERT_THAT(unsavedFile, IsUnsavedFile(filePath, + fileContent, + fileContent.byteSize())); +} + +TEST_F(UnsavedFile, Destruct) +{ + auto *unsavedFile = new ::UnsavedFile(filePath, fileContent); + unsavedFile->~UnsavedFile(); + + ASSERT_THAT(*unsavedFile, IsUnsavedFile(nullptr, nullptr, 0UL)); +} + +TEST_F(UnsavedFile, ConstructMoveToIsValid) +{ + ::UnsavedFile movedFrom(filePath, fileContent); + + ::UnsavedFile movedTo = std::move(movedFrom); + + ASSERT_THAT(movedTo, IsUnsavedFile(filePath, + fileContent, + fileContent.byteSize())); +} + +TEST_F(UnsavedFile, ConstructMoveFromIsNull) +{ + ::UnsavedFile movedFrom(filePath, fileContent); + + ::UnsavedFile movedTo = std::move(movedFrom); + + ASSERT_THAT(movedFrom, IsUnsavedFile(nullptr, nullptr, 0UL)); +} + +TEST_F(UnsavedFile, AssignMoveToIsValid) +{ + ::UnsavedFile movedFrom(filePath, fileContent); + ::UnsavedFile movedTo(otherFilePath, otherFileContent); + + movedTo = std::move(movedFrom); + + ASSERT_THAT(movedTo, IsUnsavedFile(filePath, + fileContent, + fileContent.byteSize())); +} + +TEST_F(UnsavedFile, AssignMoveFromIsSwapped) +{ + ::UnsavedFile movedFrom(filePath, fileContent); + ::UnsavedFile movedTo(otherFilePath, otherFileContent); + + movedTo = std::move(movedFrom); + + ASSERT_THAT(movedFrom, IsUnsavedFile(otherFilePath, + otherFileContent, + otherFileContent.byteSize())); +} + +TEST_F(UnsavedFile, DoNotReplaceWithOffsetZeroInEmptyContent) +{ + ::UnsavedFile unsavedFile(filePath, QStringLiteral("")); + + ASSERT_FALSE(unsavedFile.replaceAt(0, aLength, aReplacement)); +} + +TEST_F(UnsavedFile, DoNotReplaceWithOffsetOneInEmptyContent) +{ + ::UnsavedFile unsavedFile(filePath, QStringLiteral("")); + + ASSERT_FALSE(unsavedFile.replaceAt(1, aLength, aReplacement)); +} + +TEST_F(UnsavedFile, Replace) +{ + const Utf8String expectedContent = Utf8StringLiteral("hello replacement!"); + ::UnsavedFile unsavedFile(filePath, Utf8StringLiteral("hello world!")); + + const bool hasReplaced = unsavedFile.replaceAt(6, 5, aReplacement); + + ASSERT_TRUE(hasReplaced); + ASSERT_THAT(unsavedFile, IsUnsavedFile(filePath, expectedContent, expectedContent.byteSize())); +} + +} // anonymous namespace