forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
@@ -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
|
||||
|
90
src/libs/clangsupport/directoryandfilepathid.h
Normal file
90
src/libs/clangsupport/directoryandfilepathid.h
Normal 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
|
@@ -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
|
81
src/libs/clangsupport/directorypathid.h
Normal file
81
src/libs/clangsupport/directorypathid.h
Normal 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
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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:
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
169
src/libs/clangsupport/filestatuscache.cpp
Normal file
169
src/libs/clangsupport/filestatuscache.cpp
Normal 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
|
@@ -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
|
61
src/libs/clangsupport/filesystem.cpp
Normal file
61
src/libs/clangsupport/filesystem.cpp
Normal 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
|
48
src/libs/clangsupport/filesystem.h
Normal file
48
src/libs/clangsupport/filesystem.h
Normal 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
|
43
src/libs/clangsupport/filesysteminterface.h
Normal file
43
src/libs/clangsupport/filesysteminterface.h
Normal 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
|
@@ -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;
|
||||
|
@@ -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};
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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{
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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, ¬ifier};
|
||||
NiceMock<MockFileSystem> mockFileSystem;
|
||||
Watcher watcher{mockFilePathCache, mockFileSystem, ¬ifier};
|
||||
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
|
||||
|
99
tests/unit/unittest/directorypathcompressor-test.cpp
Normal file
99
tests/unit/unittest/directorypathcompressor-test.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 = ?"));
|
||||
|
@@ -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
|
||||
|
@@ -361,7 +361,7 @@ std::ostream &operator<<(std::ostream &out, const WatcherEntry &entry)
|
||||
{
|
||||
out << "("
|
||||
<< entry.id << ", "
|
||||
<< entry.pathId
|
||||
<< entry.filePathId
|
||||
<< ")";
|
||||
|
||||
return out;
|
||||
|
@@ -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));
|
||||
};
|
||||
|
||||
|
37
tests/unit/unittest/mockfilesystem.h
Normal file
37
tests/unit/unittest/mockfilesystem.h
Normal 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));
|
||||
};
|
@@ -41,4 +41,5 @@ public:
|
||||
|
||||
signals:
|
||||
void fileChanged(const QString &);
|
||||
void directoryChanged(const QString &);
|
||||
};
|
||||
|
@@ -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)
|
||||
|
@@ -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&);
|
||||
|
@@ -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));
|
||||
|
@@ -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 \
|
||||
|
Reference in New Issue
Block a user