Clang: Watch directories instead of files

Because there a limited resources to watch files we watch now directories.
So we need much less resources.

Change-Id: Iac558832e9521a7a1a67c5ea99b42ad1b0b5129c
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2019-06-06 10:57:55 +02:00
parent 9d290fc682
commit bbd58ca30b
38 changed files with 1485 additions and 541 deletions

View File

@@ -1,4 +1,4 @@
/****************************************************************************
; /****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
@@ -27,71 +27,94 @@
#include "clangpathwatcherinterface.h"
#include "clangpathwatchernotifier.h"
#include "changedfilepathcompressor.h"
#include "directorypathcompressor.h"
#include "filepathcachinginterface.h"
#include "filesystem.h"
#include "stringcache.h"
#include <utils/algorithm.h>
#include <QTimer>
namespace ClangBackEnd {
template<class InputIt1, class InputIt2, class Callable>
void set_greedy_intersection_call(
InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable)
{
while (first1 != last1 && first2 != last2) {
if (*first1 < *first2) {
++first1;
} else {
if (*first2 < *first1)
++first2;
else
callable(*first1++);
}
}
}
class WatcherEntry
{
public:
ProjectPartId id;
FilePathId pathId;
DirectoryPathId directoryPathId;
FilePathId filePathId;
long long lastModified = -1;
friend bool operator==(WatcherEntry first, WatcherEntry second)
{
return first.id == second.id && first.pathId == second.pathId;
return first.id == second.id && first.directoryPathId == second.directoryPathId
&& first.filePathId == second.filePathId;
}
friend bool operator<(WatcherEntry first, WatcherEntry second)
{
return std::tie(first.pathId, first.id) < std::tie(second.pathId, second.id);
return std::tie(first.directoryPathId, first.filePathId, first.id)
< std::tie(second.directoryPathId, second.filePathId, second.id);
}
friend bool operator<(WatcherEntry entry, FilePathId pathId)
friend bool operator<(DirectoryPathId directoryPathId, WatcherEntry entry)
{
return entry.pathId < pathId;
return directoryPathId < entry.directoryPathId;
}
friend bool operator<(FilePathId pathId, WatcherEntry entry)
friend bool operator<(WatcherEntry entry, DirectoryPathId directoryPathId)
{
return pathId < entry.pathId;
return entry.directoryPathId < directoryPathId;
}
operator FilePathId() const
{
return pathId;
}
operator FilePathId() const { return filePathId; }
operator DirectoryPathId() const { return directoryPathId; }
};
using WatcherEntries = std::vector<WatcherEntry>;
template <typename FileSystemWatcher,
typename Timer>
template<typename FileSystemWatcher, typename Timer>
class CLANGSUPPORT_GCCEXPORT ClangPathWatcher : public ClangPathWatcherInterface
{
public:
ClangPathWatcher(FilePathCachingInterface &pathCache,
ClangPathWatcherNotifier *notifier=nullptr)
: m_changedFilePathCompressor(pathCache),
m_pathCache(pathCache),
m_notifier(notifier)
FileSystemInterface &fileSystem,
ClangPathWatcherNotifier *notifier = nullptr)
: m_pathCache(pathCache)
, m_fileStatusCache(fileSystem)
, m_fileSystem(fileSystem)
, m_notifier(notifier)
{
QObject::connect(&m_fileSystemWatcher,
&FileSystemWatcher::fileChanged,
[&] (const QString &filePath) { compressChangedFilePath(filePath); });
&FileSystemWatcher::directoryChanged,
[&](const QString &path) { compressChangedDirectoryPath(path); });
m_changedFilePathCompressor.setCallback([&] (ClangBackEnd::FilePathIds &&filePathIds) {
addChangedPathForFilePath(std::move(filePathIds));
m_directoryPathCompressor.setCallback([&](ClangBackEnd::DirectoryPathIds &&directoryPathIds) {
addChangedPathForFilePath(std::move(directoryPathIds));
});
}
~ClangPathWatcher()
{
m_changedFilePathCompressor.setCallback([&] (FilePathIds &&) {});
m_directoryPathCompressor.setCallback([&](DirectoryPathIds &&) {});
}
void updateIdPaths(const std::vector<IdPaths> &idPaths) override
@@ -109,7 +132,7 @@ public:
auto filteredPaths = filterNotWatchedPaths(removedEntries);
if (!filteredPaths.empty())
m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths));
m_fileSystemWatcher.removePaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
}
void setNotifier(ClangPathWatcherNotifier *notifier) override
@@ -164,7 +187,13 @@ public:
outputIterator = std::transform(idPath.filePathIds.begin(),
idPath.filePathIds.end(),
outputIterator,
[&] (FilePathId pathId) { return WatcherEntry{id, pathId}; });
[&](FilePathId filePathId) {
return WatcherEntry{
id,
m_pathCache.directoryPathId(filePathId),
filePathId,
m_fileStatusCache.lastModifiedTime(filePathId)};
});
}
std::sort(entries.begin(), entries.end());
@@ -182,7 +211,7 @@ public:
mergeToWatchedEntries(newEntries);
if (!filteredPaths.empty())
m_fileSystemWatcher.addPaths(convertWatcherEntriesToQStringList(filteredPaths));
m_fileSystemWatcher.addPaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
}
void removeUnusedEntries(const WatcherEntries &entries, const ProjectPartIds &ids)
@@ -194,33 +223,31 @@ public:
auto filteredPaths = filterNotWatchedPaths(oldEntries);
if (!filteredPaths.empty())
m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths));
m_fileSystemWatcher.removePaths(convertWatcherEntriesToDirectoryPathList(filteredPaths));
}
FileSystemWatcher &fileSystemWatcher()
{
return m_fileSystemWatcher;
}
FileSystemWatcher &fileSystemWatcher() { return m_fileSystemWatcher; }
QStringList convertWatcherEntriesToQStringList(
const WatcherEntries &watcherEntries)
QStringList convertWatcherEntriesToDirectoryPathList(const DirectoryPathIds &directoryPathIds) const
{
QStringList paths;
paths.reserve(int(watcherEntries.size()));
std::transform(watcherEntries.begin(),
watcherEntries.end(),
std::back_inserter(paths),
[&] (WatcherEntry entry) {
return QString(m_pathCache.filePath(entry.pathId).path());
return Utils::transform<QStringList>(directoryPathIds, [&](DirectoryPathId id) {
return QString(m_pathCache.directoryPath(id));
});
return paths;
}
template <typename Compare>
WatcherEntries notWatchedEntries(const WatcherEntries &entries,
Compare compare) const
QStringList convertWatcherEntriesToDirectoryPathList(const WatcherEntries &watcherEntries) const
{
DirectoryPathIds directoryPathIds = Utils::transform<DirectoryPathIds>(
watcherEntries, [&](WatcherEntry entry) { return entry.directoryPathId; });
std::sort(directoryPathIds.begin(), directoryPathIds.end());
directoryPathIds.erase(std::unique(directoryPathIds.begin(), directoryPathIds.end()),
directoryPathIds.end());
return convertWatcherEntriesToDirectoryPathList(directoryPathIds);
}
WatcherEntries notWatchedEntries(const WatcherEntries &entries) const
{
WatcherEntries notWatchedEntries;
notWatchedEntries.reserve(entries.size());
@@ -229,24 +256,23 @@ public:
entries.end(),
m_watchedEntries.cbegin(),
m_watchedEntries.cend(),
std::back_inserter(notWatchedEntries),
compare);
std::back_inserter(notWatchedEntries));
return notWatchedEntries;
}
WatcherEntries notWatchedEntries(const WatcherEntries &entries) const
DirectoryPathIds notWatchedPaths(const DirectoryPathIds &ids) const
{
return notWatchedEntries(entries, std::less<WatcherEntry>());
}
DirectoryPathIds notWatchedDirectoryIds;
notWatchedDirectoryIds.reserve(ids.size());
WatcherEntries notWatchedPaths(const WatcherEntries &entries) const
{
auto compare = [] (WatcherEntry first, WatcherEntry second) {
return first.pathId < second.pathId;
};
std::set_difference(ids.begin(),
ids.end(),
m_watchedEntries.cbegin(),
m_watchedEntries.cend(),
std::back_inserter(notWatchedDirectoryIds));
return notWatchedEntries(entries, compare);
return notWatchedDirectoryIds;
}
template <typename Compare>
@@ -297,25 +323,24 @@ public:
m_watchedEntries = std::move(newWatchedEntries);
}
static
WatcherEntries uniquePaths(const WatcherEntries &pathEntries)
static DirectoryPathIds uniquePaths(const WatcherEntries &pathEntries)
{
WatcherEntries uniqueEntries;
uniqueEntries.reserve(pathEntries.size());
DirectoryPathIds uniqueDirectoryIds;
uniqueDirectoryIds.reserve(pathEntries.size());
auto compare = [] (WatcherEntry first, WatcherEntry second) {
return first.pathId == second.pathId;
auto compare = [](WatcherEntry first, WatcherEntry second) {
return first.directoryPathId == second.directoryPathId;
};
std::unique_copy(pathEntries.begin(),
pathEntries.end(),
std::back_inserter(uniqueEntries),
std::back_inserter(uniqueDirectoryIds),
compare);
return uniqueEntries;
return uniqueDirectoryIds;
}
WatcherEntries filterNotWatchedPaths(const WatcherEntries &entries)
DirectoryPathIds filterNotWatchedPaths(const WatcherEntries &entries) const
{
return notWatchedPaths(uniquePaths(entries));
}
@@ -351,40 +376,48 @@ public:
oldEntries.end(),
std::back_inserter(newWatchedEntries));
m_watchedEntries = newWatchedEntries;
m_watchedEntries = std::move(newWatchedEntries);
}
void compressChangedFilePath(const QString &filePath)
void compressChangedDirectoryPath(const QString &path)
{
m_changedFilePathCompressor.addFilePath(filePath);
m_directoryPathCompressor.addDirectoryPathId(
m_pathCache.directoryPathId(Utils::PathString{path}));
}
WatcherEntries watchedEntriesForPaths(ClangBackEnd::FilePathIds &&filePathIds)
WatcherEntries watchedEntriesForPaths(ClangBackEnd::DirectoryPathIds &&directoryPathIds)
{
WatcherEntries foundEntries;
foundEntries.reserve(filePathIds.size());
foundEntries.reserve(m_watchedEntries.size());
for (FilePathId pathId : filePathIds) {
auto range = std::equal_range(m_watchedEntries.begin(), m_watchedEntries.end(), pathId);
foundEntries.insert(foundEntries.end(), range.first, range.second);
}
set_greedy_intersection_call(m_watchedEntries.begin(),
m_watchedEntries.end(),
directoryPathIds.begin(),
directoryPathIds.end(),
[&](WatcherEntry &entry) {
m_fileStatusCache.update(entry.filePathId);
auto currentLastModified = m_fileStatusCache.lastModifiedTime(
entry.filePathId);
if (entry.lastModified < currentLastModified) {
foundEntries.push_back(entry);
entry.lastModified = currentLastModified;
}
});
return foundEntries;
}
FilePathIds watchedPaths(const FilePathIds &filePathIds) const
FilePathIds watchedPaths(const WatcherEntries &entries) const
{
FilePathIds watchedFilePathIds;
watchedFilePathIds.reserve(filePathIds.size());
auto filePathIds = Utils::transform<FilePathIds>(entries, [](WatcherEntry entry) {
return entry.filePathId;
});
std::set_intersection(m_watchedEntries.begin(),
m_watchedEntries.end(),
filePathIds.begin(),
filePathIds.end(),
std::back_inserter(watchedFilePathIds));
std::sort(filePathIds.begin(), filePathIds.end());
return watchedFilePathIds;
filePathIds.erase(std::unique(filePathIds.begin(), filePathIds.end()), filePathIds.end());
return filePathIds;
}
ProjectPartIds idsForWatcherEntries(const WatcherEntries &foundEntries)
@@ -403,21 +436,20 @@ public:
ProjectPartIds uniqueIds(ProjectPartIds &&ids)
{
std::sort(ids.begin(), ids.end());
auto newEnd = std::unique(ids.begin(), ids.end());
ids.erase(newEnd, ids.end());
ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
return std::move(ids);
}
void addChangedPathForFilePath(FilePathIds &&filePathIds)
void addChangedPathForFilePath(DirectoryPathIds &&directoryPathIds)
{
if (m_notifier) {
WatcherEntries foundEntries = watchedEntriesForPaths(std::move(filePathIds));
WatcherEntries foundEntries = watchedEntriesForPaths(std::move(directoryPathIds));
ProjectPartIds changedIds = idsForWatcherEntries(foundEntries);
m_notifier->pathsWithIdsChanged(uniqueIds(std::move(changedIds)));
m_notifier->pathsChanged(watchedPaths(filePathIds));
m_notifier->pathsChanged(watchedPaths(foundEntries));
}
}
@@ -428,10 +460,12 @@ public:
private:
WatcherEntries m_watchedEntries;
ChangedFilePathCompressor<Timer> m_changedFilePathCompressor;
FileSystemWatcher m_fileSystemWatcher;
FileStatusCache m_fileStatusCache;
FileSystemInterface &m_fileSystem;
FilePathCachingInterface &m_pathCache;
ClangPathWatcherNotifier *m_notifier;
DirectoryPathCompressor<Timer> m_directoryPathCompressor;
};
} // namespace ClangBackEnd

View File

@@ -18,6 +18,7 @@ SOURCES += \
$$PWD/clangcodemodelserverproxy.cpp \
$$PWD/alivemessage.cpp \
$$PWD/completionsmessage.cpp \
$$PWD/filesystem.cpp \
$$PWD/requestcompletionsmessage.cpp \
$$PWD/echomessage.cpp \
$$PWD/endmessage.cpp \
@@ -87,7 +88,8 @@ SOURCES += \
$$PWD/removegeneratedfilesmessage.cpp \
$$PWD/generatedfiles.cpp \
$$PWD/projectpartartefact.cpp \
$$PWD/projectpartcontainer.cpp
$$PWD/projectpartcontainer.cpp \
$$PWD/filestatuscache.cpp
HEADERS += \
$$PWD/cancelmessage.h \
@@ -109,7 +111,11 @@ HEADERS += \
$$PWD/alivemessage.h \
$$PWD/clangsupportexceptions.h \
$$PWD/completionsmessage.h \
$$PWD/directoryandfilepathid.h \
$$PWD/directorypathid.h \
$$PWD/executeinloop.h \
$$PWD/filesystem.h \
$$PWD/filesysteminterface.h \
$$PWD/pchpaths.h \
$$PWD/projectpartid.h \
$$PWD/projectpartsstorage.h \
@@ -217,6 +223,8 @@ HEADERS += \
$$PWD/sourceentry.h \
$$PWD/modifiedtimecheckerinterface.h \
$$PWD/environment.h \
$$PWD/filestatus.h \
$$PWD/filestatuscache.h \
$$PWD/modifiedtimechecker.h
contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "directorypathid.h"
#include "filepathid.h"
#include <QDataStream>
#include <vector>
namespace ClangBackEnd {
class DirectoryAndFilePathId
{
public:
constexpr DirectoryAndFilePathId() = default;
DirectoryAndFilePathId(const char *) = delete;
DirectoryAndFilePathId(int directoryPathId, int filePathId)
: directoryPathId(directoryPathId)
, filePathId(filePathId)
{}
bool isValid() const { return directoryPathId.isValid() && filePathId.isValid(); }
friend bool operator==(DirectoryAndFilePathId first, DirectoryAndFilePathId second)
{
return first.isValid() && first.directoryPathId == second.directoryPathId
&& first.filePathId == second.filePathId;
}
friend bool operator!=(DirectoryAndFilePathId first, DirectoryAndFilePathId second)
{
return !(first == second);
}
friend bool operator<(DirectoryAndFilePathId first, DirectoryAndFilePathId second)
{
return std::tie(first.directoryPathId, first.filePathId)
< std::tie(second.directoryPathId, second.filePathId);
}
friend QDataStream &operator<<(QDataStream &out,
const DirectoryAndFilePathId &directoryAndFilePathId)
{
out << directoryAndFilePathId.directoryPathId;
out << directoryAndFilePathId.filePathId;
return out;
}
friend QDataStream &operator>>(QDataStream &in, DirectoryAndFilePathId &directoryAndFilePathId)
{
in >> directoryAndFilePathId.directoryPathId;
in >> directoryAndFilePathId.filePathId;
return in;
}
public:
DirectoryPathId directoryPathId;
FilePathId filePathId;
};
using DirectoryAndFilePathIds = std::vector<DirectoryAndFilePathId>;
} // namespace ClangBackEnd

View File

@@ -27,53 +27,44 @@
#include "clangsupport_global.h"
#include <filepathid.h>
#include <filepathcache.h>
#include "filepathcachinginterface.h"
#include <QDir>
#include <QTimer>
#include <filepathcachinginterface.h>
#include <utils/algorithm.h>
#include <functional>
namespace ClangBackEnd {
template <typename Timer>
class ChangedFilePathCompressor
template<typename Timer>
class DirectoryPathCompressor
{
public:
ChangedFilePathCompressor(FilePathCachingInterface &filePathCache)
: m_filePathCache(filePathCache)
DirectoryPathCompressor() { m_timer.setSingleShot(true); }
virtual ~DirectoryPathCompressor() = default;
void addDirectoryPathId(DirectoryPathId directoryPathIdId)
{
m_timer.setSingleShot(true);
}
auto found = std::lower_bound(m_directoryPathIds.begin(),
m_directoryPathIds.end(),
directoryPathIdId);
virtual ~ChangedFilePathCompressor()
{
}
void addFilePath(const QString &filePath)
{
FilePathId filePathId = m_filePathCache.filePathId(FilePath(filePath));
auto found = std::lower_bound(m_filePaths.begin(), m_filePaths.end(), filePathId);
if (found == m_filePaths.end() || *found != filePathId)
m_filePaths.insert(found, filePathId);
if (found == m_directoryPathIds.end() || *found != directoryPathIdId)
m_directoryPathIds.insert(found, directoryPathIdId);
restartTimer();
}
FilePathIds takeFilePathIds()
{
return std::move(m_filePaths);
}
DirectoryPathIds takeDirectoryPathIds() { return std::move(m_directoryPathIds); }
virtual void setCallback(std::function<void(ClangBackEnd::FilePathIds &&)> &&callback)
virtual void setCallback(std::function<void(ClangBackEnd::DirectoryPathIds &&)> &&callback)
{
QObject::connect(&m_timer,
&Timer::timeout,
[this, callback=std::move(callback)] { callback(takeFilePathIds()); });
QObject::connect(&m_timer, &Timer::timeout, [this, callback = std::move(callback)] {
callback(takeDirectoryPathIds());
});
}
unittest_public:
@@ -88,9 +79,8 @@ unittest_public:
}
private:
FilePathIds m_filePaths;
DirectoryPathIds m_directoryPathIds;
Timer m_timer;
FilePathCachingInterface &m_filePathCache;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2019 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 <QDataStream>
#include <vector>
namespace ClangBackEnd {
class DirectoryPathId
{
public:
constexpr DirectoryPathId() = default;
DirectoryPathId(const char *) = delete;
DirectoryPathId(int directoryPathId)
: directoryPathId(directoryPathId)
{}
bool isValid() const { return directoryPathId >= 0; }
friend bool operator==(DirectoryPathId first, DirectoryPathId second)
{
return first.isValid() && first.directoryPathId == second.directoryPathId;
}
friend bool operator!=(DirectoryPathId first, DirectoryPathId second)
{
return !(first == second);
}
friend bool operator<(DirectoryPathId first, DirectoryPathId second)
{
return first.directoryPathId < second.directoryPathId;
}
friend QDataStream &operator<<(QDataStream &out, const DirectoryPathId &directoryPathId)
{
out << directoryPathId.directoryPathId;
return out;
}
friend QDataStream &operator>>(QDataStream &in, DirectoryPathId &directoryPathId)
{
in >> directoryPathId.directoryPathId;
return in;
}
public:
int directoryPathId = -1;
};
using DirectoryPathIds = std::vector<DirectoryPathId>;
} // namespace ClangBackEnd

View File

@@ -25,9 +25,10 @@
#pragma once
#include "directorypathid.h"
#include "filepath.h"
#include "filepathexceptions.h"
#include "filepathid.h"
#include "filepath.h"
#include "filepathview.h"
#include "stringcache.h"
@@ -121,10 +122,10 @@ public:
{
Utils::SmallStringView directoryPath = filePath.directory();
int directoryId = m_directoryPathCache.stringId(directoryPath,
[&] (const Utils::SmallStringView) {
return m_filePathStorage.fetchDirectoryId(directoryPath);
});
int directoryId = m_directoryPathCache.stringId(
directoryPath, [&](const Utils::SmallStringView directoryPath) {
return m_filePathStorage.fetchDirectoryId(directoryPath);
});
Utils::SmallStringView fileName = filePath.name();
@@ -136,6 +137,17 @@ public:
return fileNameId;
}
DirectoryPathId directoryPathId(Utils::SmallStringView directoryPath) const
{
Utils::SmallStringView path = directoryPath.back() == '/'
? directoryPath.mid(0, directoryPath.size() - 1)
: directoryPath;
return m_directoryPathCache.stringId(path, [&](const Utils::SmallStringView directoryPath) {
return m_filePathStorage.fetchDirectoryId(directoryPath);
});
}
FilePath filePath(FilePathId filePathId) const
{
if (Q_UNLIKELY(!filePathId.isValid()))
@@ -157,6 +169,32 @@ public:
return FilePath{directoryPath, entry.fileName};
}
Utils::PathString directoryPath(DirectoryPathId directoryPathId) const
{
if (Q_UNLIKELY(!directoryPathId.isValid()))
throw NoDirectoryPathForInvalidDirectoryPathId();
auto fetchDirectoryPath = [&](int id) { return m_filePathStorage.fetchDirectoryPath(id); };
return m_directoryPathCache.string(directoryPathId.directoryPathId, fetchDirectoryPath);
}
DirectoryPathId directoryPathId(FilePathId filePathId) const
{
if (Q_UNLIKELY(!filePathId.isValid()))
throw NoFilePathForInvalidFilePathId();
auto fetchSoureNameAndDirectoryId = [&](int id) {
auto entry = m_filePathStorage.fetchSourceNameAndDirectoryId(id);
return FileNameEntry{entry.sourceName, entry.directoryId};
};
FileNameEntry entry = m_fileNameCache.string(filePathId.filePathId,
fetchSoureNameAndDirectoryId);
return m_fileNameCache.string(filePathId.filePathId, fetchSoureNameAndDirectoryId).directoryId;
}
private:
mutable DirectoryPathCache m_directoryPathCache;
mutable FileNameCache m_fileNameCache;

View File

@@ -37,4 +37,19 @@ FilePath FilePathCaching::filePath(FilePathId filePathId) const
return m_cache.filePath(filePathId);
}
DirectoryPathId FilePathCaching::directoryPathId(Utils::SmallStringView directoryPath) const
{
return m_cache.directoryPathId(directoryPath);
}
Utils::PathString FilePathCaching::directoryPath(DirectoryPathId directoryPathId) const
{
return m_cache.directoryPath(directoryPathId);
}
DirectoryPathId FilePathCaching::directoryPathId(FilePathId filePathId) const
{
return m_cache.directoryPathId(filePathId);
}
} // namespace ClangBackEnd

View File

@@ -50,6 +50,9 @@ public:
FilePathId filePathId(FilePathView filePath) const override;
FilePath filePath(FilePathId filePathId) const override;
DirectoryPathId directoryPathId(Utils::SmallStringView directoryPath) const override;
Utils::PathString directoryPath(DirectoryPathId directoryPathId) const override;
DirectoryPathId directoryPathId(FilePathId filePathId) const override;
private:
Factory m_factory;

View File

@@ -25,6 +25,7 @@
#pragma once
#include "directorypathid.h"
#include "filepath.h"
#include "filepathid.h"
#include "filepathview.h"
@@ -40,8 +41,11 @@ public:
virtual FilePathId filePathId(FilePathView filePath) const = 0;
virtual FilePath filePath(FilePathId filePathId) const = 0;
virtual DirectoryPathId directoryPathId(Utils::SmallStringView directoryPath) const = 0;
virtual DirectoryPathId directoryPathId(FilePathId filePathId) const = 0;
virtual Utils::PathString directoryPath(DirectoryPathId directoryPathId) const = 0;
template <typename Container>
template<typename Container>
FilePathIds filePathIds(Container &&filePaths) const
{
FilePathIds filePathIds;

View File

@@ -38,6 +38,15 @@ public:
}
};
class NoDirectoryPathForInvalidDirectoryPathId : std::exception
{
public:
const char *what() const noexcept override
{
return "You cannot get a directory path for an invalid directory path id!";
}
};
class SourceNameIdDoesNotExists : std::exception
{
public:

View File

@@ -179,12 +179,32 @@ public:
transaction.commit();
return optionalSourceName.value();
return *optionalSourceName;
} catch (const Sqlite::StatementIsBusy &) {
return fetchSourceNameAndDirectoryId(sourceId);
}
}
int fetchDirectoryId(int sourceId)
{
try {
Sqlite::DeferredTransaction transaction{m_statementFactory.database};
ReadStatement &statement = m_statementFactory.selectDirectoryIdFromSourcesBySourceId;
auto optionalDirectoryId = statement.template value<int>(sourceId);
if (!optionalDirectoryId)
throw SourceNameIdDoesNotExists();
transaction.commit();
return *optionalDirectoryId;
} catch (const Sqlite::StatementIsBusy &) {
return fetchDirectoryId(sourceId);
}
}
std::vector<Sources::Source> fetchAllSources()
{
try {

View File

@@ -69,6 +69,8 @@ public:
"SELECT sourceName, directoryId FROM sources WHERE sourceId = ?",
database
};
ReadStatement selectDirectoryIdFromSourcesBySourceId{
"SELECT directoryId FROM sources WHERE sourceId = ?", database};
WriteStatement insertIntoSources{
"INSERT INTO sources(directoryId, sourceName) VALUES (?,?)",
database

View File

@@ -0,0 +1,169 @@
/****************************************************************************
**
** 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 "filestatuscache.h"
#include "filesystem.h"
#include <utils/algorithm.h>
#include <QDateTime>
#include <QFileInfo>
namespace ClangBackEnd {
long long FileStatusCache::lastModifiedTime(FilePathId filePathId) const
{
return findEntry(filePathId).lastModified;
}
void FileStatusCache::update(FilePathId filePathId)
{
auto found = std::lower_bound(m_cacheEntries.begin(),
m_cacheEntries.end(),
Internal::FileStatusCacheEntry{filePathId},
[] (const auto &first, const auto &second) {
return first.filePathId < second.filePathId;
});
if (found != m_cacheEntries.end() && found->filePathId == filePathId)
found->lastModified = m_fileSystem.lastModified(filePathId);
}
namespace {
template<class InputIt1, class InputIt2, class Callable>
void set_intersection_call(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable)
{
while (first1 != last1 && first2 != last2) {
if (*first1 < *first2) {
++first1;
} else {
if (!(*first2 < *first1))
callable(*first1++);
++first2;
}
}
}
template<class InputIt1, class InputIt2, class Callable>
void set_difference_call(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable)
{
while (first1 != last1) {
if (first2 == last2) {
std::for_each(first1, last1, callable);
return;
}
if (*first1 < *first2) {
callable(*first1++);
} else {
if (!(*first2 < *first1))
++first1;
++first2;
}
}
}
} // namespace
void FileStatusCache::update(FilePathIds filePathIds)
{
set_intersection_call(m_cacheEntries.begin(),
m_cacheEntries.end(),
filePathIds.begin(),
filePathIds.end(),
[&](auto &entry) {
entry.lastModified = m_fileSystem.lastModified(entry.filePathId);
});
}
FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const
{
FilePathIds modifiedFilePathIds;
modifiedFilePathIds.reserve(filePathIds.size());
set_intersection_call(m_cacheEntries.begin(),
m_cacheEntries.end(),
filePathIds.begin(),
filePathIds.end(),
[&](auto &entry) {
auto newLastModified = m_fileSystem.lastModified(entry.filePathId);
if (newLastModified > entry.lastModified) {
modifiedFilePathIds.push_back(entry.filePathId);
entry.lastModified = newLastModified;
}
});
Internal::FileStatusCacheEntries newEntries;
newEntries.reserve(filePathIds.size());
set_difference_call(filePathIds.begin(),
filePathIds.end(),
m_cacheEntries.begin(),
m_cacheEntries.end(),
[&](FilePathId newFilePathId) {
newEntries.emplace_back(newFilePathId,
m_fileSystem.lastModified(newFilePathId));
modifiedFilePathIds.push_back(newFilePathId);
});
if (newEntries.size()) {
Internal::FileStatusCacheEntries mergedEntries;
mergedEntries.reserve(m_cacheEntries.size() + newEntries.size());
std::set_union(newEntries.begin(),
newEntries.end(),
m_cacheEntries.begin(),
m_cacheEntries.end(),
std::back_inserter(mergedEntries));
m_cacheEntries = std::move(mergedEntries);
}
std::sort(modifiedFilePathIds.begin(), modifiedFilePathIds.end());
return modifiedFilePathIds;
}
FileStatusCache::size_type FileStatusCache::size() const
{
return m_cacheEntries.size();
}
Internal::FileStatusCacheEntry FileStatusCache::findEntry(FilePathId filePathId) const
{
auto found = std::lower_bound(m_cacheEntries.begin(),
m_cacheEntries.end(),
Internal::FileStatusCacheEntry{filePathId},
[] (const auto &first, const auto &second) {
return first.filePathId < second.filePathId;
});
if (found != m_cacheEntries.end() && found->filePathId == filePathId)
return *found;
auto inserted = m_cacheEntries.emplace(found, filePathId, m_fileSystem.lastModified(filePathId));
return *inserted;
}
} // namespace ClangBackEnd

View File

@@ -31,6 +31,8 @@ QT_FORWARD_DECLARE_CLASS(QFileInfo)
namespace ClangBackEnd {
class FileSystemInterface;
namespace Internal {
class FileStatusCacheEntry
{
@@ -41,8 +43,23 @@ public:
lastModified(lastModified)
{}
friend bool operator<(FileStatusCacheEntry first, FileStatusCacheEntry second)
{
return first.filePathId < second.filePathId;
}
friend bool operator<(FileStatusCacheEntry first, FilePathId second)
{
return first.filePathId < second;
}
friend bool operator<(FilePathId first, FileStatusCacheEntry second)
{
return first < second.filePathId;
}
public:
ClangBackEnd::FilePathId filePathId;
FilePathId filePathId;
long long lastModified;
};
@@ -50,27 +67,30 @@ using FileStatusCacheEntries = std::vector<FileStatusCacheEntry>;
}
class FileStatusCache
class CLANGSUPPORT_EXPORT FileStatusCache
{
public:
using size_type = Internal::FileStatusCacheEntries::size_type;
FileStatusCache(FilePathCachingInterface &filePathCache);
FileStatusCache(FileSystemInterface &fileSystem)
: m_fileSystem(fileSystem)
{}
FileStatusCache &operator=(const FileStatusCache &) = delete;
FileStatusCache(const FileStatusCache &) = delete;
long long lastModifiedTime(ClangBackEnd::FilePathId filePathId) const;
void update(ClangBackEnd::FilePathId filePathId);
long long lastModifiedTime(FilePathId filePathId) const;
void update(FilePathId filePathId);
void update(FilePathIds filePathIds);
FilePathIds modified(FilePathIds filePathIds) const;
size_type size() const;
private:
Internal::FileStatusCacheEntry findEntry(ClangBackEnd::FilePathId filePathId) const;
QFileInfo qFileInfo(ClangBackEnd::FilePathId filePathId) const;
Internal::FileStatusCacheEntry findEntry(FilePathId filePathId) const;
private:
mutable Internal::FileStatusCacheEntries m_cacheEntries;
FilePathCachingInterface &m_filePathCache;
FileSystemInterface &m_fileSystem;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "filesystem.h"
#include "filepathcachinginterface.h"
#include <utils/algorithm.h>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
namespace ClangBackEnd {
FilePathIds FileSystem::directoryEntries(const QString &directoryPath) const
{
QDir directory{directoryPath};
QFileInfoList fileInfos = directory.entryInfoList();
FilePathIds filePathIds = Utils::transform<FilePathIds>(fileInfos, [&](const QFileInfo &fileInfo) {
return m_filePathCache.filePathId(FilePath{fileInfo.path()});
});
std::sort(filePathIds.begin(), filePathIds.end());
return filePathIds;
}
long long FileSystem::lastModified(FilePathId filePathId) const
{
QFileInfo fileInfo(QString(m_filePathCache.filePath(filePathId)));
fileInfo.refresh();
return fileInfo.lastModified().toMSecsSinceEpoch() / 1000;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "filestatuscache.h"
#include "filesysteminterface.h"
namespace ClangBackEnd {
class FilePathCachingInterface;
class CLANGSUPPORT_EXPORT FileSystem final : public FileSystemInterface
{
public:
FileSystem(FilePathCachingInterface &filePathCache)
: m_filePathCache(filePathCache)
{}
FilePathIds directoryEntries(const QString &directoryPath) const override;
long long lastModified(FilePathId filePathId) const override;
private:
FilePathCachingInterface &m_filePathCache;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "filepathid.h"
#include <utils/smallstringview.h>
namespace ClangBackEnd {
class FileSystemInterface
{
public:
virtual FilePathIds directoryEntries(const QString &directoryPath) const = 0;
virtual long long lastModified(FilePathId filePathId) const = 0;
protected:
~FileSystemInterface() = default;
};
} // namespace ClangBackEnd

View File

@@ -179,6 +179,10 @@ public:
return m_pointer[0] == characterToSearch;
}
char back() const { return m_pointer[m_size - 1]; }
char operator[](std::size_t index) { return m_pointer[index]; }
private:
const char *m_pointer = "";
size_type m_size = 0;

View File

@@ -31,6 +31,7 @@
#include <environment.h>
#include <executeinloop.h>
#include <filepathcaching.h>
#include <filesystem.h>
#include <generatedfiles.h>
#include <modifiedtimechecker.h>
#include <pchcreator.h>
@@ -179,7 +180,8 @@ struct Data // because we have a cycle dependency
Sqlite::Database database;
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database};
ClangBackEnd::FilePathCaching filePathCache{database};
ClangPathWatcher<QFileSystemWatcher, QTimer> includeWatcher{filePathCache};
ClangBackEnd::FileSystem fileSystem{filePathCache};
ClangPathWatcher<QFileSystemWatcher, QTimer> includeWatcher{filePathCache, fileSystem};
ApplicationEnvironment environment;
ProjectPartsStorage<> projectPartsStorage{database};
PrecompiledHeaderStorage<> preCompiledHeaderStorage{database};

View File

@@ -17,8 +17,6 @@ HEADERS += \
$$PWD/symbolsvisitorbase.h \
$$PWD/usedmacro.h \
$$PWD/sourcedependency.h \
$$PWD/filestatus.h \
$$PWD/filestatuscache.h \
$$PWD/indexdataconsumer.h \
$$PWD/sourcesmanager.h \
$$PWD/symbolindexertaskqueue.h \
@@ -67,5 +65,4 @@ HEADERS += \
SOURCES += \
$$PWD/filestatuspreprocessorcallbacks.cpp \
$$PWD/sourcerangefilter.cpp \
$$PWD/symbolindexer.cpp \
$$PWD/filestatuscache.cpp
$$PWD/symbolindexer.cpp

View File

@@ -1,93 +0,0 @@
/****************************************************************************
**
** 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 "filestatuscache.h"
#include <QDateTime>
#include <QFileInfo>
namespace ClangBackEnd {
FileStatusCache::FileStatusCache(FilePathCachingInterface &filePathCache)
: m_filePathCache(filePathCache)
{
}
long long FileStatusCache::lastModifiedTime(FilePathId filePathId) const
{
return findEntry(filePathId).lastModified;
}
void FileStatusCache::update(FilePathId filePathId)
{
auto found = std::lower_bound(m_cacheEntries.begin(),
m_cacheEntries.end(),
Internal::FileStatusCacheEntry{filePathId},
[] (const auto &first, const auto &second) {
return first.filePathId < second.filePathId;
});
if (found != m_cacheEntries.end() && found->filePathId == filePathId) {
QFileInfo fileInfo = qFileInfo(filePathId);
found->lastModified = fileInfo.lastModified().toMSecsSinceEpoch() / 1000;
}
}
FileStatusCache::size_type FileStatusCache::size() const
{
return m_cacheEntries.size();
}
Internal::FileStatusCacheEntry FileStatusCache::findEntry(FilePathId filePathId) const
{
auto found = std::lower_bound(m_cacheEntries.begin(),
m_cacheEntries.end(),
Internal::FileStatusCacheEntry{filePathId},
[] (const auto &first, const auto &second) {
return first.filePathId < second.filePathId;
});
if (found != m_cacheEntries.end() && found->filePathId == filePathId)
return *found;
QFileInfo fileInfo = qFileInfo(filePathId);
auto inserted = m_cacheEntries.emplace(found,
filePathId,
fileInfo.lastModified().toMSecsSinceEpoch() / 1000);
return *inserted;
}
QFileInfo FileStatusCache::qFileInfo(FilePathId filePathId) const
{
QFileInfo fileInfo(QString(m_filePathCache.filePath(filePathId)));
fileInfo.refresh();
return fileInfo;
}
} // namespace ClangBackEnd

View File

@@ -39,6 +39,7 @@
#include <projectpartsstorage.h>
#include <filepathcachingfwd.h>
#include <filesystem.h>
#include <modifiedtimechecker.h>
#include <refactoringdatabaseinitializer.h>
@@ -142,8 +143,9 @@ private:
PrecompiledHeaderStorage<Sqlite::Database> m_precompiledHeaderStorage;
ProjectPartsStorage<Sqlite::Database> m_projectPartsStorage;
SymbolStorage m_symbolStorage;
ClangPathWatcher<QFileSystemWatcher, QTimer> m_sourceWatcher{m_filePathCache};
FileStatusCache m_fileStatusCache{m_filePathCache};
FileSystem m_fileSytem{m_filePathCache};
ClangPathWatcher<QFileSystemWatcher, QTimer> m_sourceWatcher{m_filePathCache, m_fileSytem};
FileStatusCache m_fileStatusCache{m_fileSytem};
SymbolsCollectorManager m_collectorManger;
ProgressCounter m_progressCounter;
std::function<TimeStamp(FilePathView filePath)> getModifiedTime{

View File

@@ -1,112 +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 "googletest.h"
#include "mocktimer.h"
#include <changedfilepathcompressor.h>
#include <filepathcaching.h>
#include <refactoringdatabaseinitializer.h>
namespace {
using testing::ElementsAre;
using testing::Invoke;
using testing::IsEmpty;
using testing::NiceMock;
using ClangBackEnd::FilePath;
using ClangBackEnd::FilePathId;
class ChangedFilePathCompressor : public testing::Test
{
protected:
void SetUp()
{
compressor.setCallback(mockCompressorCallback.AsStdFunction());
}
FilePathId filePathId(const QString &filePath)
{
Utils::SmallString utf8FilePath{filePath};
return filePathCache.filePathId(ClangBackEnd::FilePathView{utf8FilePath});
}
protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> initializer{database};
ClangBackEnd::FilePathCaching filePathCache{database};
NiceMock<MockFunction<void (const ClangBackEnd::FilePathIds &filePathIds)>> mockCompressorCallback;
ClangBackEnd::ChangedFilePathCompressor<NiceMock<MockTimer>> compressor{filePathCache};
NiceMock<MockTimer> &mockTimer = compressor.timer();
QString filePath1{"filePath1"};
QString filePath2{"filePath2"};
FilePathId filePathId1 = filePathId(filePath1);
FilePathId filePathId2 = filePathId(filePath2);
};
TEST_F(ChangedFilePathCompressor, AddFilePath)
{
compressor.addFilePath(filePath1);
ASSERT_THAT(compressor.takeFilePathIds(), ElementsAre(filePathId(filePath1)));
}
TEST_F(ChangedFilePathCompressor, NoFilePathsAferTakenThem)
{
compressor.addFilePath(filePath1);
compressor.takeFilePathIds();
ASSERT_THAT(compressor.takeFilePathIds(), IsEmpty());
}
TEST_F(ChangedFilePathCompressor, CallRestartTimerAfterAddingPath)
{
EXPECT_CALL(mockTimer, start(20));
compressor.addFilePath(filePath1);
}
TEST_F(ChangedFilePathCompressor, CallTimeOutAfterAddingPath)
{
EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(filePathId1, filePathId2)));
compressor.addFilePath(filePath1);
compressor.addFilePath(filePath2);
}
TEST_F(ChangedFilePathCompressor, RemoveDuplicates)
{
EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(filePathId1, filePathId2)));
compressor.addFilePath(filePath1);
compressor.addFilePath(filePath2);
compressor.addFilePath(filePath1);
}
}

View File

@@ -25,10 +25,11 @@
#include "googletest.h"
#include "mocktimer.h"
#include "mockfilepathcaching.h"
#include "mockqfilesystemwatcher.h"
#include "mockclangpathwatchernotifier.h"
#include "mockfilepathcaching.h"
#include "mockfilesystem.h"
#include "mockqfilesystemwatcher.h"
#include "mocktimer.h"
#include <clangpathwatcher.h>
@@ -55,91 +56,126 @@ using ClangBackEnd::WatcherEntry;
class ClangPathWatcher : public testing::Test
{
protected:
void SetUp();
void SetUp()
{
ON_CALL(mockFilePathCache, filePathId(Eq(path1))).WillByDefault(Return(pathIds[0]));
ON_CALL(mockFilePathCache, filePathId(Eq(path2))).WillByDefault(Return(pathIds[1]));
ON_CALL(mockFilePathCache, filePathId(Eq(path3))).WillByDefault(Return(pathIds[2]));
ON_CALL(mockFilePathCache, filePathId(Eq(path4))).WillByDefault(Return(pathIds[3]));
ON_CALL(mockFilePathCache, filePathId(Eq(path5))).WillByDefault(Return(pathIds[4]));
ON_CALL(mockFilePathCache, filePath(Eq(pathIds[0]))).WillByDefault(Return(FilePath{path1}));
ON_CALL(mockFilePathCache, filePath(Eq(pathIds[1]))).WillByDefault(Return(FilePath{path2}));
ON_CALL(mockFilePathCache, filePath(Eq(pathIds[2]))).WillByDefault(Return(FilePath{path3}));
ON_CALL(mockFilePathCache, filePath(Eq(pathIds[3]))).WillByDefault(Return(FilePath{path4}));
ON_CALL(mockFilePathCache, filePath(Eq(pathIds[4]))).WillByDefault(Return(FilePath{path5}));
ON_CALL(mockFilePathCache, directoryPathId(TypedEq<FilePathId>(pathIds[0])))
.WillByDefault(Return(directoryPaths[0]));
ON_CALL(mockFilePathCache, directoryPathId(TypedEq<FilePathId>(pathIds[1])))
.WillByDefault(Return(directoryPaths[0]));
ON_CALL(mockFilePathCache, directoryPathId(TypedEq<FilePathId>(pathIds[2])))
.WillByDefault(Return(directoryPaths[1]));
ON_CALL(mockFilePathCache, directoryPathId(TypedEq<FilePathId>(pathIds[3])))
.WillByDefault(Return(directoryPaths[1]));
ON_CALL(mockFilePathCache, directoryPathId(TypedEq<FilePathId>(pathIds[4])))
.WillByDefault(Return(directoryPaths[2]));
ON_CALL(mockFileSystem, lastModified(_)).WillByDefault(Return(1));
ON_CALL(mockFilePathCache,
directoryPathId(TypedEq<Utils::SmallStringView>(directoryPathString)))
.WillByDefault(Return(directoryPaths[0]));
ON_CALL(mockFilePathCache,
directoryPathId(TypedEq<Utils::SmallStringView>(directoryPathString2)))
.WillByDefault(Return(directoryPaths[1]));
ON_CALL(mockFilePathCache, directoryPath(Eq(directoryPaths[0])))
.WillByDefault(Return(directoryPath));
ON_CALL(mockFilePathCache, directoryPath(Eq(directoryPaths[1])))
.WillByDefault(Return(directoryPath2));
ON_CALL(mockFilePathCache, directoryPath(Eq(directoryPaths[2])))
.WillByDefault(Return(directoryPath3));
ON_CALL(mockFileSystem, directoryEntries(Eq(directoryPath)))
.WillByDefault(Return(FilePathIds{pathIds[0], pathIds[1]}));
ON_CALL(mockFileSystem, directoryEntries(Eq(directoryPath2)))
.WillByDefault(Return(FilePathIds{pathIds[2], pathIds[3]}));
ON_CALL(mockFileSystem, directoryEntries(Eq(directoryPath3)))
.WillByDefault(Return(FilePathIds{pathIds[4]}));
}
static WatcherEntries sorted(WatcherEntries &&entries)
{
std::stable_sort(entries.begin(), entries.end());
return entries;
return std::move(entries);
}
protected:
NiceMock<MockFilePathCaching> filePathCache;
NiceMock<MockFilePathCaching> mockFilePathCache;
NiceMock<MockClangPathWatcherNotifier> notifier;
Watcher watcher{filePathCache, &notifier};
NiceMock<MockFileSystem> mockFileSystem;
Watcher watcher{mockFilePathCache, mockFileSystem, &notifier};
NiceMock<MockQFileSytemWatcher> &mockQFileSytemWatcher = watcher.fileSystemWatcher();
ProjectPartId id1{2};
ProjectPartId id2{3};
ProjectPartId id3{4};
FilePathView path1{"/path/path1"};
FilePathView path2{"/path/path2"};
FilePathView path3{"/path2/path1"};
FilePathView path4{"/path2/path2"};
FilePathView path5{"/path3/path"};
QString path1QString = QString(path1.toStringView());
QString path2QString = QString(path2.toStringView());
FilePathIds pathIds = {1, 2};
QString directoryPath = "/path";
QString directoryPath2 = "/path2";
QString directoryPath3 = "/path3";
Utils::PathString directoryPathString = directoryPath;
Utils::PathString directoryPathString2 = directoryPath2;
FilePathIds pathIds = {1, 2, 3, 4, 5};
ClangBackEnd::DirectoryPathIds directoryPaths = {1, 2, 3};
ClangBackEnd::ProjectPartIds ids{id1, id2, id3};
WatcherEntry watcherEntry1{ids[0], pathIds[0]};
WatcherEntry watcherEntry2{ids[1], pathIds[0]};
WatcherEntry watcherEntry3{ids[0], pathIds[1]};
WatcherEntry watcherEntry4{ids[1], pathIds[1]};
WatcherEntry watcherEntry5{ids[2], pathIds[1]};
WatcherEntry watcherEntry1{ids[0], directoryPaths[0], pathIds[0]};
WatcherEntry watcherEntry2{ids[1], directoryPaths[0], pathIds[0]};
WatcherEntry watcherEntry3{ids[0], directoryPaths[0], pathIds[1]};
WatcherEntry watcherEntry4{ids[1], directoryPaths[0], pathIds[1]};
WatcherEntry watcherEntry5{ids[2], directoryPaths[0], pathIds[1]};
WatcherEntry watcherEntry6{ids[0], directoryPaths[1], pathIds[2]};
WatcherEntry watcherEntry7{ids[1], directoryPaths[1], pathIds[3]};
};
TEST_F(ClangPathWatcher, ConvertWatcherEntriesToQStringList)
{
auto convertedList = watcher.convertWatcherEntriesToQStringList(sorted({watcherEntry1, watcherEntry3}));
ASSERT_THAT(convertedList, ElementsAre(path1QString, path2QString));
}
TEST_F(ClangPathWatcher, UniquePaths)
{
auto uniqueEntries = watcher.uniquePaths(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4}));
ASSERT_THAT(uniqueEntries, ElementsAre(watcherEntry1, watcherEntry3));
}
TEST_F(ClangPathWatcher, NotWatchedEntries)
{
watcher.addEntries({watcherEntry1, watcherEntry4});
auto newEntries = watcher.notWatchedEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4}));
ASSERT_THAT(newEntries, ElementsAre(watcherEntry2, watcherEntry3));
}
TEST_F(ClangPathWatcher, AddIdPaths)
{
EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString, path2QString}));
EXPECT_CALL(mockQFileSytemWatcher,
addPaths(UnorderedElementsAre(QString(directoryPath), QString(directoryPath2))));
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}});
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
}
TEST_F(ClangPathWatcher, UpdateIdPathsCallsAddPathInFileWatcher)
{
watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[0]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path2QString}));
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(UnorderedElementsAre(QString(directoryPath2))));
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
}
TEST_F(ClangPathWatcher, UpdateIdPathsAndRemoveUnusedPathsCallsRemovePathInFileWatcher)
{
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[0]}}});
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path2QString}));
EXPECT_CALL(mockQFileSytemWatcher, removePaths(UnorderedElementsAre(QString(directoryPath2))));
watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[0]}}});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}});
}
TEST_F(ClangPathWatcher, UpdateIdPathsAndRemoveUnusedPathsDoNotCallsRemovePathInFileWatcher)
{
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[0]}}});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2]}},
{id2, {pathIds[0], pathIds[1], pathIds[3]}},
{id3, {pathIds[0]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path2QString}))
.Times(0);
EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)).Times(0);
watcher.updateIdPaths({{id1, {pathIds[1]}}, {id2, {pathIds[0]}}});
watcher.updateIdPaths({{id1, {pathIds[1]}}, {id2, {pathIds[3]}}});
}
TEST_F(ClangPathWatcher, UpdateIdPathsAndRemoveUnusedPaths)
@@ -165,36 +201,18 @@ TEST_F(ClangPathWatcher, ExtractSortedIdsFromConvertIdPaths)
ASSERT_THAT(entriesAndIds.second, ElementsAre(ids[0], ids[1], ids[2]));
}
TEST_F(ClangPathWatcher, NotWatchedPaths)
{
watcher.mergeToWatchedEntries(sorted({watcherEntry1}));
auto newEntries = watcher.notWatchedPaths({watcherEntry2, watcherEntry3});
ASSERT_THAT(newEntries, ElementsAre(watcherEntry3));
}
TEST_F(ClangPathWatcher, AddedPaths)
{
watcher.mergeToWatchedEntries(sorted({watcherEntry1, watcherEntry2}));
auto filteredEntries = watcher.filterNotWatchedPaths({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4});
ASSERT_THAT(filteredEntries, ElementsAre(watcherEntry3));
}
TEST_F(ClangPathWatcher, MergeEntries)
{
watcher.mergeToWatchedEntries(sorted({watcherEntry1, watcherEntry4}));
watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[1]}}});
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry4));
}
TEST_F(ClangPathWatcher, MergeMoreEntries)
{
watcher.mergeToWatchedEntries(sorted({watcherEntry1, watcherEntry4}));
watcher.updateIdPaths({{id2, {pathIds[0], pathIds[1]}}});
watcher.mergeToWatchedEntries(sorted({watcherEntry2, watcherEntry3}));
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}});
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4));
}
@@ -204,48 +222,48 @@ TEST_F(ClangPathWatcher, AddEmptyEntries)
EXPECT_CALL(mockQFileSytemWatcher, addPaths(_))
.Times(0);
watcher.addEntries({});
watcher.updateIdPaths({});
}
TEST_F(ClangPathWatcher, AddEntriesWithSameIdAndDifferentPaths)
{
EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString, path2QString}));
EXPECT_CALL(mockQFileSytemWatcher,
addPaths(ElementsAre(directoryPath, directoryPath2, directoryPath3)));
watcher.addEntries({watcherEntry1, watcherEntry3});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[4]}}});
}
TEST_F(ClangPathWatcher, AddEntriesWithDifferentIdAndSamePaths)
{
EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString}));
EXPECT_CALL(mockQFileSytemWatcher, addPaths(ElementsAre(directoryPath)));
watcher.addEntries({watcherEntry1, watcherEntry2});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}});
}
TEST_F(ClangPathWatcher, DontAddNewEntriesWithSameIdAndSamePaths)
{
watcher.addEntries({watcherEntry1});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString}))
.Times(0);
EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)).Times(0);
watcher.addEntries({watcherEntry1});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
}
TEST_F(ClangPathWatcher, DontAddNewEntriesWithDifferentIdAndSamePaths)
{
watcher.addEntries({watcherEntry1});
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString}))
.Times(0);
EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)).Times(0);
watcher.addEntries({watcherEntry2});
watcher.updateIdPaths({{id2, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}});
}
TEST_F(ClangPathWatcher, RemoveEntriesWithId)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}));
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[1]}}});
watcher.removeIdsFromWatchedEntries({ids[0]});
watcher.removeIds({id1});
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry2, watcherEntry4, watcherEntry5));
}
@@ -260,37 +278,52 @@ TEST_F(ClangPathWatcher, RemoveNoPathsForEmptyIds)
TEST_F(ClangPathWatcher, RemoveNoPathsForOneId)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4}));
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(_))
.Times(0);
watcher.removeIds({id1});
watcher.removeIds({id3});
}
TEST_F(ClangPathWatcher, RemovePathForOneId)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3}));
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path2QString}));
EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(directoryPath2)));
watcher.removeIds({id1});
watcher.removeIds({id2});
}
TEST_F(ClangPathWatcher, RemoveNoPathSecondTime)
{
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
watcher.removeIds({id2});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)).Times(0);
watcher.removeIds({id2});
}
TEST_F(ClangPathWatcher, RemoveAllPathsForThreeId)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}));
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path1QString, path2QString}));
EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(directoryPath, directoryPath2)));
watcher.removeIds({id1, id2, id3});
}
TEST_F(ClangPathWatcher, RemoveOnePathForTwoId)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}));
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[3]}}});
EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path1QString}));
EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(directoryPath)));
watcher.removeIds({id1, id2});
}
@@ -313,71 +346,51 @@ TEST_F(ClangPathWatcher, RemoveUnusedEntries)
ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry4, watcherEntry5));
}
TEST_F(ClangPathWatcher, EmptyVectorNotifyFileChange)
TEST_F(ClangPathWatcher, TwoNotifyFileChanges)
{
watcher.addEntries({watcherEntry3});
EXPECT_CALL(notifier, pathsWithIdsChanged(IsEmpty()));
mockQFileSytemWatcher.fileChanged(path1QString);
}
TEST_F(ClangPathWatcher, NotifyFileChange)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}));
watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2]}},
{id2, {pathIds[0], pathIds[1], pathIds[3]}},
{id3, {pathIds[4]}}});
ON_CALL(mockFileSystem, lastModified(Eq(pathIds[0]))).WillByDefault(Return(2));
ON_CALL(mockFileSystem, lastModified(Eq(pathIds[1]))).WillByDefault(Return(2));
ON_CALL(mockFileSystem, lastModified(Eq(pathIds[3]))).WillByDefault(Return(2));
EXPECT_CALL(notifier, pathsWithIdsChanged(ElementsAre(id1, id2)));
mockQFileSytemWatcher.fileChanged(path1QString);
}
TEST_F(ClangPathWatcher, TwoNotifyFileChanges)
{
watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5}));
EXPECT_CALL(notifier, pathsWithIdsChanged(ElementsAre(id1, id2, id3)));
mockQFileSytemWatcher.fileChanged(path2QString);
mockQFileSytemWatcher.fileChanged(path1QString);
mockQFileSytemWatcher.directoryChanged(directoryPath);
mockQFileSytemWatcher.directoryChanged(directoryPath2);
}
TEST_F(ClangPathWatcher, NotifyForPathChanges)
{
watcher.addEntries({watcherEntry1});
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
ON_CALL(mockFileSystem, lastModified(Eq(pathIds[0]))).WillByDefault(Return(2));
ON_CALL(mockFileSystem, lastModified(Eq(pathIds[3]))).WillByDefault(Return(2));
EXPECT_CALL(notifier, pathsChanged(ElementsAre(pathIds[0])));
mockQFileSytemWatcher.fileChanged(path1QString);
mockQFileSytemWatcher.directoryChanged(directoryPath);
}
TEST_F(ClangPathWatcher, NoNotifyForUnwatchedPathChanges)
{
watcher.addEntries({watcherEntry3});
watcher.updateIdPaths({{id1, {pathIds[3]}}, {id2, {pathIds[3]}}});
EXPECT_CALL(notifier, pathsChanged(IsEmpty()));
mockQFileSytemWatcher.fileChanged(path1QString);
mockQFileSytemWatcher.directoryChanged(directoryPath);
}
TEST_F(ClangPathWatcher, NoDuplicatePathChanges)
{
watcher.addEntries({watcherEntry1});
watcher.updateIdPaths(
{{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}});
ON_CALL(mockFileSystem, lastModified(Eq(pathIds[0]))).WillByDefault(Return(2));
EXPECT_CALL(notifier, pathsChanged(ElementsAre(pathIds[0])));
mockQFileSytemWatcher.fileChanged(path1QString);
mockQFileSytemWatcher.fileChanged(path1QString);
}
void ClangPathWatcher::SetUp()
{
ON_CALL(filePathCache, filePathId(Eq(path1)))
.WillByDefault(Return(pathIds[0]));
ON_CALL(filePathCache, filePathId(Eq(path2)))
.WillByDefault(Return(pathIds[1]));
ON_CALL(filePathCache, filePath(pathIds[0]))
.WillByDefault(Return(FilePath{path1}));
ON_CALL(filePathCache, filePath(Eq(pathIds[1])))
.WillByDefault(Return(FilePath{path2}));
}
mockQFileSytemWatcher.directoryChanged(directoryPath);
mockQFileSytemWatcher.directoryChanged(directoryPath);
}
} // namespace

View File

@@ -0,0 +1,99 @@
/****************************************************************************
**
** 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 "googletest.h"
#include "mockfilesystem.h"
#include "mocktimer.h"
#include <directorypathcompressor.h>
namespace {
using testing::ElementsAre;
using testing::Invoke;
using testing::IsEmpty;
using testing::NiceMock;
using ClangBackEnd::DirectoryPathId;
using ClangBackEnd::DirectoryPathIds;
class DirectoryPathCompressor : public testing::Test
{
protected:
void SetUp()
{
compressor.setCallback(mockCompressorCallback.AsStdFunction());
}
protected:
NiceMock<MockFunction<void(const DirectoryPathIds &directoryPathIds)>> mockCompressorCallback;
ClangBackEnd::DirectoryPathCompressor<NiceMock<MockTimer>> compressor;
NiceMock<MockTimer> &mockTimer = compressor.timer();
DirectoryPathId directoryPathId1{1};
DirectoryPathId directoryPathId2{2};
};
TEST_F(DirectoryPathCompressor, AddFilePath)
{
compressor.addDirectoryPathId(directoryPathId1);
ASSERT_THAT(compressor.takeDirectoryPathIds(), ElementsAre(directoryPathId1));
}
TEST_F(DirectoryPathCompressor, NoFilePathsAferTakenThem)
{
compressor.addDirectoryPathId(directoryPathId1);
compressor.takeDirectoryPathIds();
ASSERT_THAT(compressor.takeDirectoryPathIds(), IsEmpty());
}
TEST_F(DirectoryPathCompressor, CallRestartTimerAfterAddingPath)
{
EXPECT_CALL(mockTimer, start(20));
compressor.addDirectoryPathId(directoryPathId1);
}
TEST_F(DirectoryPathCompressor, CallTimeOutAfterAddingPath)
{
EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(directoryPathId1, directoryPathId2)));
compressor.addDirectoryPathId(directoryPathId1);
compressor.addDirectoryPathId(directoryPathId2);
}
TEST_F(DirectoryPathCompressor, RemoveDuplicates)
{
EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(directoryPathId1, directoryPathId2)));
compressor.addDirectoryPathId(directoryPathId1);
compressor.addDirectoryPathId(directoryPathId2);
compressor.addDirectoryPathId(directoryPathId1);
}
}

View File

@@ -31,6 +31,7 @@
namespace {
using ClangBackEnd::DirectoryPathId;
using ClangBackEnd::FilePathId;
using Cache = ClangBackEnd::FilePathCache<NiceMock<MockFilePathStorage>>;
using ClangBackEnd::FilePathId;
@@ -166,4 +167,153 @@ TEST_F(FilePathCache, DuplicateFilePathsAreEqual)
ASSERT_THAT(filePath2Id, Eq(filePath1Id));
}
TEST_F(FilePathCache, DirectoryPathIdCallsFetchDirectoryId)
{
EXPECT_CALL(mockStorage, fetchDirectoryId(Eq("/path/to")));
cache.directoryPathId(Utils::SmallString("/path/to"));
}
TEST_F(FilePathCache, SecondDirectoryPathIdCallsNotFetchDirectoryId)
{
cache.directoryPathId(Utils::SmallString("/path/to"));
EXPECT_CALL(mockStorage, fetchDirectoryId(Eq("/path/to"))).Times(0);
cache.directoryPathId(Utils::SmallString("/path/to"));
}
TEST_F(FilePathCache, DirectoryPathIdWithTrailingSlash)
{
EXPECT_CALL(mockStorage, fetchDirectoryId(Eq("/path/to")));
cache.directoryPathId(Utils::SmallString("/path/to/"));
}
TEST_F(FilePathCache, DirectoryPathId)
{
auto id = cache.directoryPathId(Utils::SmallString("/path/to"));
ASSERT_THAT(id, Eq(DirectoryPathId{5}));
}
TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCache)
{
auto firstId = cache.directoryPathId(Utils::SmallString("/path/to"));
auto secondId = cache.directoryPathId(Utils::SmallString("/path/to"));
ASSERT_THAT(secondId, firstId);
}
TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCacheWithTrailingSlash)
{
auto firstId = cache.directoryPathId(Utils::SmallString("/path/to/"));
auto secondId = cache.directoryPathId(Utils::SmallString("/path/to/"));
ASSERT_THAT(secondId, firstId);
}
TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCacheWithAndWithoutTrailingSlash)
{
auto firstId = cache.directoryPathId(Utils::SmallString("/path/to/"));
auto secondId = cache.directoryPathId(Utils::SmallString("/path/to"));
ASSERT_THAT(secondId, firstId);
}
TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCacheWithoutAndWithTrailingSlash)
{
auto firstId = cache.directoryPathId(Utils::SmallString("/path/to"));
auto secondId = cache.directoryPathId(Utils::SmallString("/path/to/"));
ASSERT_THAT(secondId, firstId);
}
TEST_F(FilePathCache, ThrowForGettingADirectoryPathWithAnInvalidId)
{
DirectoryPathId directoryPathId;
ASSERT_THROW(cache.directoryPath(directoryPathId),
ClangBackEnd::NoDirectoryPathForInvalidDirectoryPathId);
}
TEST_F(FilePathCache, GetADirectoryPath)
{
DirectoryPathId directoryPathId{5};
auto directoryPath = cache.directoryPath(directoryPathId);
ASSERT_THAT(directoryPath, Eq(Utils::SmallStringView{"/path/to"}));
}
TEST_F(FilePathCache, GetADirectoryPathWithCachedDirectoryPathId)
{
DirectoryPathId directoryPathId{5};
cache.directoryPath(directoryPathId);
auto directoryPath = cache.directoryPath(directoryPathId);
ASSERT_THAT(directoryPath, Eq(Utils::SmallStringView{"/path/to"}));
}
TEST_F(FilePathCache, DirectoryPathCallsFetchDirectoryPath)
{
EXPECT_CALL(mockStorage, fetchDirectoryPath(Eq(DirectoryPathId{5})));
cache.directoryPath(5);
}
TEST_F(FilePathCache, SecondDirectoryPathCallsNotFetchDirectoryPath)
{
cache.directoryPath(5);
EXPECT_CALL(mockStorage, fetchDirectoryPath(_)).Times(0);
cache.directoryPath(5);
}
TEST_F(FilePathCache, ThrowForGettingADirectoryPathIdWithAnInvalidFilePathId)
{
FilePathId filePathId;
ASSERT_THROW(cache.directoryPathId(filePathId), ClangBackEnd::NoFilePathForInvalidFilePathId);
}
TEST_F(FilePathCache, FetchDirectoryPathIdByFilePathId)
{
auto directoryId = cache.directoryPathId(42);
ASSERT_THAT(directoryId, Eq(5));
}
TEST_F(FilePathCache, FetchDirectoryPathIdByFilePathIdCached)
{
cache.directoryPathId(42);
auto directoryId = cache.directoryPathId(42);
ASSERT_THAT(directoryId, Eq(5));
}
TEST_F(FilePathCache, FetchFilePathAfterFetchingDirectoryIdByFilePathId)
{
cache.directoryPathId(42);
auto filePath = cache.filePath(42);
ASSERT_THAT(filePath, Eq("/path/to/file.cpp"));
}
TEST_F(FilePathCache, FetchDirectoryPathIdAfterFetchingFilePathByFilePathId)
{
cache.filePath(42);
auto directoryId = cache.directoryPathId(42);
ASSERT_THAT(directoryId, Eq(5));
}
} // namespace

View File

@@ -46,8 +46,8 @@ protected:
void SetUp()
{
ON_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(_))
.WillByDefault(Return(Utils::optional<int>()));
valueReturnInt32(A<Utils::SmallStringView>()))
.WillByDefault(Return(Utils::optional<int>()));
ON_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Utils::SmallStringView("")))
.WillByDefault(Return(Utils::optional<int>(0)));
@@ -77,9 +77,12 @@ protected:
ON_CALL(selectSourceNameAndDirectoryIdFromSourcesBySourceId,
valueReturnSourceNameAndDirectoryId(42))
.WillByDefault(Return(Utils::optional<ClangBackEnd::Sources::SourceNameAndDirectoryId>({"file.cpp", 5})));
ON_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq<int>(42)))
.WillByDefault(Return(Utils::optional<int>(5)));
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, valueReturnInt32(_))
.Times(AnyNumber());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(A<Utils::SmallStringView>()))
.Times(AnyNumber());
EXPECT_CALL(selectSourceIdFromSourcesByDirectoryIdAndSourceName, valueReturnInt32(_, _))
.Times(AnyNumber());
EXPECT_CALL(insertIntoDirectories, write(An<Utils::SmallStringView>()))
@@ -107,6 +110,7 @@ protected:
MockSqliteWriteStatement &insertIntoDirectories = factory.insertIntoDirectories;
MockSqliteWriteStatement &insertIntoSources = factory.insertIntoSources;
MockSqliteReadStatement &selectAllSources = factory.selectAllSources;
MockSqliteReadStatement &selectDirectoryIdFromSourcesBySourceId = factory.selectDirectoryIdFromSourcesBySourceId;
Storage storage{factory};
};
@@ -229,7 +233,7 @@ TEST_F(FilePathStorage, CallSelectForFetchingDirectoryIdForKnownPath)
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/path/to")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/path/to")));
EXPECT_CALL(mockDatabase, commit());
storage.fetchDirectoryId("/path/to");
@@ -267,7 +271,7 @@ TEST_F(FilePathStorage, CallSelectAndWriteForFetchingDirectoryIdForUnknownPath)
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/some/not/known/path")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/some/not/known/path")));
EXPECT_CALL(insertIntoDirectories, write(TypedEq<Utils::SmallStringView>("/some/not/known/path")));
EXPECT_CALL(mockDatabase, commit());
@@ -296,7 +300,7 @@ TEST_F(FilePathStorage, RestartFetchDirectoryIDIfTheStatementIsBusyInBeginBecaus
EXPECT_CALL(mockDatabase, rollback()).Times(0);
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/other/unknow/path")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(insertIntoDirectories, write(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(mockDatabase, commit());
@@ -310,13 +314,13 @@ TEST_F(FilePathStorage,
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/other/unknow/path")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(insertIntoDirectories, write(TypedEq<Utils::SmallStringView>("/other/unknow/path")))
.WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
EXPECT_CALL(mockDatabase, rollback());
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/other/unknow/path")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(insertIntoDirectories, write(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(mockDatabase, commit());
@@ -329,13 +333,13 @@ TEST_F(FilePathStorage, CallSelectAndWriteForFetchingDirectoryIdTwoTimesIfTheInd
EXPECT_CALL(mockDatabase,deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/other/unknow/path")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(insertIntoDirectories, write(TypedEq<Utils::SmallStringView>("/other/unknow/path")))
.WillOnce(Throw(Sqlite::ConstraintPreventsModification("busy")));
EXPECT_CALL(mockDatabase, rollback());
EXPECT_CALL(mockDatabase,deferredBegin());
EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath,
valueReturnInt32(Eq("/other/unknow/path")));
valueReturnInt32(TypedEq<Utils::SmallStringView>("/other/unknow/path")));
EXPECT_CALL(mockDatabase, commit());
storage.fetchDirectoryId("/other/unknow/path");
@@ -368,7 +372,6 @@ TEST_F(FilePathStorage,
EXPECT_CALL(insertIntoSources,
write(TypedEq<int>(5), TypedEq<Utils::SmallStringView>("otherunknownfile.h")))
.WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
;
EXPECT_CALL(mockDatabase, rollback());
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectSourceIdFromSourcesByDirectoryIdAndSourceName,
@@ -545,4 +548,59 @@ TEST_F(FilePathStorage, RestartFetchAllSourcesIfBeginIsBusy)
storage.fetchAllSources();
}
TEST_F(FilePathStorage, FetchDirectoryIdForUnknownFileID)
{
ASSERT_THROW(storage.fetchDirectoryId(1111), ClangBackEnd::SourceNameIdDoesNotExists);
}
TEST_F(FilePathStorage, FetchDirectoryId)
{
auto directoryId = storage.fetchDirectoryId(42);
ASSERT_THAT(directoryId, 5);
}
TEST_F(FilePathStorage, FetchDirectoryIdCalls)
{
InSequence s;
EXPECT_CALL(mockDatabase, lock());
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq<int>(42)));
EXPECT_CALL(mockDatabase, commit());
EXPECT_CALL(mockDatabase, unlock());
storage.fetchDirectoryId(42);
}
TEST_F(FilePathStorage, FetchDirectoryIdCallsDatabaseIsBusy)
{
InSequence s;
EXPECT_CALL(mockDatabase, lock());
EXPECT_CALL(mockDatabase, deferredBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
EXPECT_CALL(mockDatabase, rollback()).Times(0);
EXPECT_CALL(mockDatabase, unlock());
EXPECT_CALL(mockDatabase, lock());
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq<int>(42)));
EXPECT_CALL(mockDatabase, commit());
EXPECT_CALL(mockDatabase, unlock());
storage.fetchDirectoryId(42);
}
TEST_F(FilePathStorage, FetchDirectoryIdCallsThrows)
{
InSequence s;
EXPECT_CALL(mockDatabase, lock());
EXPECT_CALL(mockDatabase, deferredBegin());
EXPECT_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq<int>(41)));
EXPECT_CALL(mockDatabase, rollback());
EXPECT_CALL(mockDatabase, unlock());
ASSERT_ANY_THROW(storage.fetchDirectoryId(41));
}
} // namespace

View File

@@ -61,7 +61,13 @@ TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceIdFromSourcesByDirecto
Eq("SELECT sourceId FROM sources WHERE directoryId = ? AND sourceName = ?"));
}
TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceNameFromSourcesByDirectoryIdAndSourceId)
TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceNameAndDirectoryIdFromSourcesByAndSourceId)
{
ASSERT_THAT(factory.selectSourceNameAndDirectoryIdFromSourcesBySourceId.sqlStatement,
Eq("SELECT sourceName, directoryId FROM sources WHERE sourceId = ?"));
}
TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceNameAndDirectoryIdBySourceId)
{
ASSERT_THAT(factory.selectSourceNameAndDirectoryIdFromSourcesBySourceId.sqlStatement,
Eq("SELECT sourceName, directoryId FROM sources WHERE sourceId = ?"));

View File

@@ -24,6 +24,7 @@
****************************************************************************/
#include "googletest.h"
#include "mockfilesystem.h"
#include <filepathcaching.h>
#include <filestatuscache.h>
@@ -37,31 +38,33 @@
namespace {
using ClangBackEnd::FilePathId;
using ClangBackEnd::FilePathIds;
class FileStatusCache : public testing::Test
{
protected:
FilePathId filePathId(Utils::SmallStringView path) const
FileStatusCache()
{
return filePathCache.filePathId(ClangBackEnd::FilePathView(path));
}
void touchFile(FilePathId filePathId)
{
std::ofstream ostream(std::string(filePathCache.filePath(filePathId)), std::ios::binary);
ostream.write("\n", 1);
ostream.close();
ON_CALL(fileSystem, lastModified(Eq(header))).WillByDefault(Return(headerLastModifiedTime));
ON_CALL(fileSystem, lastModified(Eq(source))).WillByDefault(Return(sourceLastModifiedTime));
ON_CALL(fileSystem, lastModified(Eq(header2))).WillByDefault(Return(header2LastModifiedTime));
ON_CALL(fileSystem, lastModified(Eq(source2))).WillByDefault(Return(source2LastModifiedTime));
}
protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database};
ClangBackEnd::FilePathCaching filePathCache{database};
ClangBackEnd::FileStatusCache cache{filePathCache};
FilePathId header{filePathId(TESTDATA_DIR "/filestatuscache_header.h")};
FilePathId source{filePathId(TESTDATA_DIR "/filestatuscache_header.cpp")};
long long headerLastModifiedTime = QFileInfo(TESTDATA_DIR "/filestatuscache_header.h").lastModified().toSecsSinceEpoch();
long long sourceLastModifiedTime = QFileInfo(TESTDATA_DIR "/filestatuscache_header.cpp").lastModified().toSecsSinceEpoch();
NiceMock<MockFileSystem> fileSystem;
ClangBackEnd::FileStatusCache cache{fileSystem};
FilePathId header{1};
FilePathId source{2};
FilePathId header2{3};
FilePathId source2{4};
FilePathIds entries{header, source, header2, source2};
long long headerLastModifiedTime = 100;
long long headerLastModifiedTime2 = 110;
long long header2LastModifiedTime = 300;
long long header2LastModifiedTime2 = 310;
long long sourceLastModifiedTime = 200;
long long source2LastModifiedTime = 400;
};
TEST_F(FileStatusCache, CreateEntry)
@@ -134,18 +137,24 @@ TEST_F(FileStatusCache, AskNewEntryReverseOrderAddedForLastModifiedTime)
TEST_F(FileStatusCache, UpdateFile)
{
auto oldLastModified = cache.lastModifiedTime(header);
touchFile(header);
EXPECT_CALL(fileSystem, lastModified(Eq(header)))
.Times(2)
.WillOnce(Return(headerLastModifiedTime))
.WillOnce(Return(headerLastModifiedTime2));
cache.lastModifiedTime(header);
cache.update(header);
ASSERT_THAT(cache.lastModifiedTime(header), Gt(oldLastModified));
ASSERT_THAT(cache.lastModifiedTime(header), headerLastModifiedTime2);
}
TEST_F(FileStatusCache, UpdateFileDoesNotChangeEntryCount)
{
EXPECT_CALL(fileSystem, lastModified(Eq(header)))
.Times(2)
.WillOnce(Return(headerLastModifiedTime))
.WillOnce(Return(headerLastModifiedTime2));
cache.lastModifiedTime(header);
touchFile(header);
cache.update(header);
@@ -154,11 +163,136 @@ TEST_F(FileStatusCache, UpdateFileDoesNotChangeEntryCount)
TEST_F(FileStatusCache, UpdateFileForNonExistingEntry)
{
touchFile(header);
cache.update(header);
ASSERT_THAT(cache, SizeIs(0));
}
TEST_F(FileStatusCache, UpdateFiles)
{
EXPECT_CALL(fileSystem, lastModified(Eq(header)))
.Times(2)
.WillOnce(Return(headerLastModifiedTime))
.WillOnce(Return(headerLastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(header2)))
.Times(2)
.WillOnce(Return(header2LastModifiedTime))
.WillOnce(Return(header2LastModifiedTime2));
cache.lastModifiedTime(header);
cache.lastModifiedTime(header2);
cache.update(entries);
ASSERT_THAT(cache.lastModifiedTime(header), headerLastModifiedTime2);
ASSERT_THAT(cache.lastModifiedTime(header2), header2LastModifiedTime2);
}
TEST_F(FileStatusCache, UpdateFilesDoesNotChangeEntryCount)
{
EXPECT_CALL(fileSystem, lastModified(Eq(header)))
.Times(2)
.WillOnce(Return(headerLastModifiedTime))
.WillOnce(Return(headerLastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(header2)))
.Times(2)
.WillOnce(Return(header2LastModifiedTime))
.WillOnce(Return(header2LastModifiedTime2));
cache.lastModifiedTime(header);
cache.lastModifiedTime(header2);
cache.update(entries);
ASSERT_THAT(cache, SizeIs(2));
}
TEST_F(FileStatusCache, UpdateFilesForNonExistingEntry)
{
cache.update(entries);
ASSERT_THAT(cache, SizeIs(0));
}
TEST_F(FileStatusCache, NewModifiedEntries)
{
auto modifiedIds = cache.modified(entries);
ASSERT_THAT(modifiedIds, entries);
}
TEST_F(FileStatusCache, NoNewModifiedEntries)
{
cache.modified(entries);
auto modifiedIds = cache.modified(entries);
ASSERT_THAT(modifiedIds, IsEmpty());
}
TEST_F(FileStatusCache, SomeNewModifiedEntries)
{
cache.modified({source, header2});
auto modifiedIds = cache.modified(entries);
ASSERT_THAT(modifiedIds, ElementsAre(header, source2));
}
TEST_F(FileStatusCache, SomeAlreadyExistingModifiedEntries)
{
EXPECT_CALL(fileSystem, lastModified(Eq(header)))
.Times(2)
.WillOnce(Return(headerLastModifiedTime))
.WillOnce(Return(headerLastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(header2)))
.Times(2)
.WillOnce(Return(header2LastModifiedTime))
.WillOnce(Return(header2LastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(source))).Times(2).WillRepeatedly(Return(sourceLastModifiedTime));
EXPECT_CALL(fileSystem, lastModified(Eq(source2)))
.Times(2)
.WillRepeatedly(Return(source2LastModifiedTime));
cache.modified(entries);
auto modifiedIds = cache.modified(entries);
ASSERT_THAT(modifiedIds, ElementsAre(header, header2));
}
TEST_F(FileStatusCache, SomeAlreadyExistingAndSomeNewModifiedEntries)
{
EXPECT_CALL(fileSystem, lastModified(Eq(header))).WillRepeatedly(Return(headerLastModifiedTime));
EXPECT_CALL(fileSystem, lastModified(Eq(header2)))
.Times(2)
.WillOnce(Return(header2LastModifiedTime))
.WillOnce(Return(header2LastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(source))).Times(2).WillRepeatedly(Return(sourceLastModifiedTime));
EXPECT_CALL(fileSystem, lastModified(Eq(source2))).WillRepeatedly(Return(source2LastModifiedTime));
cache.modified({source, header2});
auto modifiedIds = cache.modified(entries);
ASSERT_THAT(modifiedIds, ElementsAre(header, header2, source2));
}
TEST_F(FileStatusCache, TimeIsUpdatedForSomeAlreadyExistingModifiedEntries)
{
EXPECT_CALL(fileSystem, lastModified(Eq(header)))
.Times(2)
.WillOnce(Return(headerLastModifiedTime))
.WillOnce(Return(headerLastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(header2)))
.Times(2)
.WillOnce(Return(header2LastModifiedTime))
.WillOnce(Return(header2LastModifiedTime2));
EXPECT_CALL(fileSystem, lastModified(Eq(source))).Times(2).WillRepeatedly(Return(sourceLastModifiedTime));
EXPECT_CALL(fileSystem, lastModified(Eq(source2)))
.Times(2)
.WillRepeatedly(Return(source2LastModifiedTime));
cache.modified(entries);
cache.modified(entries);
ASSERT_THAT(cache.lastModifiedTime(header), headerLastModifiedTime2);
}
} // namespace

View File

@@ -361,7 +361,7 @@ std::ostream &operator<<(std::ostream &out, const WatcherEntry &entry)
{
out << "("
<< entry.id << ", "
<< entry.pathId
<< entry.filePathId
<< ")";
return out;

View File

@@ -36,5 +36,11 @@ public:
ClangBackEnd::FilePathId (ClangBackEnd::FilePathView filePath));
MOCK_CONST_METHOD1(filePath,
ClangBackEnd::FilePath (ClangBackEnd::FilePathId filePathId));
MOCK_CONST_METHOD1(directoryPathId,
ClangBackEnd::DirectoryPathId(Utils::SmallStringView directoryPath));
MOCK_CONST_METHOD1(directoryPath,
Utils::PathString(ClangBackEnd::DirectoryPathId directoryPathId));
MOCK_CONST_METHOD1(directoryPathId,
ClangBackEnd::DirectoryPathId(ClangBackEnd::FilePathId filePathId));
};

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** Copyright (C) 2019 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 "googletest.h"
#include <filesysteminterface.h>
class MockFileSystem : public ClangBackEnd::FileSystemInterface
{
public:
MOCK_CONST_METHOD1(directoryEntries, ClangBackEnd::FilePathIds(const QString &directoryPath));
MOCK_CONST_METHOD1(lastModified, long long(ClangBackEnd::FilePathId filePathId));
};

View File

@@ -41,4 +41,5 @@ public:
signals:
void fileChanged(const QString &);
void directoryChanged(const QString &);
};

View File

@@ -137,6 +137,12 @@ MockSqliteReadStatement::value<int>(const int &directoryId, const Utils::SmallSt
return valueReturnInt32(directoryId, text);
}
template<>
Utils::optional<int> MockSqliteReadStatement::value<int>(const int &value)
{
return valueReturnInt32(value);
}
template <>
Utils::optional<long long>
MockSqliteReadStatement::value<long long>(const int &sourceId)

View File

@@ -92,11 +92,11 @@ public:
MOCK_METHOD1(valueReturnInt32, Utils::optional<int>(Utils::SmallStringView));
MOCK_METHOD2(valueReturnInt32,
Utils::optional<int>(int, Utils::SmallStringView));
MOCK_METHOD2(valueReturnInt32, Utils::optional<int>(int, Utils::SmallStringView));
MOCK_METHOD1(valueReturnInt64,
Utils::optional<long long>(int));
MOCK_METHOD1(valueReturnInt32, Utils::optional<int>(int));
MOCK_METHOD1(valueReturnInt64, Utils::optional<long long>(int));
MOCK_METHOD1(valueReturnPathString,
Utils::optional<Utils::PathString>(int));
@@ -244,6 +244,9 @@ template <>
Utils::optional<int>
MockSqliteReadStatement::value<int>(const int&, const Utils::SmallStringView&);
template<>
Utils::optional<int> MockSqliteReadStatement::value<int>(const int &);
template <>
Utils::optional<long long>
MockSqliteReadStatement::value<long long>(const ClangBackEnd::FilePathId&);

View File

@@ -28,6 +28,7 @@
#include "mockbuilddependenciesstorage.h"
#include "mockclangpathwatcher.h"
#include "mockfilepathcaching.h"
#include "mockfilesystem.h"
#include "mockmodifiedtimechecker.h"
#include "mockprecompiledheaderstorage.h"
#include "mockprojectpartsstorage.h"
@@ -149,13 +150,6 @@ protected:
data.reset();
}
void touchFile(FilePathId filePathId)
{
std::ofstream ostream(std::string(filePathCache.filePath(filePathId)), std::ios::binary);
ostream.write("\n", 1);
ostream.close();
}
FilePathId filePathId(Utils::SmallStringView path) const
{
return filePathCache.filePathId(ClangBackEnd::FilePathView(path));
@@ -249,7 +243,8 @@ protected:
NiceMock<MockPrecompiledHeaderStorage> mockPrecompiledHeaderStorage;
NiceMock<MockProjectPartsStorage> mockProjectPartsStorage;
NiceMock<MockClangPathWatcher> mockPathWatcher;
ClangBackEnd::FileStatusCache fileStatusCache{filePathCache};
NiceMock<MockFileSystem> mockFileSystem;
ClangBackEnd::FileStatusCache fileStatusCache{mockFileSystem};
ClangBackEnd::GeneratedFiles generatedFiles;
Manager collectorManger{generatedFiles};
NiceMock<MockFunction<void(int, int)>> mockSetProgressCallback;
@@ -1664,13 +1659,12 @@ TEST_F(SymbolIndexer, DISABLED_DontReparseInUpdateProjectPartsIfDefinesAreTheSam
TEST_F(SymbolIndexer, PathsChangedUpdatesFileStatusCache)
{
auto sourceId = filePathId(TESTDATA_DIR "/symbolindexer_pathChanged.cpp");
auto oldLastModified = fileStatusCache.lastModifiedTime(sourceId);
touchFile(sourceId);
ON_CALL(mockFileSystem, lastModified(Eq(sourceId))).WillByDefault(Return(65));
ON_CALL(mockSymbolStorage, fetchDependentSourceIds(_)).WillByDefault(Return(FilePathIds{sourceId}));
indexer.pathsChanged({sourceId});
ASSERT_THAT(fileStatusCache.lastModifiedTime(sourceId), Gt(oldLastModified));
ASSERT_THAT(fileStatusCache.lastModifiedTime(sourceId), 65);
}
TEST_F(SymbolIndexer, GetUpdatableFilePathIdsIfCompilerMacrosAreDifferent)
@@ -1706,6 +1700,7 @@ TEST_F(SymbolIndexer, GetNoUpdatableFilePathIdsIfArtefactsAreTheSame)
TEST_F(SymbolIndexer, OutdatedFilesPassUpdatableFilePathIds)
{
ON_CALL(mockFileSystem, lastModified(Eq(main1PathId))).WillByDefault(Return(65));
indexer.pathsChanged({main1PathId});
ON_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(A<ProjectPartId>()))
.WillByDefault(Return(artefact));

View File

@@ -46,7 +46,7 @@ QMAKE_SUBSTITUTES += cpptoolsjson
DEFINES += CPPTOOLS_JSON=\"R\\\"xxx($${cpptoolsjson.output})xxx\\\"\"
SOURCES += \
changedfilepathcompressor-test.cpp \
directorypathcompressor-test.cpp \
clangpathwatcher-test.cpp \
clangqueryexamplehighlightmarker-test.cpp \
clangqueryhighlightmarker-test.cpp \
@@ -175,7 +175,7 @@ SOURCES += \
translationunitupdater-test.cpp \
unsavedfiles-test.cpp \
unsavedfile-test.cpp \
utf8positionfromlinecolumn-test.cpp \
utf8positionfromlinecolumn-test.cpp
}
!isEmpty(LIBTOOLING_LIBS) {
@@ -226,6 +226,7 @@ HEADERS += \
mockclangcodemodelserver.h \
mockclangpathwatcher.h \
mockclangpathwatchernotifier.h \
mockfilesystem.h \
mockpchcreator.h \
mockpchmanagerclient.h \
mockpchmanagernotifier.h \