QmlDesigner: Add project storage

Task-number: QDS-4252
Change-Id: Ic74e027b20bc41f3712e3ae155cddc64f8972cb0
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2021-04-28 16:18:59 +02:00
parent a79adedb90
commit e4fea9f159
24 changed files with 2905 additions and 120 deletions

View File

@@ -0,0 +1,99 @@
/****************************************************************************
**
** Copyright (C) 2021 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 <vector>
namespace QmlDesigner {
template<auto Type, typename InternalIntergerType = long long>
class BasicId
{
public:
using DatabaseType = InternalIntergerType;
constexpr explicit BasicId() = default;
BasicId(const char *) = delete;
constexpr explicit BasicId(InternalIntergerType id)
: id{id}
{}
constexpr friend bool operator==(BasicId first, BasicId second)
{
return first.id == second.id && first.isValid() && second.isValid();
}
constexpr friend bool operator!=(BasicId first, BasicId second) { return !(first == second); }
constexpr friend bool operator<(BasicId first, BasicId second) { return first.id < second.id; }
constexpr friend bool operator>(BasicId first, BasicId second) { return first.id > second.id; }
constexpr friend bool operator<=(BasicId first, BasicId second)
{
return first.id <= second.id;
}
constexpr friend bool operator>=(BasicId first, BasicId second)
{
return first.id >= second.id;
}
constexpr bool isValid() const { return id >= 0; }
explicit operator bool() const { return isValid(); }
explicit operator std::size_t() const { return static_cast<std::size_t>(id); }
InternalIntergerType operator&() const { return id; }
public:
InternalIntergerType id = -1;
};
enum class BasicIdType {
Type,
PropertyType,
PropertyDeclaration,
SourceId,
SourceContextId,
StorageCacheIndex
};
using TypeId = BasicId<BasicIdType::Type>;
using TypeIds = std::vector<TypeId>;
using PropertyDeclarationId = BasicId<BasicIdType::PropertyDeclaration>;
using PropertyDeclarationIds = std::vector<PropertyDeclarationId>;
using SourceContextId = BasicId<BasicIdType::SourceContextId, int>;
using SourceContextIds = std::vector<SourceContextId>;
using SourceId = BasicId<BasicIdType::SourceId, int>;
using SourceIds = std::vector<SourceId>;
enum class TypeAccessSemantics { Reference, Value, Sequence, IsEnum = 0xF };
} // namespace QmlDesigner

View File

@@ -0,0 +1,446 @@
/****************************************************************************
**
** Copyright (C) 2021 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 "projectstorageexceptions.h"
#include "projectstorageids.h"
#include "projectstoragetypes.h"
#include <sqlitetable.h>
#include <sqlitetransaction.h>
#include <utils/optional.h>
#include <tuple>
namespace QmlDesigner {
template<typename Database>
class ProjectStorage
{
public:
template<int ResultCount>
using ReadStatement = typename Database::template ReadStatement<ResultCount>;
template<int ResultCount>
using ReadWriteStatement = typename Database::template ReadWriteStatement<ResultCount>;
using WriteStatement = typename Database::WriteStatement;
ProjectStorage(Database &database, bool isInitialized)
: database{database}
, initializer{database, isInitialized}
{}
template<typename Container>
TypeId upsertType(Utils::SmallStringView name,
TypeId prototype,
TypeAccessSemantics accessSemantics,
const Container &qualifiedNames)
{
Sqlite::ImmediateTransaction transaction{database};
auto typeId = upsertTypeStatement.template value<TypeId>(name,
static_cast<long long>(accessSemantics),
&prototype);
for (Utils::SmallStringView qualifiedName : qualifiedNames)
upsertQualifiedTypeNameStatement.write(qualifiedName, &typeId);
transaction.commit();
return typeId;
}
PropertyDeclarationId upsertPropertyDeclaration(TypeId typeId,
Utils::SmallStringView name,
TypeId propertyTypeId)
{
Sqlite::ImmediateTransaction transaction{database};
auto propertyDeclarationId = upsertPropertyDeclarationStatement
.template value<PropertyDeclarationId>(&typeId,
name,
&propertyTypeId);
transaction.commit();
return propertyDeclarationId;
}
PropertyDeclarationId fetchPropertyDeclarationByTypeIdAndName(TypeId typeId,
Utils::SmallStringView name)
{
return selectPropertyDeclarationByTypeIdAndNameStatement
.template valueWithTransaction<PropertyDeclarationId>(&typeId, name);
}
TypeId fetchTypeIdByQualifiedName(Utils::SmallStringView name)
{
return selectTypeIdByQualifiedNameStatement.template valueWithTransaction<TypeId>(name);
}
bool fetchIsProtype(TypeId type, TypeId prototype)
{
return bool(
selectPrototypeIdStatement.template valueWithTransaction<TypeId>(&type, &prototype));
}
auto fetchPrototypes(TypeId type)
{
return selectPrototypeIdsStatement.template rangeWithTransaction<TypeId>(&type);
}
SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath)
{
auto sourceContextId = readSourceContextId(sourceContextPath);
return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath);
}
SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath)
{
try {
Sqlite::DeferredTransaction transaction{database};
auto sourceContextId = fetchSourceContextIdUnguarded(sourceContextPath);
transaction.commit();
return sourceContextId;
} catch (const Sqlite::ConstraintPreventsModification &) {
return fetchSourceContextId(sourceContextPath);
}
}
Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const
{
Sqlite::DeferredTransaction transaction{database};
auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement
.template optionalValue<Utils::PathString>(
&sourceContextId);
if (!optionalSourceContextPath)
throw SourceContextIdDoesNotExists();
transaction.commit();
return std::move(*optionalSourceContextPath);
}
auto fetchAllSourceContexts() const
{
return selectAllSourceContextsStatement.template rangeWithTransaction<Sources::SourceContext>();
}
SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
Sqlite::DeferredTransaction transaction{database};
auto sourceId = fetchSourceIdUnguarded(sourceContextId, sourceName);
transaction.commit();
return sourceId;
}
Sources::SourceNameAndSourceContextId fetchSourceNameAndSourceContextId(SourceId sourceId) const
{
auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement
.template valueWithTransaction<Sources::SourceNameAndSourceContextId>(
&sourceId);
if (!value.sourceContextId)
throw SourceIdDoesNotExists();
return value;
}
SourceContextId fetchSourceContextId(SourceId sourceId) const
{
auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement
.template valueWithTransaction<SourceContextId>(sourceId.id);
if (!sourceContextId)
throw SourceIdDoesNotExists();
return sourceContextId;
}
auto fetchAllSources() const
{
return selectAllSourcesStatement.template rangeWithTransaction<Sources::Source>();
}
SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
auto sourceId = readSourceId(sourceContextId, sourceName);
if (sourceId)
return sourceId;
return writeSourceId(sourceContextId, sourceName);
}
private:
SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath)
{
return selectSourceContextIdFromSourceContextsBySourceContextPathStatement
.template value<SourceContextId>(sourceContextPath);
}
SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath)
{
insertIntoSourceContextsStatement.write(sourceContextPath);
return SourceContextId(database.lastInsertedRowId());
}
SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
insertIntoSourcesStatement.write(&sourceContextId, sourceName);
return SourceId(database.lastInsertedRowId());
}
SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
return selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement
.template value<SourceId>(&sourceContextId, sourceName);
}
class Initializer
{
public:
Initializer(Database &database, bool isInitialized)
{
if (!isInitialized) {
Sqlite::ExclusiveTransaction transaction{database};
createTypesTable(database);
createQualifiedTypeNamesTable(database);
createPropertyDeclarationsTable(database);
createEnumValuesTable(database);
createMethodsTable(database);
createSignalsTable(database);
createSourceContextsTable(database);
createSourcesTable(database);
transaction.commit();
database.walCheckpointFull();
}
}
void createPropertyDeclarationsTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("propertyDeclarations");
table.addColumn("propertyDeclarationId",
Sqlite::ColumnType::Integer,
{Sqlite::PrimaryKey{}});
auto &typeIdColumn = table.addColumn("typeId");
auto &nameColumn = table.addColumn("name");
table.addColumn("propertyTypeId");
table.addUniqueIndex({typeIdColumn, nameColumn});
table.initialize(database);
}
void createTypesTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("types");
table.addColumn("typeId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
auto &nameColumn = table.addColumn("name");
table.addColumn("accessSemantics");
table.addColumn("prototype");
table.addColumn("defaultProperty");
table.addUniqueIndex({nameColumn});
table.initialize(database);
}
void createQualifiedTypeNamesTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setUseWithoutRowId(true);
table.setName("qualifiedTypeNames");
auto &qualifiedNameColumn = table.addColumn("qualifiedName");
table.addColumn("typeId");
table.addPrimaryKeyContraint({qualifiedNameColumn});
table.initialize(database);
}
void createEnumValuesTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("enumerationValues");
table.addColumn("enumerationValueId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
auto &enumIdColumn = table.addColumn("typeId");
auto &nameColumn = table.addColumn("name");
table.addUniqueIndex({enumIdColumn, nameColumn});
table.initialize(database);
}
void createMethodsTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("methods");
table.addColumn("methodId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
auto &nameColumn = table.addColumn("name");
table.addUniqueIndex({nameColumn});
table.initialize(database);
}
void createSignalsTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("signals");
table.addColumn("signalId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
auto &nameColumn = table.addColumn("name");
table.addUniqueIndex({nameColumn});
table.initialize(database);
}
void createSourceContextsTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("sourceContexts");
table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath");
table.addUniqueIndex({sourceContextPathColumn});
table.initialize(database);
}
void createSourcesTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("sources");
table.addColumn("sourceId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
const Sqlite::Column &sourceContextIdColumn = table.addColumn(
"sourceContextId",
Sqlite::ColumnType::Integer,
{Sqlite::NotNull{},
Sqlite::ForeignKey{"sourceContexts",
"sourceContextId",
Sqlite::ForeignKeyAction::NoAction,
Sqlite::ForeignKeyAction::Cascade}});
const Sqlite::Column &sourceNameColumn = table.addColumn("sourceName",
Sqlite::ColumnType::Text);
table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn});
table.initialize(database);
}
};
public:
Database &database;
Initializer initializer;
ReadWriteStatement<1> upsertTypeStatement{
"INSERT INTO types(name, accessSemantics, prototype) VALUES(?1, ?2, nullif(?3, -1)) ON "
"CONFLICT DO UPDATE SET "
"prototype=excluded.prototype, accessSemantics=excluded.accessSemantics RETURNING typeId",
database};
mutable ReadStatement<1> selectTypeIdByQualifiedNameStatement{
"SELECT typeId FROM qualifiedTypeNames WHERE qualifiedName=?", database};
mutable ReadStatement<1> selectPrototypeIdStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
" VALUES(?1) "
" UNION ALL "
" SELECT prototype FROM types JOIN typeSelection USING(typeId)) "
"SELECT typeId FROM typeSelection WHERE typeId=?2 LIMIT 1",
database};
ReadWriteStatement<1> upsertPropertyDeclarationStatement{
"INSERT INTO propertyDeclarations(typeId, name, propertyTypeId) VALUES(?1, ?2, ?3) ON "
"CONFLICT DO UPDATE SET "
"typeId=excluded.typeId, name=excluded.name, propertyTypeId=excluded.propertyTypeId "
"RETURNING propertyDeclarationId",
database};
mutable ReadStatement<1> selectPropertyDeclarationByTypeIdAndNameStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
" VALUES(?1) "
" UNION ALL "
" SELECT prototype FROM types JOIN typeSelection USING(typeId)) "
"SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) "
" WHERE name=?2 LIMIT 1",
database};
WriteStatement upsertQualifiedTypeNameStatement{
"INSERT INTO qualifiedTypeNames(qualifiedName, typeId) VALUES(?1, ?2) ON CONFLICT DO "
"UPDATE SET typeId=excluded.typeId",
database};
mutable ReadStatement<1> selectAccessSemanticsStatement{
"SELECT typeId FROM qualifiedTypeNames WHERE qualifiedName=?", database};
mutable ReadStatement<1> selectPrototypeIdsStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
" VALUES(?1) "
" UNION ALL "
" SELECT prototype FROM types JOIN typeSelection USING(typeId)) "
"SELECT typeId FROM typeSelection",
database};
mutable ReadStatement<1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{
"SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database};
mutable ReadStatement<1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{
"SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database};
mutable ReadStatement<2> selectAllSourceContextsStatement{
"SELECT sourceContextPath, sourceContextId FROM sourceContexts", database};
WriteStatement insertIntoSourceContextsStatement{
"INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database};
mutable ReadStatement<1> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{
"SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database};
mutable ReadStatement<2> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{
"SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database};
mutable ReadStatement<1> selectSourceContextIdFromSourcesBySourceIdStatement{
"SELECT sourceContextId FROM sources WHERE sourceId = ?", database};
WriteStatement insertIntoSourcesStatement{
"INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database};
mutable ReadStatement<3> selectAllSourcesStatement{
"SELECT sourceName, sourceContextId, sourceId FROM sources", database};
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <exception>
namespace QmlDesigner {
class NoFilePathForInvalidFilePathId : std::exception
{
public:
const char *what() const noexcept override
{
return "You cannot get a file path for an invalid file path id!";
}
};
class NoSourceContextPathForInvalidSourceContextId : std::exception
{
public:
const char *what() const noexcept override
{
return "You cannot get a directory path for an invalid directory path id!";
}
};
class SourceContextIdDoesNotExists : std::exception
{
public:
const char *what() const noexcept override
{
return "The source context id does not exist in the database!";
}
};
class SourceIdDoesNotExists : std::exception
{
public:
const char *what() const noexcept override
{
return "The source id does not exist in the database!";
}
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,167 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "projectstorageids.h"
#include "storagecacheentry.h"
#include <utils/smallstring.h>
#include <cstdint>
#include <vector>
#include <tuple>
#include <unordered_map>
namespace QmlDesigner {
class FileNameView
{
public:
friend bool operator==(const FileNameView &first, const FileNameView &second)
{
return first.sourceContextId == second.sourceContextId && first.fileName == second.fileName;
}
static int compare(FileNameView first, FileNameView second) noexcept
{
int directoryDifference = first.sourceContextId.id - second.sourceContextId.id;
if (directoryDifference)
return directoryDifference;
return Utils::compare(first.fileName, second.fileName);
}
public:
Utils::SmallStringView fileName;
SourceContextId sourceContextId;
};
class FileNameEntry
{
public:
FileNameEntry(Utils::SmallStringView fileName, int sourceContextId)
: fileName(fileName)
, sourceContextId(sourceContextId)
{}
FileNameEntry(Utils::SmallStringView fileName, SourceContextId sourceContextId)
: fileName(fileName)
, sourceContextId(sourceContextId)
{}
FileNameEntry(FileNameView view)
: fileName(view.fileName)
, sourceContextId(view.sourceContextId)
{}
friend bool operator==(const FileNameEntry &first, const FileNameEntry &second)
{
return first.sourceContextId == second.sourceContextId && first.fileName == second.fileName;
}
friend bool operator!=(const FileNameEntry &first, const FileNameEntry &second)
{
return !(first == second);
}
friend bool operator==(const FileNameEntry &first, const FileNameView &second)
{
return first.sourceContextId == second.sourceContextId && first.fileName == second.fileName;
}
friend bool operator!=(const FileNameEntry &first, const FileNameView &second)
{
return !(first == second);
}
operator FileNameView() const { return {fileName, sourceContextId}; }
operator Utils::SmallString() && { return std::move(fileName); }
public:
Utils::SmallString fileName;
SourceContextId sourceContextId;
};
namespace Sources {
class SourceContext
: public StorageCacheEntry<Utils::PathString, Utils::SmallStringView, SourceContextId>
{
using Base = StorageCacheEntry<Utils::PathString, Utils::SmallStringView, SourceContextId>;
public:
using Base::Base;
friend bool operator==(const SourceContext &first, const SourceContext &second)
{
return first.id == second.id && first.value == second.value;
}
};
using SourceContexts = std::vector<SourceContext>;
class Source : public StorageCacheEntry<FileNameEntry, FileNameView, SourceId>
{
using Base = StorageCacheEntry<FileNameEntry, FileNameView, SourceId>;
public:
using Base::Base;
Source(Utils::SmallStringView sourceName, SourceContextId sourceContextId, SourceId sourceId)
: Base{{sourceName, sourceContextId}, sourceId}
{}
Source(Utils::SmallStringView sourceName, int sourceContextId, int sourceId)
: Base{{sourceName, SourceContextId{sourceContextId}}, SourceId{sourceId}}
{}
friend bool operator==(const Source &first, const Source &second)
{
return first.id == second.id && first.value == second.value;
}
};
using Sources = std::vector<Source>;
class SourceNameAndSourceContextId
{
public:
constexpr SourceNameAndSourceContextId() = default;
SourceNameAndSourceContextId(Utils::SmallStringView sourceName, int sourceContextId)
: sourceName(sourceName)
, sourceContextId(sourceContextId)
{}
SourceNameAndSourceContextId(Utils::SmallStringView sourceName, SourceContextId sourceContextId)
: sourceName{sourceName}
, sourceContextId{sourceContextId}
{}
Utils::SmallString sourceName;
SourceContextId sourceContextId;
};
} // namespace ClangBackEnd
} // namespace QmlDesigner

View File

@@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <cstdint>
#include <tuple>
#include <vector>
namespace QmlDesigner {
class SourceId
{
public:
constexpr SourceId() = default;
SourceId(const char *) = delete;
explicit SourceId(int id)
: id(id)
{}
bool isValid() const { return id >= 0; }
friend bool operator==(SourceId first, SourceId second)
{
return first.isValid() && first.id == second.id;
}
friend bool operator!=(SourceId first, SourceId second) { return !(first == second); }
friend bool operator<(SourceId first, SourceId second) { return first.id < second.id; }
public:
int id = -1;
};
using SourceIds = std::vector<SourceId>;
} // namespace QmlDesigner
namespace std {
template<>
struct hash<QmlDesigner::SourceId>
{
using argument_type = QmlDesigner::SourceId;
using result_type = std::size_t;
result_type operator()(const argument_type &id) const { return std::hash<int>{}(id.id); }
};
} // namespace std

View File

@@ -0,0 +1,148 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "sourcepathview.h"
#include <utils/smallstring.h>
namespace QmlDesigner {
class SourcePath : public Utils::PathString
{
using size_type = Utils::PathString::size_type;
public:
SourcePath() = default;
explicit SourcePath(Utils::PathString &&sourcePath)
: Utils::PathString(std::move(sourcePath))
{
SourcePathView view{*this};
m_slashIndex = view.slashIndex();
}
explicit SourcePath(Utils::SmallStringView &&sourcePath)
: Utils::PathString(sourcePath)
{
SourcePathView view{*this};
m_slashIndex = view.slashIndex();
}
SourcePath(SourcePathView sourcePathView)
: Utils::PathString(sourcePathView.toStringView())
, m_slashIndex(sourcePathView.slashIndex())
{
}
template<size_type Size>
SourcePath(const char (&string)[Size]) noexcept
: SourcePath(SourcePathView(string, Size - 1))
{
static_assert(Size >= 1, "Invalid string literal! Length is zero!");
}
explicit SourcePath(const Utils::PathString &sourcePath)
: SourcePath(sourcePath.clone())
{
}
explicit SourcePath(Utils::PathString &&sourcePath, std::ptrdiff_t slashIndex)
: Utils::PathString(std::move(sourcePath))
, m_slashIndex(slashIndex)
{
}
explicit SourcePath(const QString &sourcePath)
: SourcePath(Utils::PathString(sourcePath))
{
}
SourcePath(Utils::SmallStringView directory, Utils::SmallStringView name)
: Utils::PathString({directory, "/", name})
, m_slashIndex(std::ptrdiff_t(directory.size()))
{}
bool isValid() const { return size() > 0 && m_slashIndex >= 0; }
Utils::SmallStringView directory() const noexcept
{
return mid(0, std::size_t(std::max(std::ptrdiff_t(0), m_slashIndex)));
}
Utils::SmallStringView name() const noexcept
{
return mid(std::size_t(m_slashIndex + 1),
std::size_t(std::ptrdiff_t(size()) - m_slashIndex - std::ptrdiff_t(1)));
}
const Utils::PathString &path() const noexcept
{
return *this;
}
operator SourcePathView() const noexcept { return SourcePathView(toStringView()); }
operator Utils::SmallStringView() const noexcept
{
return toStringView();
}
friend bool operator==(const SourcePath &first, const SourcePath &second)
{
return first.slashIndex() == second.slashIndex()
&& first.name() == second.name()
&& first.directory() == second.directory();
}
friend bool operator==(const SourcePath &first, const SourcePathView &second)
{
return first.toStringView() == second.toStringView();
}
friend bool operator==(const SourcePathView &first, const SourcePath &second)
{
return second == first;
}
friend bool operator<(const SourcePath &first, const SourcePath &second)
{
return std::make_tuple(first.slashIndex(), first.name(), first.directory())
< std::make_tuple(second.slashIndex(), second.name(), second.directory());
}
SourcePath clone() const { return *this; }
std::ptrdiff_t slashIndex() const { return m_slashIndex; }
private:
std::ptrdiff_t m_slashIndex = -1;
};
using SourcePaths = std::vector<SourcePath>;
} // namespace QmlDesigner

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** 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 <vector>
namespace QmlDesigner {
class SourcePathContextId
{
public:
constexpr SourcePathContextId() = default;
SourcePathContextId(const char *) = delete;
SourcePathContextId(int directoryPathId)
: directoryPathId(directoryPathId)
{}
bool isValid() const { return directoryPathId >= 0; }
friend bool operator==(SourcePathContextId first, SourcePathContextId second)
{
return first.isValid() && first.directoryPathId == second.directoryPathId;
}
friend bool operator!=(SourcePathContextId first, SourcePathContextId second)
{
return !(first == second);
}
friend bool operator<(SourcePathContextId first, SourcePathContextId second)
{
return first.directoryPathId < second.directoryPathId;
}
public:
int directoryPathId = -1;
};
using SourcePathContextIds = std::vector<SourcePathContextId>;
} // namespace QmlDesigner

View File

@@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/smallstringview.h>
#include <algorithm>
#include <vector>
namespace QmlDesigner {
template<char separator>
class AbstractSourcePathView : public Utils::SmallStringView
{
public:
constexpr AbstractSourcePathView() = default;
explicit AbstractSourcePathView(const char *const string, const size_type size) noexcept
: AbstractSourcePathView{Utils::SmallStringView{string, size}}
{
}
explicit AbstractSourcePathView(Utils::SmallStringView filePath)
: Utils::SmallStringView(filePath)
, m_slashIndex(lastSlashIndex(filePath))
{
}
template<typename String, typename = std::enable_if_t<std::is_lvalue_reference<String>::value>>
explicit AbstractSourcePathView(String &&filePath)
: AbstractSourcePathView(filePath.data(), filePath.size())
{
}
template<size_type Size>
AbstractSourcePathView(const char (&string)[Size]) noexcept
: AbstractSourcePathView(string, Size - 1)
{
static_assert(Size >= 1, "Invalid string literal! Length is zero!");
}
Utils::SmallStringView toStringView() const
{
return *this;
}
std::ptrdiff_t slashIndex() const
{
return m_slashIndex;
}
Utils::SmallStringView directory() const noexcept
{
return mid(0, std::size_t(std::max(std::ptrdiff_t(0), m_slashIndex)));
}
Utils::SmallStringView name() const noexcept
{
return mid(std::size_t(m_slashIndex + 1),
std::size_t(std::ptrdiff_t(size()) - m_slashIndex - std::ptrdiff_t(1)));
}
static
std::ptrdiff_t lastSlashIndex(Utils::SmallStringView filePath)
{
auto foundReverse = std::find(filePath.rbegin(), filePath.rend(), separator);
auto found = foundReverse.base();
auto distance = std::distance(filePath.begin(), found);
return distance - 1;
}
friend bool operator==(const AbstractSourcePathView &first, const AbstractSourcePathView &second)
{
return first.toStringView() == second.toStringView();
}
private:
std::ptrdiff_t m_slashIndex = -1;
};
using SourcePathView = AbstractSourcePathView<'/'>;
using SourcePathViews = std::vector<SourcePathView>;
} // namespace QmlDesigner

View File

@@ -25,6 +25,7 @@
#pragma once
#include "projectstorageids.h"
#include "storagecacheentry.h"
#include "storagecachefwd.h"
@@ -51,8 +52,8 @@ class NonLockingMutex
{
public:
constexpr NonLockingMutex() noexcept {}
NonLockingMutex(const NonLockingMutex&) = delete;
NonLockingMutex& operator=(const NonLockingMutex&) = delete;
NonLockingMutex(const NonLockingMutex &) = delete;
NonLockingMutex &operator=(const NonLockingMutex &) = delete;
void lock() {}
void unlock() {}
void lock_shared() {}
@@ -71,6 +72,57 @@ class StorageCache
friend StorageCache;
using ResultType = std::conditional_t<std::is_base_of<NonLockingMutex, Mutex>::value, ViewType, Type>;
using IndexDatabaseType = typename IndexType::DatabaseType;
class StorageCacheIndex
{
public:
constexpr explicit StorageCacheIndex() = default;
StorageCacheIndex(const char *) = delete;
constexpr explicit StorageCacheIndex(int id)
: id{id}
{}
constexpr explicit StorageCacheIndex(std::size_t id)
: id{static_cast<int>(id)}
{}
constexpr explicit StorageCacheIndex(std::ptrdiff_t id)
: id{static_cast<int>(id)}
{}
constexpr StorageCacheIndex operator=(std::ptrdiff_t newId)
{
id = static_cast<int>(newId);
return *this;
}
constexpr StorageCacheIndex operator+(int amount) { return StorageCacheIndex{id + amount}; }
constexpr friend bool operator==(StorageCacheIndex first, StorageCacheIndex second)
{
return first.id == second.id && first.isValid() && second.isValid();
}
constexpr friend bool operator<(StorageCacheIndex first, StorageCacheIndex second)
{
return first.id < second.id;
}
constexpr friend bool operator>=(StorageCacheIndex first, StorageCacheIndex second)
{
return first.id >= second.id;
}
constexpr bool isValid() const { return id >= 0; }
explicit operator std::size_t() const { return static_cast<std::size_t>(id); }
public:
int id = -1;
};
public:
using MutexType = Mutex;
@@ -127,7 +179,7 @@ public:
m_entries = std::move(entries);
int max_id = 0;
std::size_t max_id = 0;
auto found = std::max_element(m_entries.begin(),
m_entries.end(),
@@ -136,9 +188,9 @@ public:
});
if (found != m_entries.end())
max_id = found->id + 1;
max_id = static_cast<std::size_t>(found->id) + 1;
m_indices.resize(max_id, -1);
m_indices.resize(max_id);
updateIndices();
}
@@ -171,10 +223,10 @@ public:
return first.id < second.id;
});
int max_id = found->id + 1;
auto max_id = static_cast<std::size_t>(found->id) + 1;
if (max_id > int(m_indices.size()))
m_indices.resize(max_id, -1);
if (max_id > m_indices.size())
m_indices.resize(max_id);
CacheEntries mergedCacheEntries;
mergedCacheEntries.reserve(newCacheEntries.size() + m_entries.size());
@@ -234,8 +286,11 @@ public:
{
std::shared_lock<Mutex> sharedLock(m_mutex);
if (IndexType(m_indices.size()) > id && m_indices.at(id) >= 0)
return m_entries.at(m_indices.at(id)).value;
if (IndexType{static_cast<IndexDatabaseType>(m_indices.size())} > id) {
if (auto indirectionIndex = m_indices.at(static_cast<std::size_t>(id));
indirectionIndex.isValid())
return m_entries.at(static_cast<std::size_t>(indirectionIndex)).value;
}
sharedLock.unlock();
std::lock_guard<Mutex> exclusiveLock(m_mutex);
@@ -253,25 +308,23 @@ public:
std::vector<ResultType> values;
values.reserve(ids.size());
for (IndexType id : ids)
values.emplace_back(m_entries.at(m_indices.at(id)).value);
for (IndexType id : ids) {
values.emplace_back(
m_entries.at(static_cast<std::size_t>(m_indices.at(static_cast<std::size_t>(id)))).value);
}
return values;
}
bool isEmpty() const { return m_entries.empty() && m_indices.empty(); }
Mutex &mutex() const
{
return m_mutex;
}
Mutex &mutex() const { return m_mutex; }
private:
void updateIndices()
{
auto begin = m_entries.cbegin();
for (auto current = begin; current != m_entries.cend(); ++current)
m_indices[current->id] = std::distance(begin, current);
m_indices[static_cast<std::size_t>(current->id)] = std::distance(begin, current);
}
auto find(ViewType view)
@@ -287,32 +340,30 @@ private:
return m_entries.end();
}
void incrementLargerOrEqualIndicesByOne(IndexType newIndex)
void incrementLargerOrEqualIndicesByOne(StorageCacheIndex newIndirectionIndex)
{
std::transform(m_indices.begin(),
m_indices.end(),
m_indices.begin(),
[&] (IndexType index) {
return index >= newIndex ? ++index : index;
std::transform(m_indices.begin(), m_indices.end(), m_indices.begin(), [&](StorageCacheIndex index) {
return index >= newIndirectionIndex ? index + 1 : index;
});
}
void ensureSize(IndexType id)
void ensureSize(std::size_t size)
{
if (m_indices.size() <= std::size_t(id))
m_indices.resize(id + 1, -1);
if (m_indices.size() <= size)
m_indices.resize(size + 1);
}
auto insertEntry(const_iterator beforeIterator, ViewType view, IndexType id)
{
auto inserted = m_entries.emplace(beforeIterator, view, id);
auto newIndex = IndexType(std::distance(m_entries.begin(), inserted));
StorageCacheIndex newIndirectionIndex{std::distance(m_entries.begin(), inserted)};
incrementLargerOrEqualIndicesByOne(newIndex);
incrementLargerOrEqualIndicesByOne(newIndirectionIndex);
ensureSize(id);
m_indices.at(id) = newIndex;
auto indirectionIndex = static_cast<std::size_t>(id);
ensureSize(indirectionIndex);
m_indices.at(indirectionIndex) = newIndirectionIndex;
return inserted;
}
@@ -327,7 +378,7 @@ private:
private:
CacheEntries m_entries;
std::vector<IndexType> m_indices;
std::vector<StorageCacheIndex> m_indices;
mutable Mutex m_mutex;
Storage m_storage;
};

View File

@@ -36,6 +36,11 @@ public:
, id(id)
{}
StorageCacheEntry(ViewType value, typename IndexType::DatabaseType id)
: value(value)
, id{id}
{}
operator ViewType() const { return value; }
friend bool operator==(const StorageCacheEntry &first, const StorageCacheEntry &second)
{

View File

@@ -56,6 +56,11 @@ HEADERS += \
$$PWD/designercore/metainfo/storagecache.h \
$$PWD/designercore/metainfo/storagecacheentry.h \
$$PWD/designercore/metainfo/storagecachefwd.h \
$$PWD/designercore/metainfo/sourceid.h \
$$PWD/designercore/metainfo/sourcepathcontextid.h \
$$PWD/designercore/metainfo/sourcepath.h \
$$PWD/designercore/metainfo/sourcepathview.h \
$$PWD/designercore/metainfo/sourcepathcache.h \
$$PWD/designercore/model/model_p.h \
$$PWD/designercore/include/qmldesignercorelib_global.h \
$$PWD/designercore/model/internalbindingproperty.h \
@@ -73,4 +78,7 @@ HEADERS += \
$$PWD/designercore/include/signalhandlerproperty.h \
$$PWD/designercore/include/variantproperty.h \
$$PWD/designercore/rewritertransaction.h \
$$PWD/components/listmodeleditor/listmodeleditormodel.h
$$PWD/components/listmodeleditor/listmodeleditormodel.h \
$$PWD/designercore/metainfo/projectstorage.h \
$$PWD/designercore/include/projectstorageids.h \
$$PWD/designercore/metainfo/projectstoragetypes.h

View File

@@ -47,6 +47,7 @@
#include <filepathview.h>
#include <filestatus.h>
#include <includesearchpath.h>
#include <metainfo/projectstoragetypes.h>
#include <modelnode.h>
#include <nativefilepath.h>
#include <pchpaths.h>
@@ -1570,6 +1571,13 @@ std::ostream &operator<<(std::ostream &out, const VariantProperty &property)
return out << "(" << property.parentModelNode() << ", " << property.name() << ", "
<< property.value() << ")";
}
namespace Sources {
std::ostream &operator<<(std::ostream &out, const SourceContext &sourceContext)
{
return out << "(" << sourceContext.id << ", " << sourceContext.value << ")";
}
} // namespace Sources
namespace Internal {
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageImageEntry &entry)

View File

@@ -374,10 +374,24 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag);
namespace QmlDesigner {
class ModelNode;
class VariantProperty;
template<auto Type, typename InternalIntergerType>
class BasicId;
std::ostream &operator<<(std::ostream &out, const ModelNode &node);
std::ostream &operator<<(std::ostream &out, const VariantProperty &property);
template<auto Type, typename InternalIntergerType>
std::ostream &operator<<(std::ostream &out, const BasicId<Type, InternalIntergerType> &id)
{
return out << "(" << &id << ")";
}
namespace Sources {
class SourceContext;
std::ostream &operator<<(std::ostream &out, const SourceContext &sourceContext);
} // namespace Sources
namespace Internal {
class ImageCacheStorageImageEntry;
class ImageCacheStorageIconEntry;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "googletest.h"
#include "sqlitedatabasemock.h"
#include <metainfo/projectstoragetypes.h>
#include <projectstorageids.h>
class ProjectStorageMock
{
public:
ProjectStorageMock(SqliteDatabaseMock &databaseMock)
: databaseMock{databaseMock}
{}
MOCK_METHOD1(fetchSourceContextId,
QmlDesigner::SourceContextId(Utils::SmallStringView SourceContextPath));
MOCK_METHOD2(fetchSourceId,
QmlDesigner::SourceId(QmlDesigner::SourceContextId SourceContextId,
Utils::SmallStringView sourceName));
MOCK_METHOD1(fetchSourceContextIdUnguarded,
QmlDesigner::SourceContextId(Utils::SmallStringView SourceContextPath));
MOCK_METHOD2(fetchSourceIdUnguarded,
QmlDesigner::SourceId(QmlDesigner::SourceContextId SourceContextId,
Utils::SmallStringView sourceName));
MOCK_METHOD1(fetchSourceContextPath,
Utils::PathString(QmlDesigner::SourceContextId sourceContextId));
MOCK_METHOD1(fetchSourceNameAndSourceContextId,
QmlDesigner::Sources::SourceNameAndSourceContextId(QmlDesigner::SourceId sourceId));
MOCK_METHOD0(fetchAllSourceContexts, std::vector<QmlDesigner::Sources::SourceContext>());
MOCK_METHOD0(fetchAllSources, std::vector<ClangBackEnd::Sources::Source>());
SqliteDatabaseMock &database() { return databaseMock; }
SqliteDatabaseMock &databaseMock;
};

View File

@@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include <metainfo/sourcepath.h>
namespace {
TEST(SourcePath, CreateFromPathString)
{
QmlDesigner::SourcePath sourcePath{Utils::PathString{"/file/pathOne"}};
ASSERT_THAT(sourcePath.directory(), "/file");
ASSERT_THAT(sourcePath.name(), "pathOne");
}
TEST(FilePath, CreateFromDirectoryAndFileName)
{
QmlDesigner::SourcePath sourcePath{Utils::PathString{"/file"}, Utils::PathString{"pathOne"}};
ASSERT_THAT(sourcePath.directory(), "/file");
ASSERT_THAT(sourcePath.name(), "pathOne");
ASSERT_THAT(sourcePath.path(), "/file/pathOne");
}
TEST(FilePath, CreateFromCString)
{
QmlDesigner::SourcePath sourcePath{"/file/pathOne"};
ASSERT_THAT(sourcePath.directory(), "/file");
ASSERT_THAT(sourcePath.name(), "pathOne");
}
TEST(FilePath, CreateFromFilePathView)
{
QmlDesigner::SourcePath sourcePath{QmlDesigner::SourcePathView{"/file/pathOne"}};
ASSERT_THAT(sourcePath.directory(), "/file");
ASSERT_THAT(sourcePath.name(), "pathOne");
}
TEST(FilePath, CreateFromQString)
{
QmlDesigner::SourcePath sourcePath{QString{"/file/pathOne"}};
ASSERT_THAT(sourcePath.directory(), "/file");
ASSERT_THAT(sourcePath.name(), "pathOne");
}
TEST(FilePath, DefaultFilePath)
{
QmlDesigner::SourcePath sourcePath;
ASSERT_THAT(sourcePath.directory(), "");
ASSERT_THAT(sourcePath.name(), "");
}
TEST(FilePath, EmptyFilePath)
{
QmlDesigner::SourcePath sourcePath("");
ASSERT_THAT(sourcePath.directory(), "");
ASSERT_THAT(sourcePath.name(), "");
}
}

View File

@@ -0,0 +1,123 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include <metainfo/sourcepathview.h>
namespace {
TEST(SourcePathView, FilePathSlashForEmptyPath)
{
QmlDesigner::SourcePathView filePath("");
ASSERT_THAT(filePath.slashIndex(), -1);
}
TEST(SourcePathView, FilePathSlashForSingleSlash)
{
QmlDesigner::SourcePathView filePath("/");
ASSERT_THAT(filePath.slashIndex(), 0);
}
TEST(SourcePathView, FilePathSlashForFileInRoot)
{
QmlDesigner::SourcePathView filePath("/file.h");
ASSERT_THAT(filePath.slashIndex(), 0);
}
TEST(SourcePathView, FilePathSlashForSomeLongerPath)
{
QmlDesigner::SourcePathView filePath("/path/to/some/file.h");
ASSERT_THAT(filePath.slashIndex(), 13);
}
TEST(SourcePathView, FilePathSlashForFileNameOnly)
{
QmlDesigner::SourcePathView filePath("file.h");
ASSERT_THAT(filePath.slashIndex(), -1);
}
TEST(SourcePathView, DirectoryPathForEmptyPath)
{
QmlDesigner::SourcePathView filePath("");
ASSERT_THAT(filePath.directory(), "");
}
TEST(SourcePathView, DirectoryPathForSingleSlashPath)
{
QmlDesigner::SourcePathView filePath{"/"};
ASSERT_THAT(filePath.directory(), "");
}
TEST(SourcePathView, DirectoryPathForLongerPath)
{
QmlDesigner::SourcePathView filePath{"/path/to/some/file.h"};
ASSERT_THAT(filePath.directory(), "/path/to/some");
}
TEST(SourcePathView, DirectoryPathForFileNameOnly)
{
QmlDesigner::SourcePathView filePath{"file.h"};
ASSERT_THAT(filePath.directory(), IsEmpty());
}
TEST(SourcePathView, FileNameForEmptyPath)
{
QmlDesigner::SourcePathView filePath("");
ASSERT_THAT(filePath.name(), "");
}
TEST(SourcePathView, FileNameForSingleSlashPath)
{
QmlDesigner::SourcePathView filePath{"/"};
ASSERT_THAT(filePath.name(), "");
}
TEST(SourcePathView, FileNameForLongerPath)
{
QmlDesigner::SourcePathView filePath{"/path/to/some/file.h"};
ASSERT_THAT(filePath.name(), "file.h");
}
TEST(SourcePathView, FileNameForFileNameOnly)
{
QmlDesigner::SourcePathView filePath{"file.h"};
ASSERT_THAT(filePath.name(), "file.h");
}
}

View File

@@ -28,6 +28,7 @@
#include "googletest.h"
#include "sqlitereadstatementmock.h"
#include "sqlitereadwritestatementmock.h"
#include "sqlitetransactionbackendmock.h"
#include "sqlitewritestatementmock.h"
@@ -43,6 +44,8 @@ public:
template<int ResultCount>
using ReadStatement = NiceMock<SqliteReadStatementMock<ResultCount>>;
using WriteStatement = NiceMock<SqliteWriteStatementMock>;
template<int ResultCount>
using ReadWriteStatement = NiceMock<SqliteReadWriteStatementMock<ResultCount>>;
MOCK_METHOD(void, prepare, (Utils::SmallStringView sqlStatement), ());

View File

@@ -29,11 +29,13 @@
#include <cpptools/usages.h>
#include <filepathstoragesources.h>
#include <metainfo/projectstoragetypes.h>
#include <pchpaths.h>
#include <projectpartartefact.h>
#include <projectpartcontainer.h>
#include <projectpartpch.h>
#include <projectpartstoragestructs.h>
#include <projectstorageids.h>
#include <sourceentry.h>
#include <sourcelocations.h>
#include <sqliteblob.h>
@@ -160,6 +162,33 @@ public:
valuesReturnPrecompiledHeaderTimeStamps,
(int projectPartId));
MOCK_METHOD(QmlDesigner::TypeId, valueReturnsTypeId, (Utils::SmallStringView name), ());
MOCK_METHOD(QmlDesigner::TypeId, valueWithTransactionReturnsTypeId, (long long, long long), ());
MOCK_METHOD(QmlDesigner::PropertyDeclarationId,
valueWithTransactionReturnsPropertyDeclarationId,
(long long, Utils::SmallStringView),
());
MOCK_METHOD((std::tuple<QmlDesigner::PropertyDeclarationId, QmlDesigner::TypeId>),
valueReturnsPropertyDeclaration,
(long long, Utils::SmallStringView),
());
MOCK_METHOD(std::vector<QmlDesigner::Sources::SourceContext>,
valuesReturnSourcesSourceContexts,
(std::size_t),
());
MOCK_METHOD(std::vector<QmlDesigner::Sources::Source>, valuesReturnSourcesSources, (std::size_t), ());
MOCK_METHOD(QmlDesigner::Sources::SourceNameAndSourceContextId,
valueReturnSourcesSourceNameAndSourceContextId,
(int) );
MOCK_METHOD(QmlDesigner::SourceContextId, valueReturnsSourceContextId, (Utils::SmallStringView), ());
MOCK_METHOD(QmlDesigner::SourceContextId, valueWithTransactionReturnsSourceContextId, (int), ());
MOCK_METHOD(QmlDesigner::SourceId, valueReturnsSourceId, (int, Utils::SmallStringView), ());
template<typename ResultType, typename... QueryTypes>
auto optionalValue(const QueryTypes &...queryValues)
{
@@ -199,8 +228,38 @@ public:
template<typename ResultType, typename... QueryTypes>
auto value(const QueryTypes &...queryValues)
{
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::value does not handle result type!");
if constexpr (std::is_same_v<ResultType, QmlDesigner::TypeId>)
return valueReturnsTypeId(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::PropertyDeclarationId>)
return valueReturnsPropertyDeclarationId(queryValues...);
else if constexpr (std::is_same_v<ResultType,
std::tuple<QmlDesigner::PropertyDeclarationId, QmlDesigner::TypeId>>)
return valueReturnsPropertyDeclaration(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::Sources::SourceNameAndSourceContextId>)
return valueReturnSourcesSourceNameAndSourceContextId(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::SourceContextId>)
return valueReturnsSourceContextId(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::SourceId>)
return valueReturnsSourceId(queryValues...);
else
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::value does not handle result type!");
}
template<typename ResultType, typename... QueryTypes>
auto valueWithTransaction(const QueryTypes &...queryValues)
{
if constexpr (std::is_same_v<ResultType, QmlDesigner::TypeId>)
return valueWithTransactionReturnsTypeId(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::PropertyDeclarationId>)
return valueWithTransactionReturnsPropertyDeclarationId(queryValues...);
else if constexpr (std::is_same_v<ResultType,
std::tuple<QmlDesigner::PropertyDeclarationId, QmlDesigner::TypeId>>)
return valueReturnsPropertyDeclaration(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::SourceContextId>)
return valueWithTransactionReturnsSourceContextId(queryValues...);
else
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::value does not handle result type!");
}
template<typename ResultType, typename... QueryTypes>
@@ -240,11 +299,29 @@ public:
return valuesReturnSourceEntries(reserveSize, queryValues...);
else if constexpr (std::is_same_v<ResultType, SourceTimeStamp>)
return valuesReturnSourceTimeStamps(reserveSize, queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::Sources::SourceContext>)
return valuesReturnSourcesSourceContexts(reserveSize);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::Sources::Source>)
return valuesReturnSourcesSources(reserveSize);
else
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::values does not handle result type!");
}
template<typename ResultType, typename... QueryTypes>
auto range(const QueryTypes &...queryValues)
{
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::values does not handle result type!");
}
template<typename ResultType, typename... QueryTypes>
auto rangeWithTransaction(const QueryTypes &...queryValues)
{
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::values does not handle result type!");
}
public:
Utils::SmallString sqlStatement;
};

View File

@@ -0,0 +1,35 @@
/****************************************************************************
**
** Copyright (C) 2021 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 "sqlitereadwritestatementmock.h"
#include "sqlitedatabasemock.h"
SqliteReadWriteStatementMockBase::SqliteReadWriteStatementMockBase(Utils::SmallStringView sqlStatement,
SqliteDatabaseMock &databaseMock)
: sqlStatement{sqlStatement}
{
databaseMock.prepare(sqlStatement);
}

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** Copyright (C) 2021 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 <projectstorageids.h>
#include <sqlite/sqlitevalue.h>
#include <utils/optional.h>
#include <utils/smallstring.h>
#include <cstdint>
#include <tuple>
#include <vector>
class SqliteDatabaseMock;
class SqliteReadWriteStatementMockBase
{
public:
SqliteReadWriteStatementMockBase() = default;
SqliteReadWriteStatementMockBase(Utils::SmallStringView sqlStatement,
SqliteDatabaseMock &databaseMock);
MOCK_METHOD(QmlDesigner::TypeId,
valueReturnsTypeId,
(Utils::SmallStringView name, long long id, long long),
());
MOCK_METHOD(QmlDesigner::PropertyDeclarationId,
valueReturnsPropertyDeclarationId,
(long long, Utils::SmallStringView, long long),
());
template<typename ResultType, typename... QueryTypes>
auto optionalValue(const QueryTypes &...queryValues)
{
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::value does not handle result type!");
}
template<typename ResultType, typename... QueryTypes>
auto value(const QueryTypes &...queryValues)
{
if constexpr (std::is_same_v<ResultType, QmlDesigner::TypeId>)
return valueReturnsTypeId(queryValues...);
else if constexpr (std::is_same_v<ResultType, QmlDesigner::PropertyDeclarationId>)
return valueReturnsPropertyDeclarationId(queryValues...);
else
static_assert(!std::is_same_v<ResultType, ResultType>,
"SqliteReadStatementMock::value does not handle result type!");
}
public:
Utils::SmallString sqlStatement;
};
template<int ResultCount>
class SqliteReadWriteStatementMock : public SqliteReadWriteStatementMockBase
{
public:
using SqliteReadWriteStatementMockBase::SqliteReadWriteStatementMockBase;
};

View File

@@ -42,6 +42,8 @@ public:
MOCK_METHOD(void, write, (Utils::SmallStringView), ());
MOCK_METHOD(void, write, (long long), ());
MOCK_METHOD(void, write, (long long, long long), ());
MOCK_METHOD(void, write, (Utils::SmallStringView, long long), ());
MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView), ());
MOCK_METHOD(void, write, (long long, Utils::SmallStringView), ());
MOCK_METHOD(void,

View File

@@ -25,31 +25,30 @@
#include "googletest.h"
#include "mockfilepathstorage.h"
#include "mockmutex.h"
#include "projectstoragemock.h"
#include "sqlitedatabasemock.h"
#include <metainfo/storagecache.h>
#include <projectstorageids.h>
#include <utils/smallstringio.h>
namespace {
using QmlDesigner::SourceContextId;
using QmlDesigner::StorageCacheException;
using uint64 = unsigned long long;
using Utils::compare;
using Utils::reverseCompare;
class StorageAdapter
{
public:
auto fetchId(Utils::SmallStringView view) { return storage.fetchDirectoryId(view); }
auto fetchId(Utils::SmallStringView view) { return storage.fetchSourceContextId(view); }
auto fetchValue(int id) { return storage.fetchDirectoryPath(id); }
auto fetchValue(SourceContextId id) { return storage.fetchSourceContextPath(id); }
MockFilePathStorage &storage;
ProjectStorageMock &storage;
};
auto less(Utils::SmallStringView first, Utils::SmallStringView second) -> bool
@@ -58,10 +57,14 @@ auto less(Utils::SmallStringView first, Utils::SmallStringView second) -> bool
};
using CacheWithMockLocking = QmlDesigner::
StorageCache<Utils::PathString, Utils::SmallStringView, int, StorageAdapter, NiceMock<MockMutex>, less>;
StorageCache<Utils::PathString, Utils::SmallStringView, SourceContextId, StorageAdapter, NiceMock<MockMutex>, less>;
using CacheWithoutLocking = QmlDesigner::
StorageCache<Utils::PathString, Utils::SmallStringView, int, StorageAdapter, NiceMock<MockMutexNonLocking>, less>;
using CacheWithoutLocking = QmlDesigner::StorageCache<Utils::PathString,
Utils::SmallStringView,
SourceContextId,
StorageAdapter,
NiceMock<MockMutexNonLocking>,
less>;
template<typename Cache>
class StorageCache : public testing::Test
@@ -76,21 +79,22 @@ protected:
return reverseCompare(f, l) < 0;
});
ON_CALL(this->mockStorage, fetchDirectoryId(Eq("foo"))).WillByDefault(Return(42));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq("bar"))).WillByDefault(Return(43));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq("poo"))).WillByDefault(Return(44));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq("taa"))).WillByDefault(Return(45));
ON_CALL(this->mockStorage, fetchDirectoryPath(41)).WillByDefault(Return(Utils::PathString("bar")));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq(filePath1))).WillByDefault(Return(0));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq(filePath2))).WillByDefault(Return(1));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq(filePath3))).WillByDefault(Return(2));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq(filePath4))).WillByDefault(Return(3));
ON_CALL(this->mockStorage, fetchDirectoryId(Eq(filePath5))).WillByDefault(Return(4));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq("foo"))).WillByDefault(Return(id42));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq("bar"))).WillByDefault(Return(id43));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq("poo"))).WillByDefault(Return(id44));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq("taa"))).WillByDefault(Return(id45));
ON_CALL(this->mockStorage, fetchSourceContextPath(this->id41))
.WillByDefault(Return(Utils::PathString("bar")));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq(filePath1))).WillByDefault(Return(id1));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq(filePath2))).WillByDefault(Return(id2));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq(filePath3))).WillByDefault(Return(id3));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq(filePath4))).WillByDefault(Return(id4));
ON_CALL(this->mockStorage, fetchSourceContextId(Eq(filePath5))).WillByDefault(Return(id5));
}
protected:
NiceMock<SqliteDatabaseMock> databaseMock;
NiceMock<MockFilePathStorage> mockStorage{databaseMock};
NiceMock<ProjectStorageMock> mockStorage{databaseMock};
StorageAdapter storageAdapter{mockStorage};
Cache cache{storageAdapter};
typename Cache::MutexType &mockMutex = cache.mutex();
@@ -99,16 +103,18 @@ protected:
Utils::PathString filePath3{"/file/pathThree"};
Utils::PathString filePath4{"/file/pathFour"};
Utils::PathString filePath5{"/file/pathFife"};
Utils::PathStringVector filePaths{filePath1,
filePath2,
filePath3,
filePath4,
filePath5};
Utils::PathStringVector reverseFilePaths{filePath1,
filePath2,
filePath3,
filePath4,
filePath5};
Utils::PathStringVector filePaths{filePath1, filePath2, filePath3, filePath4, filePath5};
Utils::PathStringVector reverseFilePaths{filePath1, filePath2, filePath3, filePath4, filePath5};
SourceContextId id1{0};
SourceContextId id2{1};
SourceContextId id3{2};
SourceContextId id4{3};
SourceContextId id5{4};
SourceContextId id41{41};
SourceContextId id42{42};
SourceContextId id43{43};
SourceContextId id44{44};
SourceContextId id45{45};
};
using CacheTypes = ::testing::Types<CacheWithMockLocking, CacheWithoutLocking>;
@@ -118,7 +124,7 @@ TYPED_TEST(StorageCache, AddFilePath)
{
auto id = this->cache.id(this->filePath1);
ASSERT_THAT(id, 0);
ASSERT_THAT(id, this->id1);
}
TYPED_TEST(StorageCache, AddSecondFilePath)
@@ -127,7 +133,7 @@ TYPED_TEST(StorageCache, AddSecondFilePath)
auto id = this->cache.id(this->filePath2);
ASSERT_THAT(id, 1);
ASSERT_THAT(id, this->id2);
}
TYPED_TEST(StorageCache, AddDuplicateFilePath)
@@ -136,7 +142,7 @@ TYPED_TEST(StorageCache, AddDuplicateFilePath)
auto id = this->cache.id(this->filePath1);
ASSERT_THAT(id, 0);
ASSERT_THAT(id, this->id1);
}
TYPED_TEST(StorageCache, AddDuplicateFilePathBetweenOtherEntries)
@@ -148,14 +154,14 @@ TYPED_TEST(StorageCache, AddDuplicateFilePathBetweenOtherEntries)
auto id = this->cache.id(this->filePath3);
ASSERT_THAT(id, 2);
ASSERT_THAT(id, this->id3);
}
TYPED_TEST(StorageCache, GetFilePathForIdWithOneEntry)
{
this->cache.id(this->filePath1);
auto filePath = this->cache.value(0);
auto filePath = this->cache.value(this->id1);
ASSERT_THAT(filePath, this->filePath1);
}
@@ -167,7 +173,7 @@ TYPED_TEST(StorageCache, GetFilePathForIdWithSomeEntries)
this->cache.id(this->filePath3);
this->cache.id(this->filePath4);
auto filePath = this->cache.value(2);
auto filePath = this->cache.value(this->id3);
ASSERT_THAT(filePath, this->filePath3);
}
@@ -179,7 +185,7 @@ TYPED_TEST(StorageCache, GetAllFilePaths)
this->cache.id(this->filePath3);
this->cache.id(this->filePath4);
auto filePaths = this->cache.values({0, 1, 2, 3});
auto filePaths = this->cache.values({this->id1, this->id2, this->id3, this->id4});
ASSERT_THAT(filePaths,
ElementsAre(this->filePath1, this->filePath2, this->filePath3, this->filePath4));
@@ -189,14 +195,14 @@ TYPED_TEST(StorageCache, AddFilePaths)
{
auto ids = this->cache.ids({this->filePath1, this->filePath2, this->filePath3, this->filePath4});
ASSERT_THAT(ids, ElementsAre(0, 1, 2, 3));
ASSERT_THAT(ids, ElementsAre(this->id1, this->id2, this->id3, this->id4));
}
TYPED_TEST(StorageCache, AddFilePathsWithStorageFunction)
{
auto ids = this->cache.ids({"foo", "taa", "poo", "bar"});
ASSERT_THAT(ids, UnorderedElementsAre(42, 43, 44, 45));
ASSERT_THAT(ids, UnorderedElementsAre(this->id42, this->id43, this->id44, this->id45));
}
TYPED_TEST(StorageCache, IsEmpty)
@@ -226,10 +232,10 @@ TYPED_TEST(StorageCache, PopulateWithEmptyVector)
TYPED_TEST(StorageCache, IsNotEmptyAfterPopulateWithSomeEntries)
{
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), 0},
{this->filePath2.clone(), 3},
{this->filePath3.clone(), 2},
{this->filePath4.clone(), 5}};
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), this->id1},
{this->filePath2.clone(), this->id4},
{this->filePath3.clone(), this->id3},
{this->filePath4.clone(), SourceContextId{5}}};
this->cache.uncheckedPopulate(std::move(entries));
@@ -238,33 +244,33 @@ TYPED_TEST(StorageCache, IsNotEmptyAfterPopulateWithSomeEntries)
TYPED_TEST(StorageCache, GetEntryAfterPopulateWithSomeEntries)
{
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), 0},
{this->filePath2.clone(), 1},
{this->filePath3.clone(), 7},
{this->filePath4.clone(), 3}};
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), this->id1},
{this->filePath2.clone(), this->id2},
{this->filePath3.clone(), SourceContextId{7}},
{this->filePath4.clone(), this->id4}};
this->cache.uncheckedPopulate(std::move(entries));
auto value = this->cache.value(7);
auto value = this->cache.value(SourceContextId{7});
ASSERT_THAT(value, this->filePath3);
}
TYPED_TEST(StorageCache, EntriesHaveUniqueIds)
{
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), 0},
{this->filePath2.clone(), 1},
{this->filePath3.clone(), 2},
{this->filePath4.clone(), 2}};
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), this->id1},
{this->filePath2.clone(), this->id2},
{this->filePath3.clone(), this->id3},
{this->filePath4.clone(), this->id3}};
ASSERT_THROW(this->cache.populate(std::move(entries)), StorageCacheException);
}
TYPED_TEST(StorageCache, MultipleEntries)
{
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), 0},
{this->filePath1.clone(), 1},
{this->filePath3.clone(), 2},
{this->filePath4.clone(), 3}};
typename TypeParam::CacheEntries entries{{this->filePath1.clone(), this->id1},
{this->filePath1.clone(), this->id2},
{this->filePath3.clone(), this->id3},
{this->filePath4.clone(), this->id4}};
ASSERT_THROW(this->cache.populate(std::move(entries)), StorageCacheException);
}
@@ -288,7 +294,7 @@ TYPED_TEST(StorageCache, IdWithStorageFunctionIsReadAndWriteLockedForUnknownEntr
EXPECT_CALL(this->mockMutex, lock_shared());
EXPECT_CALL(this->mockMutex, unlock_shared());
EXPECT_CALL(this->mockMutex, lock());
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("foo")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("foo")));
EXPECT_CALL(this->mockMutex, unlock());
this->cache.id("foo");
@@ -302,7 +308,7 @@ TYPED_TEST(StorageCache, IdWithStorageFunctionIsReadLockedForKnownEntry)
EXPECT_CALL(this->mockMutex, lock_shared());
EXPECT_CALL(this->mockMutex, unlock_shared());
EXPECT_CALL(this->mockMutex, lock()).Times(0);
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("foo"))).Times(0);
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("foo"))).Times(0);
EXPECT_CALL(this->mockMutex, unlock()).Times(0);
this->cache.id("foo");
@@ -363,29 +369,29 @@ TYPED_TEST(StorageCache, ValueWithStorageFunctionIsReadAndWriteLockedForUnknownI
EXPECT_CALL(this->mockMutex, lock_shared());
EXPECT_CALL(this->mockMutex, unlock_shared());
EXPECT_CALL(this->mockMutex, lock());
EXPECT_CALL(this->mockStorage, fetchDirectoryPath(Eq(41)));
EXPECT_CALL(this->mockStorage, fetchSourceContextPath(Eq(this->id41)));
EXPECT_CALL(this->mockMutex, unlock());
this->cache.value(41);
this->cache.value(this->id41);
}
TYPED_TEST(StorageCache, ValueWithStorageFunctionIsReadLockedForKnownId)
{
InSequence s;
this->cache.value(41);
this->cache.value(this->id41);
EXPECT_CALL(this->mockMutex, lock_shared());
EXPECT_CALL(this->mockMutex, unlock_shared());
EXPECT_CALL(this->mockMutex, lock()).Times(0);
EXPECT_CALL(this->mockStorage, fetchDirectoryPath(Eq(41))).Times(0);
EXPECT_CALL(this->mockStorage, fetchSourceContextPath(Eq(this->id41))).Times(0);
EXPECT_CALL(this->mockMutex, unlock()).Times(0);
this->cache.value(41);
this->cache.value(this->id41);
}
TYPED_TEST(StorageCache, IdWithStorageFunctionWhichHasNoEntryIsCallingStorageFunction)
{
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("foo")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("foo")));
this->cache.id("foo");
}
@@ -394,7 +400,7 @@ TYPED_TEST(StorageCache, IdWithStorageFunctionWhichHasEntryIsNotCallingStorageFu
{
this->cache.id("foo");
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("foo"))).Times(0);
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("foo"))).Times(0);
this->cache.id("foo");
}
@@ -405,14 +411,14 @@ TYPED_TEST(StorageCache, IndexOfIdWithStorageFunctionWhichHasEntry)
auto index = this->cache.id("foo");
ASSERT_THAT(index, 42);
ASSERT_THAT(index, this->id42);
}
TYPED_TEST(StorageCache, IndexOfIdWithStorageFunctionWhichHasNoEntry)
{
auto index = this->cache.id("foo");
ASSERT_THAT(index, 42);
ASSERT_THAT(index, this->id42);
}
TYPED_TEST(StorageCache, GetEntryByIndexAfterInsertingByCustomIndex)
@@ -424,34 +430,35 @@ TYPED_TEST(StorageCache, GetEntryByIndexAfterInsertingByCustomIndex)
ASSERT_THAT(value, Eq("foo"));
}
TYPED_TEST(StorageCache, CallFetchDirectoryPathForLowerIndex)
TYPED_TEST(StorageCache, CallFetchSourceContextPathForLowerIndex)
{
auto index = this->cache.id("foo");
SourceContextId lowerIndex{&index - 1};
EXPECT_CALL(this->mockStorage, fetchDirectoryPath(Eq(index - 1)));
EXPECT_CALL(this->mockStorage, fetchSourceContextPath(Eq(lowerIndex)));
this->cache.value(index - 1);
this->cache.value(lowerIndex);
}
TYPED_TEST(StorageCache, CallFetchDirectoryPathForUnknownIndex)
TYPED_TEST(StorageCache, CallFetchSourceContextPathForUnknownIndex)
{
EXPECT_CALL(this->mockStorage, fetchDirectoryPath(Eq(0)));
EXPECT_CALL(this->mockStorage, fetchSourceContextPath(Eq(this->id1)));
this->cache.value(0);
this->cache.value(this->id1);
}
TYPED_TEST(StorageCache, FetchDirectoryPathForUnknownIndex)
TYPED_TEST(StorageCache, FetchSourceContextPathForUnknownIndex)
{
auto value = this->cache.value(41);
auto value = this->cache.value(this->id41);
ASSERT_THAT(value, Eq("bar"));
}
TYPED_TEST(StorageCache, AddCalls)
{
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("foo")));
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("bar")));
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("poo")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("foo")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("bar")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("poo")));
this->cache.add({"foo", "bar", "poo"});
}
@@ -460,8 +467,8 @@ TYPED_TEST(StorageCache, AddCallsOnlyForNewValues)
{
this->cache.add({"foo", "poo"});
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("taa")));
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("bar")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("taa")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("bar")));
this->cache.add({"foo", "bar", "poo", "taa"});
}
@@ -508,12 +515,12 @@ TYPED_TEST(StorageCache, FetchIdsFromStorageCalls)
EXPECT_CALL(this->mockMutex, lock_shared());
EXPECT_CALL(this->mockMutex, unlock_shared());
EXPECT_CALL(this->mockMutex, lock());
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("foo")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("foo")));
EXPECT_CALL(this->mockMutex, unlock());
EXPECT_CALL(this->mockMutex, lock_shared());
EXPECT_CALL(this->mockMutex, unlock_shared());
EXPECT_CALL(this->mockMutex, lock());
EXPECT_CALL(this->mockStorage, fetchDirectoryId(Eq("bar")));
EXPECT_CALL(this->mockStorage, fetchSourceContextId(Eq("bar")));
EXPECT_CALL(this->mockMutex, unlock());
this->cache.ids({"foo", "bar"});

View File

@@ -144,7 +144,11 @@ SOURCES += \
sqlstatementbuilder-test.cpp \
createtablesqlstatementbuilder-test.cpp \
sqlitereadstatementmock.cpp \
sqlitewritestatementmock.cpp
sqlitewritestatementmock.cpp \
sqlitereadwritestatementmock.cpp \
sourcepath-test.cpp \
sourcepathview-test.cpp \
projectstorage-test.cpp
!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += matchingtext-test.cpp
@@ -319,7 +323,9 @@ HEADERS += \
sqlitereadstatementmock.h \
sqlitestatementmock.h \
sqlitetransactionbackendmock.h \
sqlitewritestatementmock.h
sqlitewritestatementmock.h \
sqlitereadwritestatementmock.h \
projectstoragemock.h
!isEmpty(LIBCLANG_LIBS) {