QmlDesigner: Add Import Synchronization

Task-number: QDS-4460
Change-Id: Icd419baa7881fe882539f10d335ab6b3d36bc7b5
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-06-01 17:46:49 +02:00
parent 029aecef1e
commit fc0668882d
7 changed files with 627 additions and 2 deletions

View File

@@ -82,7 +82,8 @@ enum class BasicIdType {
StorageCacheIndex, StorageCacheIndex,
FunctionDeclaration, FunctionDeclaration,
SignalDeclaration, SignalDeclaration,
EnumerationDeclaration EnumerationDeclaration,
Import
}; };
using TypeId = BasicId<BasicIdType::Type>; using TypeId = BasicId<BasicIdType::Type>;
@@ -106,4 +107,7 @@ using SourceContextIds = std::vector<SourceContextId>;
using SourceId = BasicId<BasicIdType::SourceId, int>; using SourceId = BasicId<BasicIdType::SourceId, int>;
using SourceIds = std::vector<SourceId>; using SourceIds = std::vector<SourceId>;
using ImportId = BasicId<BasicIdType::Import>;
using ImportIds = std::vector<ImportId>;
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -75,6 +75,16 @@ public:
transaction.commit(); transaction.commit();
} }
void synchronizeImports(Storage::Imports imports)
{
Sqlite::ImmediateTransaction transaction{database};
synchronizeImportsAndUpdatesImportIds(imports);
synchronizeImportDependencies(createSortedImportDependecies(imports));
transaction.commit();
}
TypeId upsertType(Utils::SmallStringView name, TypeId upsertType(Utils::SmallStringView name,
TypeId prototypeId, TypeId prototypeId,
Storage::TypeAccessSemantics accessSemantics, Storage::TypeAccessSemantics accessSemantics,
@@ -255,7 +265,149 @@ public:
return writeSourceId(sourceContextId, sourceName); return writeSourceId(sourceContextId, sourceName);
} }
auto fetchAllImports() const
{
Storage::Imports imports;
imports.reserve(128);
auto callback = [&](Utils::SmallStringView name, int version, int sourceId, long long importId) {
auto &lastImport = imports.emplace_back(name,
Storage::VersionNumber{version},
SourceId{sourceId});
lastImport.importDependencies = selectImportsForThatDependentOnThisImportIdStatement
.template values<Storage::BasicImport>(6, importId);
return Sqlite::CallbackControl::Continue;
};
selectAllImportsStatement.readCallbackWithTransaction(callback);
return imports;
}
private: private:
struct ImportDependency
{
ImportDependency(ImportId id, ImportId dependencyId)
: id{id}
, dependencyId{dependencyId}
{}
ImportDependency(long long id, long long dependencyId)
: id{id}
, dependencyId{dependencyId}
{}
ImportId id;
ImportId dependencyId;
friend bool operator<(ImportDependency first, ImportDependency second)
{
return std::tie(first.id, first.dependencyId) < std::tie(second.id, second.dependencyId);
}
friend bool operator==(ImportDependency first, ImportDependency second)
{
return first.id == second.id && first.dependencyId == second.dependencyId;
}
};
void synchronizeImportsAndUpdatesImportIds(Storage::Imports &imports)
{
auto compareKey = [](auto &&first, auto &&second) {
auto nameCompare = Sqlite::compare(first.name, second.name);
if (nameCompare != 0)
return nameCompare;
return first.version.version - second.version.version;
};
std::sort(imports.begin(), imports.end(), [&](auto &&first, auto &&second) {
return compareKey(first, second) < 0;
});
auto range = selectAllImportsStatement.template range<Storage::ImportView>();
auto insert = [&](Storage::Import &import) {
import.importId = insertImportStatement.template value<ImportId>(import.name,
import.version.version,
&import.sourceId);
};
auto update = [&](const Storage::ImportView &importView, Storage::Import &import) {
if (importView.sourceId.id != import.sourceId.id)
updateImportStatement.write(&importView.importId, &import.sourceId);
import.importId = importView.importId;
};
auto remove = [&](const Storage::ImportView &importView) {
deleteImportStatement.write(&importView.importId);
};
Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove);
}
std::vector<ImportDependency> createSortedImportDependecies(const Storage::Imports &imports) const
{
std::vector<ImportDependency> importDependecies;
importDependecies.reserve(imports.size() * 5);
for (const Storage::Import &import : imports) {
for (const Storage::BasicImport &importDependency : import.importDependencies) {
auto importIdForDependency = fetchImportId(importDependency);
if (!importIdForDependency)
throw ImportDoesNotExists{};
importDependecies.emplace_back(import.importId, importIdForDependency);
}
}
std::sort(importDependecies.begin(), importDependecies.end());
importDependecies.erase(std::unique(importDependecies.begin(), importDependecies.end()),
importDependecies.end());
return importDependecies;
}
void synchronizeImportDependencies(const std::vector<ImportDependency> &importDependecies)
{
auto compareKey = [](ImportDependency first, ImportDependency second) {
auto idCompare = first.id.id - second.id.id;
if (idCompare != 0)
return idCompare;
return first.dependencyId.id - second.dependencyId.id;
};
auto range = selectAllImportDependenciesStatement.template range<ImportDependency>();
auto insert = [&](ImportDependency dependency) {
insertImportDependencyStatement.write(&dependency.id, &dependency.dependencyId);
};
auto update = [](ImportDependency, ImportDependency) {};
auto remove = [&](ImportDependency dependency) {
deleteImportDependencyStatement.write(&dependency.id, &dependency.dependencyId);
};
Sqlite::insertUpdateDelete(range, importDependecies, compareKey, insert, update, remove);
}
ImportId fetchImportId(const Storage::BasicImport &import) const
{
if (import.version) {
return selectImportIdByNameAndVersionStatement
.template value<ImportId>(import.name, import.version.version);
}
return selectImportIdByNameStatement.template value<ImportId>(import.name);
}
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &sourceIds) void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &sourceIds)
{ {
auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) { auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) {
@@ -638,6 +790,8 @@ private:
if (!isInitialized) { if (!isInitialized) {
Sqlite::ExclusiveTransaction transaction{database}; Sqlite::ExclusiveTransaction transaction{database};
createImportsTable(database);
createImportDependeciesTable(database);
createSourceContextsTable(database); createSourceContextsTable(database);
createSourcesTable(database); createSourcesTable(database);
createTypesTable(database); createTypesTable(database);
@@ -791,6 +945,35 @@ private:
table.initialize(database); table.initialize(database);
} }
void createImportsTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setName("imports");
table.addColumn("importId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
auto &nameColumn = table.addColumn("name");
auto &versionColumn = table.addColumn("version");
table.addColumn("sourceId");
table.addUniqueIndex({nameColumn, versionColumn});
table.initialize(database);
}
void createImportDependeciesTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setUseWithoutRowId(true);
table.setName("importDependencies");
auto &importIdColumn = table.addColumn("importId");
auto &parentImportIdColumn = table.addColumn("parentImportId");
table.addPrimaryKeyContraint({importIdColumn, parentImportIdColumn});
table.initialize(database);
}
}; };
public: public:
@@ -974,6 +1157,28 @@ public:
database}; database};
WriteStatement deleteEnumerationDeclarationStatement{ WriteStatement deleteEnumerationDeclarationStatement{
"DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database};
mutable ReadWriteStatement<1> insertImportStatement{
"INSERT INTO imports(name, version, sourceId) VALUES(?1, ?2, ?3) RETURNING importId",
database};
WriteStatement updateImportStatement{"UPDATE imports SET sourceId=?2 WHERE importId=?1", database};
WriteStatement deleteImportStatement{"DELETE FROM imports WHERE importId=?", database};
mutable ReadStatement<1> selectImportIdByNameStatement{
"SELECT importId FROM imports WHERE name=? ORDER BY version DESC LIMIT 1", database};
mutable ReadStatement<1> selectImportIdByNameAndVersionStatement{
"SELECT importId FROM imports WHERE name=? AND version=?", database};
mutable ReadStatement<4> selectAllImportsStatement{
"SELECT name, version, sourceId, importId FROM imports ORDER BY name, version", database};
WriteStatement insertImportDependencyStatement{
"INSERT INTO importDependencies(importId, parentImportId) VALUES(?1, ?2)", database};
WriteStatement deleteImportDependencyStatement{
"DELETE FROM importDependencies WHERE importId=?1 AND parentImportId=?2", database};
mutable ReadStatement<2> selectAllImportDependenciesStatement{
"SELECT importId, parentImportId FROM importDependencies ORDER BY importId, parentImportId",
database};
mutable ReadStatement<2> selectImportsForThatDependentOnThisImportIdStatement{
"SELECT name, version FROM importDependencies JOIN imports ON "
"importDependencies.parentImportId = imports.importId WHERE importDependencies.importId=?",
database};
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -71,4 +71,10 @@ public:
const char *what() const noexcept override { return "The source id is invalid!"; } const char *what() const noexcept override { return "The source id is invalid!"; }
}; };
class ImportDoesNotExists : std::exception
{
public:
const char *what() const noexcept override { return "The simport does not exist!"; }
};
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -60,7 +60,7 @@ public:
: version{version} : version{version}
{} {}
explicit operator bool() { return version >= 0; } explicit operator bool() const { return version >= 0; }
friend bool operator==(VersionNumber first, VersionNumber second) noexcept friend bool operator==(VersionNumber first, VersionNumber second) noexcept
{ {
@@ -432,4 +432,84 @@ public:
using Types = std::vector<Type>; using Types = std::vector<Type>;
class BasicImport
{
public:
explicit BasicImport(Utils::SmallStringView name, VersionNumber version = VersionNumber{})
: name{name}
, version{version}
{}
explicit BasicImport(Utils::SmallStringView name, int version)
: name{name}
, version{version}
{}
friend bool operator==(const BasicImport &first, const BasicImport &second)
{
return first.name == second.name && first.version == second.version;
}
public:
Utils::PathString name;
VersionNumber version;
};
using BasicImports = std::vector<BasicImport>;
class Import : public BasicImport
{
public:
explicit Import(Utils::SmallStringView name,
VersionNumber version = VersionNumber{},
SourceId sourceId = SourceId{},
BasicImports importDependencies = {})
: BasicImport(name, version)
, importDependencies{std::move(importDependencies)}
, sourceId{sourceId}
{}
explicit Import(Utils::SmallStringView name, int version, int sourceId)
: BasicImport(name, version)
, sourceId{sourceId}
{}
friend bool operator==(const Import &first, const Import &second)
{
return static_cast<const BasicImport &>(first) == static_cast<const BasicImport &>(second)
&& first.sourceId == second.sourceId
&& first.importDependencies == second.importDependencies;
}
public:
BasicImports importDependencies;
SourceId sourceId;
ImportId importId;
};
using Imports = std::vector<Import>;
class ImportView
{
public:
explicit ImportView(Utils::SmallStringView name, int version, int sourceId, long long importId)
: name{name}
, version{version}
, sourceId{sourceId}
, importId{importId}
{}
friend bool operator==(const ImportView &first, const ImportView &second)
{
return first.name == second.name
&& first.version == second.version & first.sourceId == second.sourceId;
}
public:
Utils::SmallStringView name;
VersionNumber version;
SourceId sourceId;
ImportId importId;
};
} // namespace QmlDesigner::Storage } // namespace QmlDesigner::Storage

View File

@@ -1732,6 +1732,17 @@ std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumer
<< enumerationDeclaration.enumeratorDeclarations << ")"; << enumerationDeclaration.enumeratorDeclarations << ")";
} }
std::ostream &operator<<(std::ostream &out, const BasicImport &import)
{
return out << "(" << import.name << ", " << import.version << ")";
}
std::ostream &operator<<(std::ostream &out, const Import &import)
{
return out << "(" << import.name << ", " << import.version << ", " << import.sourceId << ", "
<< import.importDependencies << ")";
}
} // namespace Storage } // namespace Storage
namespace Internal { namespace Internal {

View File

@@ -407,6 +407,8 @@ class ParameterDeclaration;
class SignalDeclaration; class SignalDeclaration;
class EnumerationDeclaration; class EnumerationDeclaration;
class EnumeratorDeclaration; class EnumeratorDeclaration;
class BasicImport;
class Import;
std::ostream &operator<<(std::ostream &out, TypeAccessSemantics accessSemantics); std::ostream &operator<<(std::ostream &out, TypeAccessSemantics accessSemantics);
std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber); std::ostream &operator<<(std::ostream &out, VersionNumber versionNumber);
@@ -420,6 +422,8 @@ std::ostream &operator<<(std::ostream &out, const ParameterDeclaration &paramete
std::ostream &operator<<(std::ostream &out, const SignalDeclaration &signalDeclaration); std::ostream &operator<<(std::ostream &out, const SignalDeclaration &signalDeclaration);
std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumerationDeclaration); std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumerationDeclaration);
std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumeratorDeclaration); std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumeratorDeclaration);
std::ostream &operator<<(std::ostream &out, const BasicImport &import);
std::ostream &operator<<(std::ostream &out, const Import &import);
} // namespace Storage } // namespace Storage

View File

@@ -119,6 +119,28 @@ MATCHER_P3(IsPropertyDeclaration,
&& propertyDeclaration.traits == traits; && propertyDeclaration.traits == traits;
} }
MATCHER_P2(IsBasicImport,
name,
version,
std::string(negation ? "isn't " : "is ") + PrintToString(Storage::Import{name, version}))
{
const Storage::BasicImport &import = arg;
return import.name == name && import.version == version;
}
MATCHER_P3(IsImport,
name,
version,
sourceId,
std::string(negation ? "isn't " : "is ")
+ PrintToString(Storage::Import{name, version, sourceId}))
{
const Storage::Import &import = arg;
return import.name == name && import.version == version && &import.sourceId == &sourceId;
}
class ProjectStorage : public testing::Test class ProjectStorage : public testing::Test
{ {
public: public:
@@ -528,6 +550,24 @@ protected:
Storage::ExportedType{"Qml.Object", Storage::Version{5, 1}}}}}; Storage::ExportedType{"Qml.Object", Storage::Version{5, 1}}}}};
} }
auto createImports()
{
importSourceId1 = sourcePathCache.sourceId(importPath1);
importSourceId2 = sourcePathCache.sourceId(importPath2);
importSourceId3 = sourcePathCache.sourceId(importPath3);
return Storage::Imports{Storage::Import{"Qml", Storage::VersionNumber{2}, importSourceId1, {}},
Storage::Import{"QtQuick",
Storage::VersionNumber{},
importSourceId2,
{Storage::Import{"Qml", Storage::VersionNumber{2}}}},
Storage::Import{"/path/to",
Storage::VersionNumber{},
SourceId{},
{Storage::Import{"QtQuick"},
Storage::Import{"Qml", Storage::VersionNumber{2}}}}};
}
protected: protected:
using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>;
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
@@ -541,6 +581,12 @@ protected:
SourceId sourceId2; SourceId sourceId2;
SourceId sourceId3; SourceId sourceId3;
SourceId sourceId4; SourceId sourceId4;
QmlDesigner::SourcePathView importPath1{"/import/path1/to"};
QmlDesigner::SourcePathView importPath2{"/import/path2/to"};
QmlDesigner::SourcePathView importPath3{"/import/aaaa/to"};
SourceId importSourceId1;
SourceId importSourceId2;
SourceId importSourceId3;
}; };
TEST_F(ProjectStorageSlowTest, FetchTypeIdByName) TEST_F(ProjectStorageSlowTest, FetchTypeIdByName)
@@ -1953,4 +1999,273 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddEnumerationDeclaration)
Eq(types[0].enumerationDeclarations[2])))))); Eq(types[0].enumerationDeclarations[2]))))));
} }
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddImports)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
IsImport("/path/to", Storage::VersionNumber{}, SourceId{})));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddImportsAgain)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
IsImport("/path/to", Storage::VersionNumber{}, SourceId{})));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddMoreImports)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.push_back(Storage::Import{"QtQuick.Foo", Storage::VersionNumber{1}, importSourceId3});
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
IsImport("QtQuick.Foo", Storage::VersionNumber{1}, importSourceId3)));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddSameImportNameButDifferentVersion)
{
Storage::Imports imports{createImports()};
imports.push_back(Storage::Import{"Qml", Storage::VersionNumber{4}, importSourceId3});
storage.synchronizeImports(imports);
imports.pop_back();
imports.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}, importSourceId3});
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
IsImport("Qml", Storage::VersionNumber{3}, importSourceId3),
IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
IsImport("/path/to", Storage::VersionNumber{}, SourceId{})));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsRemoveImport)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.pop_back();
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2)));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsUpdateImport)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports[1].sourceId = importSourceId3;
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
IsImport("QtQuick", Storage::VersionNumber{}, importSourceId3),
IsImport("/path/to", Storage::VersionNumber{}, SourceId{})));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddImportDependecies)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
ElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("QtQuick",
Storage::VersionNumber{}))))));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddImportDependeciesWhichDoesNotExitsThrows)
{
Storage::Imports imports{createImports()};
imports[1].importDependencies.push_back(Storage::Import{"QmlBase", Storage::VersionNumber{2}});
ASSERT_THROW(storage.synchronizeImports(imports), QmlDesigner::ImportDoesNotExists);
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsRemovesDependeciesForRemovedImports)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
auto last = imports.back();
imports.pop_back();
storage.synchronizeImports(imports);
last.importDependencies.pop_back();
imports.push_back(last);
storage.synchronizeImports(imports);
ASSERT_THAT(storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
ElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(
IsBasicImport("QtQuick", Storage::VersionNumber{}))))));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddMoreImportDependecies)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.push_back(Storage::Import{"QmlBase", Storage::VersionNumber{2}, importSourceId1, {}});
imports[1].importDependencies.push_back(Storage::Import{"QmlBase", Storage::VersionNumber{2}});
storage.synchronizeImports(imports);
ASSERT_THAT(
storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QmlBase", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("QmlBase", Storage::VersionNumber{2})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("QtQuick", Storage::VersionNumber{}))))));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsAddMoreImportDependeciesWithDifferentVersionNumber)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}, importSourceId1, {}});
imports[1].importDependencies.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}});
storage.synchronizeImports(imports);
ASSERT_THAT(
storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("Qml", Storage::VersionNumber{3}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("Qml", Storage::VersionNumber{3})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("QtQuick", Storage::VersionNumber{}))))));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsDependencyGetsHighestVersionIfNoVersionIsSupplied)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}, importSourceId1, {}});
imports[1].importDependencies.push_back(Storage::Import{"Qml"});
storage.synchronizeImports(imports);
ASSERT_THAT(
storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("Qml", Storage::VersionNumber{3}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("Qml", Storage::VersionNumber{3})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("QtQuick", Storage::VersionNumber{}))))));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsDependencyGetsOnlyTheHighestDependency)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.push_back(Storage::Import{"Qml", Storage::VersionNumber{1}, importSourceId1, {}});
imports[1].importDependencies.push_back(Storage::Import{"Qml"});
storage.synchronizeImports(imports);
ASSERT_THAT(
storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("Qml", Storage::VersionNumber{1}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("QtQuick", Storage::VersionNumber{}))))));
}
TEST_F(ProjectStorageSlowTest, SynchronizeImportsDependencyRemoveDuplicateDependencies)
{
Storage::Imports imports{createImports()};
storage.synchronizeImports(imports);
imports.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}, importSourceId1, {}});
imports[2].importDependencies.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}});
imports[2].importDependencies.push_back(Storage::Import{"Qml", Storage::VersionNumber{2}});
imports[2].importDependencies.push_back(Storage::Import{"Qml", Storage::VersionNumber{3}});
imports[2].importDependencies.push_back(Storage::Import{"Qml", Storage::VersionNumber{2}});
storage.synchronizeImports(imports);
ASSERT_THAT(
storage.fetchAllImports(),
UnorderedElementsAre(
AllOf(IsImport("Qml", Storage::VersionNumber{2}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("Qml", Storage::VersionNumber{3}, importSourceId1),
Field(&Storage::Import::importDependencies, IsEmpty())),
AllOf(IsImport("QtQuick", Storage::VersionNumber{}, importSourceId2),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2})))),
AllOf(IsImport("/path/to", Storage::VersionNumber{}, SourceId{}),
Field(&Storage::Import::importDependencies,
UnorderedElementsAre(IsBasicImport("Qml", Storage::VersionNumber{2}),
IsBasicImport("Qml", Storage::VersionNumber{3}),
IsBasicImport("QtQuick", Storage::VersionNumber{}))))));
}
} // namespace } // namespace