diff --git a/src/plugins/clangrefactoring/qtcreatorsearchhandle.cpp b/src/plugins/clangrefactoring/qtcreatorsearchhandle.cpp index 67b073c7b1d..3cc4a96a44e 100644 --- a/src/plugins/clangrefactoring/qtcreatorsearchhandle.cpp +++ b/src/plugins/clangrefactoring/qtcreatorsearchhandle.cpp @@ -59,6 +59,7 @@ void QtCreatorSearchHandle::cancel() { SearchHandle::cancel(); promise.reportCanceled(); + promise.reportFinished(); } void QtCreatorSearchHandle::finishSearch() diff --git a/src/tools/clangrefactoringbackend/source/clangquerygatherer.cpp b/src/tools/clangrefactoringbackend/source/clangquerygatherer.cpp new file mode 100644 index 00000000000..cbf9a9c70c2 --- /dev/null +++ b/src/tools/clangrefactoringbackend/source/clangquerygatherer.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "clangquerygatherer.h" + +#include "clangquery.h" + +namespace ClangBackEnd { + +ClangQueryGatherer::ClangQueryGatherer(std::vector &&sources, + std::vector &&unsaved, + Utils::SmallString &&query) + : m_sources(std::move(sources)), + m_unsaved(std::move(unsaved)), + m_query(std::move(query)) +{ +} + +SourceRangesAndDiagnosticsForQueryMessage +ClangQueryGatherer::createSourceRangesAndDiagnosticsForSource( + V2::FileContainer &&source, + const std::vector &unsaved, + Utils::SmallString &&query) +{ + ClangQuery clangQuery(std::move(query)); + + clangQuery.addFile(source.filePath().directory(), + source.filePath().name(), + source.takeUnsavedFileContent(), + source.takeCommandLineArguments()); + + clangQuery.addUnsavedFiles(unsaved); + + clangQuery.findLocations(); + + return {clangQuery.takeSourceRanges(), clangQuery.takeDiagnosticContainers()}; +} + +bool ClangQueryGatherer::canCreateSourceRangesAndDiagnostics() const +{ + return !m_sources.empty(); +} + +SourceRangesAndDiagnosticsForQueryMessage ClangQueryGatherer::createNextSourceRangesAndDiagnostics() +{ + auto message = createSourceRangesAndDiagnosticsForSource(std::move(m_sources.back()), + m_unsaved, + m_query.clone()); + m_sources.pop_back(); + + return message; +} + +ClangQueryGatherer::Future ClangQueryGatherer::startCreateNextSourceRangesAndDiagnosticsMessage() +{ + Future future = std::async(std::launch::async, + createSourceRangesAndDiagnosticsForSource, + std::move(m_sources.back()), + m_unsaved, + m_query.clone()); + + m_sources.pop_back(); + + return future; +} + +void ClangQueryGatherer::startCreateNextSourceRangesAndDiagnosticsMessages() +{ + std::vector futures; + + while (!m_sources.empty() && m_sourceFutures.size() < m_processingSlotCount) + m_sourceFutures.push_back(startCreateNextSourceRangesAndDiagnosticsMessage()); +} + +void ClangQueryGatherer::waitForFinished() +{ + for (Future &future : m_sourceFutures) + future.wait(); +} + +bool ClangQueryGatherer::isFinished() const +{ + return m_sources.empty() && m_sourceFutures.empty(); +} + +const std::vector &ClangQueryGatherer::sources() const +{ + return m_sources; +} + +const std::vector &ClangQueryGatherer::sourceFutures() const +{ + return m_sourceFutures; +} + +std::vector ClangQueryGatherer::allCurrentProcessedMessages() +{ + std::vector messages; + + for (Future &future : m_sourceFutures) + messages.push_back(future.get()); + + return messages; +} + +std::vector ClangQueryGatherer::finishedMessages() +{ + std::vector messages; + + for (auto &&future : finishedFutures()) + messages.push_back(future.get()); + + return messages; +} + +void ClangQueryGatherer::setProcessingSlotCount(uint count) +{ + m_processingSlotCount = count; +} + +std::vector ClangQueryGatherer::finishedFutures() +{ + std::vector finishedFutures; + finishedFutures.reserve(m_sourceFutures.size()); + + auto beginReady = std::partition(m_sourceFutures.begin(), + m_sourceFutures.end(), + [] (const Future &future) { + return future.wait_for(std::chrono::duration::zero()) != std::future_status::ready; + }); + + std::move(beginReady, m_sourceFutures.end(), std::back_inserter(finishedFutures)); + m_sourceFutures.erase(beginReady, m_sourceFutures.end()); + + return finishedFutures; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/clangquerygatherer.h b/src/tools/clangrefactoringbackend/source/clangquerygatherer.h new file mode 100644 index 00000000000..a7ba8fac790 --- /dev/null +++ b/src/tools/clangrefactoringbackend/source/clangquerygatherer.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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. +** +****************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace ClangBackEnd { + +class ClangQueryGatherer +{ +public: + using Future = std::future; + + ClangQueryGatherer() = default; + ClangQueryGatherer(std::vector &&sources, + std::vector &&unsaved, + Utils::SmallString &&query); + + static + SourceRangesAndDiagnosticsForQueryMessage createSourceRangesAndDiagnosticsForSource( + V2::FileContainer &&source, + const std::vector &unsaved, + Utils::SmallString &&query); + + bool canCreateSourceRangesAndDiagnostics() const; + SourceRangesAndDiagnosticsForQueryMessage createNextSourceRangesAndDiagnostics(); + Future startCreateNextSourceRangesAndDiagnosticsMessage(); + void startCreateNextSourceRangesAndDiagnosticsMessages(); + void waitForFinished(); + bool isFinished() const; + + const std::vector &sources() const; + const std::vector &sourceFutures() const; + std::vector allCurrentProcessedMessages(); + std::vector finishedMessages(); + + void setProcessingSlotCount(uint count); + +protected: + std::vector finishedFutures(); + +private: + std::vector m_sources; + std::vector m_unsaved; + Utils::SmallString m_query; + uint m_processingSlotCount = 1; + std::vector m_sourceFutures; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri index cdcce846f11..37d503f210c 100644 --- a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri +++ b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri @@ -2,6 +2,7 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/clangrefactoringbackend_global.h \ + $$PWD/clangquerygatherer.h !isEmpty(LIBTOOLING_LIBS) { SOURCES += \ @@ -31,3 +32,6 @@ HEADERS += \ $$PWD/sourcerangeextractor.h \ $$PWD/locationsourcefilecallbacks.h } + +SOURCES += \ + $$PWD/clangquerygatherer.cpp diff --git a/src/tools/clangrefactoringbackend/source/refactoringserver.cpp b/src/tools/clangrefactoringbackend/source/refactoringserver.cpp index 4468c5e13da..58e944784df 100644 --- a/src/tools/clangrefactoringbackend/source/refactoringserver.cpp +++ b/src/tools/clangrefactoringbackend/source/refactoringserver.cpp @@ -36,16 +36,17 @@ #include -#include -#include -#include -#include +#include namespace ClangBackEnd { RefactoringServer::RefactoringServer() { - pollEventLoop = [] () { QCoreApplication::processEvents(); }; + m_pollTimer.setInterval(100); + + QObject::connect(&m_pollTimer, + &QTimer::timeout, + std::bind(&RefactoringServer::pollSourceRangesAndDiagnosticsForQueryMessages, this)); } void RefactoringServer::end() @@ -72,110 +73,62 @@ void RefactoringServer::requestSourceLocationsForRenamingMessage(RequestSourceLo void RefactoringServer::requestSourceRangesAndDiagnosticsForQueryMessage( RequestSourceRangesAndDiagnosticsForQueryMessage &&message) { - gatherSourceRangesAndDiagnosticsForQueryMessage(message.takeSources(), - message.takeUnsavedContent(), - message.takeQuery()); + gatherSourceRangesAndDiagnosticsForQueryMessages(message.takeSources(), + message.takeUnsavedContent(), + message.takeQuery()); } void RefactoringServer::cancel() { - cancelWork = true; + m_gatherer.waitForFinished(); + m_gatherer = ClangQueryGatherer(); + m_pollTimer.stop(); } bool RefactoringServer::isCancelingJobs() const { - return cancelWork; + return m_gatherer.isFinished(); } -void RefactoringServer::supersedePollEventLoop(std::function &&pollEventLoop) +void RefactoringServer::pollSourceRangesAndDiagnosticsForQueryMessages() { - this->pollEventLoop = std::move(pollEventLoop); + for (auto &&message : m_gatherer.finishedMessages()) + client()->sourceRangesAndDiagnosticsForQueryMessage(std::move(message)); + + if (!m_gatherer.isFinished()) + m_gatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + else + m_pollTimer.stop(); } -namespace { - -SourceRangesAndDiagnosticsForQueryMessage createSourceRangesAndDiagnosticsForQueryMessage( - V2::FileContainer &&source, - std::vector &&unsaved, - Utils::SmallString &&query, - const std::atomic_bool &cancelWork) { - ClangQuery clangQuery(std::move(query)); - - if (!cancelWork) { - clangQuery.addFile(source.filePath().directory(), - source.filePath().name(), - source.takeUnsavedFileContent(), - source.takeCommandLineArguments()); - - clangQuery.addUnsavedFiles(std::move(unsaved)); - - clangQuery.findLocations(); +void RefactoringServer::waitThatSourceRangesAndDiagnosticsForQueryMessagesAreFinished() +{ + while (!m_gatherer.isFinished()) { + m_gatherer.waitForFinished(); + pollSourceRangesAndDiagnosticsForQueryMessages(); } - - return {clangQuery.takeSourceRanges(), clangQuery.takeDiagnosticContainers()}; } - +bool RefactoringServer::pollTimerIsActive() const +{ + return m_pollTimer.isActive(); } -void RefactoringServer::gatherSourceRangesAndDiagnosticsForQueryMessage( +void RefactoringServer::gatherSourceRangesAndDiagnosticsForQueryMessages( std::vector &&sources, std::vector &&unsaved, Utils::SmallString &&query) { - std::vector futures; - #ifdef _WIN32 - std::size_t freeProcessors = 1; + uint freeProcessors = 1; #else - std::size_t freeProcessors = std::thread::hardware_concurrency(); + uint freeProcessors = std::thread::hardware_concurrency(); #endif - while (!sources.empty() || !futures.empty()) { - --freeProcessors; + m_gatherer = ClangQueryGatherer(std::move(sources), std::move(unsaved), std::move(query)); + m_gatherer.setProcessingSlotCount(freeProcessors); - if (!sources.empty()) { - Future &&future = std::async(std::launch::async, - createSourceRangesAndDiagnosticsForQueryMessage, - std::move(sources.back()), - Utils::clone(unsaved), - query.clone(), - std::ref(cancelWork)); - sources.pop_back(); - - futures.emplace_back(std::move(future)); - } - - if (freeProcessors == 0 || sources.empty()) - freeProcessors += waitForNewSourceRangesAndDiagnosticsForQueryMessage(futures); - } -} - -std::size_t RefactoringServer::waitForNewSourceRangesAndDiagnosticsForQueryMessage(std::vector &futures) -{ - while (true) { - pollEventLoop(); - - std::vector readyFutures; - readyFutures.reserve(futures.size()); - - auto beginReady = std::partition(futures.begin(), - futures.end(), - [] (const Future &future) { - return future.wait_for(std::chrono::duration::zero()) != std::future_status::ready; - }); - - std::move(beginReady, futures.end(), std::back_inserter(readyFutures)); - futures.erase(beginReady, futures.end()); - - for (Future &readyFuture : readyFutures) - client()->sourceRangesAndDiagnosticsForQueryMessage(readyFuture.get()); - - if (readyFutures.empty()) - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - else - return readyFutures.size(); - } + m_pollTimer.start(); } } // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/refactoringserver.h b/src/tools/clangrefactoringbackend/source/refactoringserver.h index b7d6adfb154..e3b40d3c56f 100644 --- a/src/tools/clangrefactoringbackend/source/refactoringserver.h +++ b/src/tools/clangrefactoringbackend/source/refactoringserver.h @@ -25,9 +25,12 @@ #pragma once +#include "clangquerygatherer.h" + #include -#include +#include + #include namespace ClangBackEnd { @@ -51,18 +54,19 @@ public: bool isCancelingJobs() const; - void supersedePollEventLoop(std::function &&pollEventLoop); + void pollSourceRangesAndDiagnosticsForQueryMessages(); + void waitThatSourceRangesAndDiagnosticsForQueryMessagesAreFinished(); + + bool pollTimerIsActive() const; private: - void gatherSourceRangesAndDiagnosticsForQueryMessage(std::vector &&sources, - std::vector &&unsaved, - Utils::SmallString &&query); - std::size_t waitForNewSourceRangesAndDiagnosticsForQueryMessage(std::vector &futures); + void gatherSourceRangesAndDiagnosticsForQueryMessages(std::vector &&sources, + std::vector &&unsaved, + Utils::SmallString &&query); private: - std::function pollEventLoop; - std::atomic_bool cancelWork{false}; - + ClangQueryGatherer m_gatherer; + QTimer m_pollTimer; }; } // namespace ClangBackEnd diff --git a/tests/unit/unittest/clangquery-test.cpp b/tests/unit/unittest/clangquery-test.cpp index a43872c43d6..b71e267697d 100644 --- a/tests/unit/unittest/clangquery-test.cpp +++ b/tests/unit/unittest/clangquery-test.cpp @@ -89,6 +89,20 @@ TEST_F(ClangQuerySlowTest, SourceRangeInUnsavedFileDeclarationRange) IsSourceRangeWithText(1, 1, 1, 15, "void unsaved();")); } +TEST_F(ClangQuerySlowTest, DISABLED_SourceRangeInUnsavedFileDeclarationRangeOverride) // seems not to work in Clang +{ + ::ClangQuery query; + query.addFile(TESTDATA_DIR, "query_simplefunction.cpp", "void f() {}", {"cc", "query_simplefunction.cpp", "-std=c++14"}); + query.setQuery("functionDecl()"); + ClangBackEnd::V2::FileContainer unsavedFile{{TESTDATA_DIR, "query_simplefunction.cpp"}, "void unsaved();", {}}; + query.addUnsavedFiles({unsavedFile}); + + query.findLocations(); + + ASSERT_THAT(query.takeSourceRanges().sourceRangeWithTextContainers().at(0), + IsSourceRangeWithText(1, 1, 1, 15, "void unsaved();")); +} + TEST_F(ClangQuerySlowTest, RootSourceRangeForSimpleFieldDeclarationRange) { simpleClassQuery.setQuery("fieldDecl(hasType(isInteger()))"); diff --git a/tests/unit/unittest/clangquerygatherer-test.cpp b/tests/unit/unittest/clangquerygatherer-test.cpp new file mode 100644 index 00000000000..089f273f062 --- /dev/null +++ b/tests/unit/unittest/clangquerygatherer-test.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "googletest.h" + +#include "sourcerangecontainer-matcher.h" + +#include + +#include + +namespace { + +using testing::AllOf; +using testing::AtLeast; +using testing::AtMost; +using testing::Contains; +using testing::Each; +using testing::Eq; +using testing::Ge; +using testing::IsEmpty; +using testing::Le; +using testing::NiceMock; +using testing::Pair; +using testing::PrintToString; +using testing::Property; +using testing::SizeIs; +using testing::_; + +using ClangBackEnd::V2::FileContainer; +using ClangBackEnd::SourceRangesAndDiagnosticsForQueryMessage; +using ClangBackEnd::SourceRangesContainer; +using ClangBackEnd::SourceRangesContainer; + +MATCHER_P2(Contains, line, column, + std::string(negation ? "isn't " : "is ") + + "(" + PrintToString(line) + + ", " + PrintToString(column) + + ")" + ) +{ + return arg.line() == uint(line) + && arg.column() == uint(column); +} + +class ClangQueryGatherer : public ::testing::Test +{ +protected: + void SetUp() override; + +protected: + Utils::SmallString sourceContent{"#include \"query_simplefunction.h\"\nvoid f()\n {}"}; + FileContainer source{{TESTDATA_DIR, "query_simplefunction.cpp"}, + sourceContent.clone(), + {"cc", "query_simplefunction.cpp"}}; + Utils::SmallString unsavedContent{"void f();"}; + FileContainer unsaved{{TESTDATA_DIR, "query_simplefunction.h"}, + unsavedContent.clone(), + {}}; + Utils::SmallString query{"functionDecl()"}; + ClangBackEnd::ClangQueryGatherer gatherer{{source.clone()}, {unsaved.clone()}, query.clone()}; + ClangBackEnd::ClangQueryGatherer manyGatherer{{source.clone(), source.clone(), source.clone()}, + {unsaved.clone()}, + query.clone()}; +}; + +TEST_F(ClangQueryGatherer, CreateSourceRangesAndDiagnostics) +{ + auto sourceRangesAndDiagnostics = gatherer.createSourceRangesAndDiagnosticsForSource(source.clone(), {}, query.clone()); + + ASSERT_THAT(sourceRangesAndDiagnostics, + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(2, 1, 3, 4, "void f()\n {}"))))); +} + +TEST_F(ClangQueryGatherer, CreateSourceRangesAndDiagnosticssWithUnsavedContent) +{ + auto sourceRangesAndDiagnostics = gatherer.createSourceRangesAndDiagnosticsForSource(source.clone(), {unsaved}, query.clone()); + + ASSERT_THAT(sourceRangesAndDiagnostics, + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(1, 1, 1, 9, "void f();"))))); +} + +TEST_F(ClangQueryGatherer, CanCreateSourceRangesAndDiagnosticsIfItHasSources) +{ + ASSERT_TRUE(gatherer.canCreateSourceRangesAndDiagnostics()); +} + +TEST_F(ClangQueryGatherer, CanNotCreateSourceRangesAndDiagnosticsIfItHasNoSources) +{ + ClangBackEnd::ClangQueryGatherer empthyGatherer{{}, {unsaved.clone()}, query.clone()}; + + ASSERT_FALSE(empthyGatherer.canCreateSourceRangesAndDiagnostics()); +} + +TEST_F(ClangQueryGatherer, CreateSourceRangesAndDiagnosticsForNextSource) +{ + auto sourceRangesAndDiagnostics = gatherer.createNextSourceRangesAndDiagnostics(); + + ASSERT_THAT(sourceRangesAndDiagnostics, + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(1, 1, 1, 9, "void f();"))))); +} + +TEST_F(ClangQueryGatherer, CreateSourceRangesAndDiagnosticsForNextSourcePopsSource) +{ + manyGatherer.createNextSourceRangesAndDiagnostics(); + + ASSERT_THAT(manyGatherer.sources(), SizeIs(2)); +} + +TEST_F(ClangQueryGatherer, StartCreateSourceRangesAndDiagnosticsForNextSource) +{ + auto future = gatherer.startCreateNextSourceRangesAndDiagnosticsMessage(); + future.wait(); + + ASSERT_THAT(future.get(), + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(1, 1, 1, 9, "void f();"))))); +} + +TEST_F(ClangQueryGatherer, StartCreateSourceRangesAndDiagnosticsForNextSourcePopsSource) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessage(); + + ASSERT_THAT(manyGatherer.sources(), SizeIs(2)); +} + +TEST_F(ClangQueryGatherer, AfterStartCreateSourceRangesAndDiagnosticsMessagesFutureCountIsTwos) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + + ASSERT_THAT(manyGatherer.sourceFutures(), SizeIs(2)); +} + +TEST_F(ClangQueryGatherer, AfterRestartCreateSourceRangesAndDiagnosticsMessagesFutureCountIsTwos) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + + ASSERT_THAT(manyGatherer.sourceFutures(), SizeIs(2)); +} + +TEST_F(ClangQueryGatherer, AfterStartCreateSourceRangesAndDiagnosticsMessagesGetCollected) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + + ASSERT_THAT(manyGatherer.allCurrentProcessedMessages(), + Each( + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(1, 1, 1, 9, "void f();")))))); +} + + + +TEST_F(ClangQueryGatherer, GetFinishedMessages) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + + auto messages = manyGatherer.finishedMessages(); + + ASSERT_THAT(messages, + AllOf(SizeIs(2), + Each( + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(1, 1, 1, 9, "void f();"))))))); +} + +TEST_F(ClangQueryGatherer, GetFinishedMessagesAfterSecondPass) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + + auto messages = manyGatherer.finishedMessages(); + + ASSERT_THAT(messages, + AllOf(SizeIs(1), + Each( + Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, + Property(&SourceRangesContainer::sourceRangeWithTextContainers, + Contains(IsSourceRangeWithText(1, 1, 1, 9, "void f();"))))))); +} + +TEST_F(ClangQueryGatherer, AfterGetFinishedMessagesFuturesAreReduced) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + + manyGatherer.finishedMessages(); + + ASSERT_THAT(manyGatherer.sourceFutures(), SizeIs(0)); +} + +TEST_F(ClangQueryGatherer, SourceFutureIsOneInTheSecondRun) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + + ASSERT_THAT(manyGatherer.sourceFutures(), SizeIs(1)); +} + +TEST_F(ClangQueryGatherer, GetOneMessageInTheSecondRun) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + + auto messages = manyGatherer.finishedMessages(); + + ASSERT_THAT(messages, SizeIs(1)); +} + +TEST_F(ClangQueryGatherer, IsNotFinishedIfSourcesExists) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + + bool isFinished = manyGatherer.isFinished(); + + ASSERT_FALSE(isFinished); +} + +TEST_F(ClangQueryGatherer, IsNotFinishedIfSourceFuturesExists) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + + bool isFinished = manyGatherer.isFinished(); + + ASSERT_FALSE(isFinished); +} + +TEST_F(ClangQueryGatherer, IsFinishedIfNoSourceAndSourceFuturesExists) +{ + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + manyGatherer.startCreateNextSourceRangesAndDiagnosticsMessages(); + manyGatherer.waitForFinished(); + manyGatherer.finishedMessages(); + + bool isFinished = manyGatherer.isFinished(); + + ASSERT_TRUE(isFinished); +} + +void ClangQueryGatherer::SetUp() +{ + manyGatherer.setProcessingSlotCount(2); +} + +} diff --git a/tests/unit/unittest/refactoringserver-test.cpp b/tests/unit/unittest/refactoringserver-test.cpp index bfcb4c7324a..33b63534e3f 100644 --- a/tests/unit/unittest/refactoringserver-test.cpp +++ b/tests/unit/unittest/refactoringserver-test.cpp @@ -67,7 +67,9 @@ MATCHER_P2(IsSourceLocation, line, column, class RefactoringServer : public ::testing::Test { +protected: void SetUp() override; + void TearDown() override; protected: ClangBackEnd::RefactoringServer refactoringServer; @@ -99,8 +101,7 @@ TEST_F(RefactoringServerSlowTest, RequestSourceLocationsForRenamingMessage) AllOf(Contains(IsSourceLocation(1, 5)), Contains(IsSourceLocation(3, 9)))), Property(&SourceLocationsContainer::filePaths, - Contains(Pair(_, FilePath(TESTDATA_DIR, "renamevariable.cpp"))))))))) - .Times(1); + Contains(Pair(_, FilePath(TESTDATA_DIR, "renamevariable.cpp"))))))))); refactoringServer.requestSourceLocationsForRenamingMessage(std::move(requestSourceLocationsForRenamingMessage)); } @@ -115,8 +116,7 @@ TEST_F(RefactoringServerSlowTest, RequestSingleSourceRangesAndDiagnosticsForQuer sourceRangesAndDiagnosticsForQueryMessage( Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, Property(&SourceRangesContainer::sourceRangeWithTextContainers, - Contains(IsSourceRangeWithText(1, 1, 2, 4, sourceContent)))))) - .Times(1); + Contains(IsSourceRangeWithText(1, 1, 2, 4, sourceContent)))))); refactoringServer.requestSourceRangesAndDiagnosticsForQueryMessage(std::move(requestSourceRangesAndDiagnosticsForQueryMessage)); } @@ -138,8 +138,7 @@ TEST_F(RefactoringServerSlowTest, RequestSingleSourceRangesAndDiagnosticsWithUns sourceRangesAndDiagnosticsForQueryMessage( Property(&SourceRangesAndDiagnosticsForQueryMessage::sourceRanges, Property(&SourceRangesContainer::sourceRangeWithTextContainers, - Contains(IsSourceRangeWithText(1, 1, 1, 9, unsavedContent)))))) - .Times(1); + Contains(IsSourceRangeWithText(1, 1, 1, 9, unsavedContent)))))); refactoringServer.requestSourceRangesAndDiagnosticsForQueryMessage(std::move(requestSourceRangesAndDiagnosticsForQueryMessage)); } @@ -181,13 +180,6 @@ TEST_F(RefactoringServerVerySlowTest, RequestManySourceRangesAndDiagnosticsForQu } TEST_F(RefactoringServer, CancelJobs) -{ - refactoringServer.cancel(); - - ASSERT_TRUE(refactoringServer.isCancelingJobs()); -} - -TEST_F(RefactoringServerVerySlowTest, PollEventLoopAsQueryIsRunning) { std::vector sources; std::fill_n(std::back_inserter(sources), @@ -196,12 +188,46 @@ TEST_F(RefactoringServerVerySlowTest, PollEventLoopAsQueryIsRunning) RequestSourceRangesAndDiagnosticsForQueryMessage requestSourceRangesAndDiagnosticsForQueryMessage{"functionDecl()", std::move(sources), {}}; - bool eventLoopIsPolled = false; - refactoringServer.supersedePollEventLoop([&] () { eventLoopIsPolled = true; }); + refactoringServer.requestSourceRangesAndDiagnosticsForQueryMessage(std::move(requestSourceRangesAndDiagnosticsForQueryMessage)); + + refactoringServer.cancel(); + + ASSERT_TRUE(refactoringServer.isCancelingJobs()); +} + +TEST_F(RefactoringServer, PollTimerIsActiveAfterStart) +{ + RequestSourceRangesAndDiagnosticsForQueryMessage requestSourceRangesAndDiagnosticsForQueryMessage{"functionDecl()", + {source}, + {}}; refactoringServer.requestSourceRangesAndDiagnosticsForQueryMessage(std::move(requestSourceRangesAndDiagnosticsForQueryMessage)); - ASSERT_TRUE(eventLoopIsPolled); + ASSERT_TRUE(refactoringServer.pollTimerIsActive()); +} + +TEST_F(RefactoringServer, PollTimerIsNotActiveAfterFinishing) +{ + RequestSourceRangesAndDiagnosticsForQueryMessage requestSourceRangesAndDiagnosticsForQueryMessage{"functionDecl()", + {source}, + {}}; + refactoringServer.requestSourceRangesAndDiagnosticsForQueryMessage(std::move(requestSourceRangesAndDiagnosticsForQueryMessage)); + + refactoringServer.waitThatSourceRangesAndDiagnosticsForQueryMessagesAreFinished(); + + ASSERT_FALSE(refactoringServer.pollTimerIsActive()); +} + +TEST_F(RefactoringServer, PollTimerNotIsActiveAfterCanceling) +{ + RequestSourceRangesAndDiagnosticsForQueryMessage requestSourceRangesAndDiagnosticsForQueryMessage{"functionDecl()", + {source}, + {}}; + refactoringServer.requestSourceRangesAndDiagnosticsForQueryMessage(std::move(requestSourceRangesAndDiagnosticsForQueryMessage)); + + refactoringServer.cancel(); + + ASSERT_FALSE(refactoringServer.pollTimerIsActive()); } void RefactoringServer::SetUp() @@ -209,4 +235,9 @@ void RefactoringServer::SetUp() refactoringServer.setClient(&mockRefactoringClient); } +void RefactoringServer::TearDown() +{ + refactoringServer.waitThatSourceRangesAndDiagnosticsForQueryMessagesAreFinished(); +} + } diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index f9447eee92f..054ae386e59 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -63,6 +63,7 @@ SOURCES += \ projectupdater-test.cpp \ pchmanagerserver-test.cpp \ pchmanagerclientserverinprocess-test.cpp \ + clangquerygatherer-test.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \