QmlDesigner: Use error notifier for prototype and extension type name resolving

If the prototype or extension has an unresolved id, it needs to be
checked, if an exported name belonging to the prototype or extension was
updated. In that case the id has to be again resolved.

Task-number: QDS-12761
Change-Id: I7a733662cf37e13e8c2db53dec5a4f3e0a9b6ecf
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2024-05-16 18:17:21 +02:00
parent bcf0594419
commit b8322aece2
8 changed files with 846 additions and 140 deletions

View File

@@ -36,13 +36,6 @@ class DatabaseBackend;
enum class Type : char { Invalid, Integer, Float, Text, Blob, Null }; enum class Type : char { Invalid, Integer, Float, Text, Blob, Null };
template<typename Enumeration>
constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
class SQLITE_EXPORT BaseStatement class SQLITE_EXPORT BaseStatement
{ {
public: public:
@@ -87,7 +80,7 @@ public:
template<typename Type, typename = std::enable_if_t<Type::IsBasicId::value>> template<typename Type, typename = std::enable_if_t<Type::IsBasicId::value>>
void bind(int index, Type id) void bind(int index, Type id)
{ {
if (id) if (!id.isNull())
bind(index, id.internalId()); bind(index, id.internalId());
else else
bindNull(index); bindNull(index);

View File

@@ -11,6 +11,13 @@
namespace Sqlite { namespace Sqlite {
template<typename Enumeration>
static constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
template<auto Type, typename InternalIntegerType = long long> template<auto Type, typename InternalIntegerType = long long>
class BasicId class BasicId
{ {
@@ -27,7 +34,15 @@ public:
return id; return id;
} }
constexpr friend bool compareInvalidAreTrue(BasicId first, BasicId second) template<typename Enumeration>
static constexpr BasicId createSpecialState(Enumeration specialState)
{
BasicId id;
id.id = ::Sqlite::to_underlying(specialState);
return id;
}
friend constexpr bool compareInvalidAreTrue(BasicId first, BasicId second)
{ {
return first.id == second.id; return first.id == second.id;
} }
@@ -57,6 +72,14 @@ public:
constexpr bool isValid() const { return id > 0; } constexpr bool isValid() const { return id > 0; }
constexpr bool isNull() const { return id == 0; }
template<typename Enumeration>
constexpr bool hasSpecialState(Enumeration specialState) const
{
return id == ::Sqlite::to_underlying(specialState);
}
explicit operator bool() const { return isValid(); } explicit operator bool() const { return isValid(); }
explicit operator std::size_t() const { return static_cast<std::size_t>(id); } explicit operator std::size_t() const { return static_cast<std::size_t>(id); }
@@ -68,13 +91,13 @@ public:
template<typename String> template<typename String>
friend void convertToString(String &string, BasicId id) friend void convertToString(String &string, BasicId id)
{ {
if (id.isValid()) if (id.isNull())
NanotraceHR::convertToString(string, id.internalId()); NanotraceHR::convertToString(string, "invalid null");
else else
NanotraceHR::convertToString(string, "invalid"); NanotraceHR::convertToString(string, id.internalId());
} }
private: protected:
InternalIntegerType id = 0; InternalIntegerType id = 0;
}; };

View File

@@ -7,6 +7,25 @@
namespace QmlDesigner { namespace QmlDesigner {
enum class SpecialIdState { Unresolved = -1 };
constexpr TypeId unresolvedTypeId = TypeId::createSpecialState(SpecialIdState::Unresolved);
class UnresolvedTypeId : public TypeId
{
public:
constexpr UnresolvedTypeId()
: TypeId{TypeId::createSpecialState(SpecialIdState::Unresolved)}
{}
static constexpr UnresolvedTypeId create(DatabaseType idNumber)
{
UnresolvedTypeId id;
id.id = idNumber;
return id;
}
};
struct ProjectStorage::Statements struct ProjectStorage::Statements
{ {
Statements(Sqlite::Database &database) Statements(Sqlite::Database &database)
@@ -17,9 +36,13 @@ struct ProjectStorage::Statements
Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{ Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{
"INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database};
Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{ Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{
"UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " "UPDATE types "
"WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " "SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 "
"IS NOT ?4 OR extensionNameId IS NOT ?5)", "WHERE typeId=?1 AND ( "
" prototypeId IS NOT ?2 "
" OR extensionId IS NOT ?3 "
" OR prototypeId IS NOT ?4 "
" OR extensionNameId IS NOT ?5)",
database}; database};
mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{
"SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; "SELECT typeId FROM exportedTypeNames WHERE name=?1", database};
@@ -357,13 +380,51 @@ struct ProjectStorage::Statements
"SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{ Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ Sqlite::ReadWriteStatement<2, 2> updatePrototypeIdToTypeIdStatement{
"UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " "UPDATE types "
"typeId, prototypeNameId", "SET prototypeId=?2 "
"WHERE prototypeId=?1 "
"RETURNING typeId, prototypeNameId",
database}; database};
Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ Sqlite::ReadWriteStatement<2, 2> updateExtensionIdToTypeIdStatement{
"UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " "UPDATE types "
"typeId, extensionNameId", "SET extensionId=?2 "
"WHERE extensionId=?1 "
"RETURNING typeId, extensionNameId",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement{
"SELECT typeId, prototypeNameId "
"FROM types "
"WHERE prototypeNameId IN ( "
" SELECT importedTypeNameId "
" FROM "
" importedTypeNames WHERE name=?1) "
" AND prototypeId=?2",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement{
"SELECT typeId , prototypeNameId "
"FROM types "
"WHERE prototypeId=?1 AND sourceId=?2",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement{
"SELECT typeId, extensionNameId "
"FROM types "
"WHERE extensionId=?1 AND sourceId=?2",
database};
Sqlite::ReadWriteStatement<3, 3> updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement{
"UPDATE types "
"SET prototypeId=?2, extensionId=?3 "
"WHERE sourceId=?1 "
"RETURNING typeId, prototypeNameId, extensionNameId",
database};
Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{
"SELECT typeId , prototypeNameId "
"FROM types "
"WHERE extensionNameId IN ( "
" SELECT importedTypeNameId "
" FROM importedTypeNames "
" WHERE name=?1) "
" AND extensionId=?2",
database}; database};
Sqlite::WriteStatement<2> updateTypePrototypeStatement{ Sqlite::WriteStatement<2> updateTypePrototypeStatement{
"UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database};
@@ -619,6 +680,8 @@ struct ProjectStorage::Statements
"UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database};
mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{
"SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database};
mutable Sqlite::ReadStatement<1, 1> selectSourceIdByTypeIdStatement{
"SELECT sourceId FROM types WHERE typeId=?", database};
mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{ mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{
"SELECT annotationTraits " "SELECT annotationTraits "
"FROM types " "FROM types "
@@ -801,23 +864,23 @@ public:
auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer);
auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text);
typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer);
auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", auto &prototypeIdColumn = typesTable.addColumn("prototypeId",
typesTable, Sqlite::StrictColumnType::Integer);
Sqlite::ForeignKeyAction::NoAction, auto &prototypeNameIdColumn = typesTable.addColumn("prototypeNameId",
Sqlite::ForeignKeyAction::Restrict); Sqlite::StrictColumnType::Integer);
typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); auto &extensionIdColumn = typesTable.addColumn("extensionId",
auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", Sqlite::StrictColumnType::Integer);
typesTable, auto &extensionNameIdColumn = typesTable.addColumn("extensionNameId",
Sqlite::ForeignKeyAction::NoAction, Sqlite::StrictColumnType::Integer);
Sqlite::ForeignKeyAction::Restrict);
typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer);
auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId",
Sqlite::StrictColumnType::Integer); Sqlite::StrictColumnType::Integer);
typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer);
typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn});
typesTable.addIndex({defaultPropertyIdColumn}); typesTable.addIndex({defaultPropertyIdColumn});
typesTable.addIndex({prototypeIdColumn}); typesTable.addIndex({prototypeIdColumn, sourceIdColumn});
typesTable.addIndex({extensionIdColumn}); typesTable.addIndex({extensionIdColumn, sourceIdColumn});
typesTable.addIndex({prototypeNameIdColumn});
typesTable.addIndex({extensionNameIdColumn});
typesTable.initialize(database); typesTable.initialize(database);
@@ -1131,7 +1194,7 @@ ProjectStorage::ProjectStorage(Database &database,
ProjectStorageErrorNotifierInterface &errorNotifier, ProjectStorageErrorNotifierInterface &errorNotifier,
bool isInitialized) bool isInitialized)
: database{database} : database{database}
, errorNotifier{errorNotifier} , errorNotifier{&errorNotifier}
, exclusiveTransaction{database} , exclusiveTransaction{database}
, initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)} , initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)}
, moduleCache{ModuleStorageAdapter{*this}} , moduleCache{ModuleStorageAdapter{*this}}
@@ -1175,7 +1238,9 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag
package.moduleDependencies, package.moduleDependencies,
package.updatedModuleDependencySourceIds, package.updatedModuleDependencySourceIds,
package.moduleExportedImports, package.moduleExportedImports,
package.updatedModuleIds); package.updatedModuleIds,
relinkablePrototypes,
relinkableExtensions);
synchronizeTypes(package.types, synchronizeTypes(package.types,
updatedTypeIds, updatedTypeIds,
insertedAliasPropertyDeclarations, insertedAliasPropertyDeclarations,
@@ -1223,7 +1288,24 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, Source
keyValue("source id", sourceId)}; keyValue("source id", sourceId)};
Sqlite::withImmediateTransaction(database, [&] { Sqlite::withImmediateTransaction(database, [&] {
synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import); AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
PropertyDeclarations relinkablePropertyDeclarations;
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
synchronizeDocumentImports(imports,
{sourceId},
Storage::Synchronization::ImportKind::Import,
Relink::Yes,
relinkablePrototypes,
relinkableExtensions);
relink(relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
}); });
} }
@@ -2642,19 +2724,29 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports,
Storage::Imports &moduleDependencies, Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds, const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports, Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
const ModuleIds &updatedModuleIds) const ModuleIds &updatedModuleIds,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{ {
NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()};
synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds);
NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()};
synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import); synchronizeDocumentImports(imports,
updatedSourceIds,
Storage::Synchronization::ImportKind::Import,
Relink::No,
relinkablePrototypes,
relinkableExtensions);
importTracer.end(); importTracer.end();
NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t,
projectStorageCategory()}; projectStorageCategory()};
synchronizeDocumentImports(moduleDependencies, synchronizeDocumentImports(moduleDependencies,
updatedModuleDependencySourceIds, updatedModuleDependencySourceIds,
Storage::Synchronization::ImportKind::ModuleDependency); Storage::Synchronization::ImportKind::ModuleDependency,
Relink::Yes,
relinkablePrototypes,
relinkableExtensions);
moduleDependenciesTracer.end(); moduleDependenciesTracer.end();
} }
@@ -2821,11 +2913,30 @@ void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkable
keyValue("type id", prototypeId), keyValue("type id", prototypeId),
keyValue("relinkable prototypes", relinkablePrototypes)}; keyValue("relinkable prototypes", relinkablePrototypes)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
if (prototypeNameId)
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
s->updatePrototypeIdToTypeIdStatement.readCallback(callback, prototypeId, unresolvedTypeId);
}
void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId(
Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle invalid prototypes"_t,
projectStorageCategory(),
keyValue("type id", exportedTypeName),
keyValue("relinkable prototypes", relinkablePrototypes)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
relinkablePrototypes.emplace_back(typeId, prototypeNameId); relinkablePrototypes.emplace_back(typeId, prototypeNameId);
}; };
s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement.readCallback(callback,
exportedTypeName,
typeId);
} }
void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions)
@@ -2836,11 +2947,28 @@ void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkable
keyValue("type id", extensionId), keyValue("type id", extensionId),
keyValue("relinkable extensions", relinkableExtensions)}; keyValue("relinkable extensions", relinkableExtensions)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
if (extensionNameId)
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->updateExtensionIdToTypeIdStatement.readCallback(callback, extensionId, unresolvedTypeId);
}
void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId(
Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle invalid extensions"_t,
projectStorageCategory(),
keyValue("type id", exportedTypeName),
keyValue("relinkable extensions", relinkableExtensions)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
relinkableExtensions.emplace_back(typeId, extensionNameId); relinkableExtensions.emplace_back(typeId, extensionNameId);
}; };
s->updateExtensionIdToNullStatement.readCallback(callback, extensionId); s->selectTypeIdForExtensionIdAndTypeNameStatement.readCallback(callback, exportedTypeName, typeId);
} }
void ProjectStorage::deleteType(TypeId typeId, void ProjectStorage::deleteType(TypeId typeId,
@@ -2927,6 +3055,39 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable
TypeCompare<PropertyDeclaration>{}); TypeCompare<PropertyDeclaration>{});
} }
template<typename Callable>
void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes,
const TypeIds &deletedTypeIds,
Callable updateStatement)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"relink prototypes"_t,
projectStorageCategory(),
keyValue("relinkable prototypes", relinkablePrototypes),
keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
relinkablePrototypes.erase(std::unique(relinkablePrototypes.begin(), relinkablePrototypes.end()),
relinkablePrototypes.end());
Utils::set_greedy_difference(
relinkablePrototypes.cbegin(),
relinkablePrototypes.cend(),
deletedTypeIds.begin(),
deletedTypeIds.end(),
[&](const Prototype &prototype) {
TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
if (!prototypeId)
errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(prototype.prototypeNameId),
fetchTypeSourceId(prototype.typeId));
updateStatement(prototype.typeId, prototypeId);
checkForPrototypeChainCycle(prototype.typeId);
},
TypeCompare<Prototype>{});
}
void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const SourceIds &updatedSourceIds, const SourceIds &updatedSourceIds,
const TypeIds &typeIdsToBeDeleted, const TypeIds &typeIdsToBeDeleted,
@@ -3141,6 +3302,9 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
} catch (const Sqlite::ConstraintPreventsModification &) { } catch (const Sqlite::ConstraintPreventsModification &) {
throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; throw QmlDesigner::ExportedTypeCannotBeInserted{type.name};
} }
handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes);
handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions);
}; };
auto update = [&](const Storage::Synchronization::ExportedTypeView &view, auto update = [&](const Storage::Synchronization::ExportedTypeView &view,
@@ -3176,6 +3340,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds,
relinkableAliasPropertyDeclarations); relinkableAliasPropertyDeclarations);
handlePrototypes(view.typeId, relinkablePrototypes); handlePrototypes(view.typeId, relinkablePrototypes);
handleExtensions(view.typeId, relinkableExtensions); handleExtensions(view.typeId, relinkableExtensions);
s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId); s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId);
}; };
@@ -3491,11 +3656,90 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull(
PropertyCompare<AliasPropertyDeclaration>{}); PropertyCompare<AliasPropertyDeclaration>{});
} }
void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId,
TypeId prototypeId,
Prototypes &relinkablePrototypes)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t,
projectStorageCategory(),
keyValue("source id", sourceId),
keyValue("type id", prototypeId)};
auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) {
if (prototypeNameId)
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
};
s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement.readCallback(callback,
prototypeId,
sourceId);
}
void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId,
TypeId prototypeId,
TypeId extensionId,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle prototypes with source id"_t,
projectStorageCategory(),
keyValue("source id", sourceId),
keyValue("prototype id", prototypeId),
keyValue("extension id", extensionId)};
auto callback =
[&](TypeId typeId, ImportedTypeNameId prototypeNameId, ImportedTypeNameId extensionNameId) {
if (prototypeNameId)
relinkablePrototypes.emplace_back(typeId, prototypeNameId);
if (extensionNameId)
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement.readCallback(callback,
sourceId,
prototypeId,
extensionId);
}
void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId,
TypeId extensionId,
Prototypes &relinkableExtensions)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t,
projectStorageCategory(),
keyValue("source id", sourceId),
keyValue("type id", extensionId)};
auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) {
if (extensionNameId)
relinkableExtensions.emplace_back(typeId, extensionNameId);
};
s->selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement.readCallback(callback,
extensionId,
sourceId);
}
ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind, Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId, ModuleId sourceModuleId,
ImportId parentImportId) ImportId parentImportId,
Relink relink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{ {
if (relink == Relink::Yes) {
handlePrototypesWithSourceIdAndPrototypeId(import.sourceId,
unresolvedTypeId,
relinkablePrototypes);
handleExtensionsWithSourceIdAndExtensionId(import.sourceId,
unresolvedTypeId,
relinkableExtensions);
}
if (import.version.minor) { if (import.version.minor) {
return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId, return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId,
import.moduleId, import.moduleId,
@@ -3522,7 +3766,10 @@ ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import,
void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds, const SourceIds &updatedSourceIds,
Storage::Synchronization::ImportKind importKind) Storage::Synchronization::ImportKind importKind,
Relink relink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions)
{ {
std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) {
return std::tie(first.sourceId, first.moduleId, first.version) return std::tie(first.sourceId, first.moduleId, first.version)
@@ -3559,7 +3806,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
keyValue("source id", import.sourceId), keyValue("source id", import.sourceId),
keyValue("module id", import.moduleId)}; keyValue("module id", import.moduleId)};
auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); auto importId = insertDocumentImport(import,
importKind,
import.moduleId,
ImportId{},
relink,
relinkablePrototypes,
relinkableExtensions);
auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) {
Storage::Import additionImport{exportedModuleId, Storage::Import additionImport{exportedModuleId,
Storage::Version{majorVersion, minorVersion}, Storage::Version{majorVersion, minorVersion},
@@ -3579,7 +3832,10 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
auto indirectImportId = insertDocumentImport(additionImport, auto indirectImportId = insertDocumentImport(additionImport,
exportedImportKind, exportedImportKind,
import.moduleId, import.moduleId,
importId); importId,
relink,
relinkablePrototypes,
relinkableExtensions);
tracer.end(keyValue("import id", indirectImportId)); tracer.end(keyValue("import id", indirectImportId));
}; };
@@ -3606,6 +3862,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports,
s->deleteDocumentImportStatement.write(view.importId); s->deleteDocumentImportStatement.write(view.importId);
s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId);
if (relink == Relink::Yes) {
handlePrototypesAndExtensionsWithSourceId(view.sourceId,
unresolvedTypeId,
unresolvedTypeId,
relinkablePrototypes,
relinkableExtensions);
}
}; };
Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove);
@@ -4156,25 +4419,29 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla
} }
std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId( std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId(
const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId)
{ {
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t,
projectStorageCategory(), projectStorageCategory(),
keyValue("imported type name", typeName), keyValue("imported type name", importedTypeName),
keyValue("source id", sourceId)}; keyValue("source id", sourceId)};
TypeId typeId; TypeId typeId;
ImportedTypeNameId typeNameId; ImportedTypeNameId typeNameId;
if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; },
typeNameId = fetchImportedTypeNameId(typeName, sourceId); importedTypeName);
if (!typeName.empty()) {
typeNameId = fetchImportedTypeNameId(importedTypeName, sourceId);
typeId = fetchTypeId(typeNameId); typeId = fetchTypeId(typeNameId);
tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId));
if (!typeId) if (!typeId) {
throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; errorNotifier->typeNameCannotBeResolved(typeName, sourceId);
return {unresolvedTypeId, typeNameId};
}
} }
return {typeId, typeNameId}; return {typeId, typeNameId};
@@ -4323,6 +4590,11 @@ Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId type
return s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId); return s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId);
} }
SourceId ProjectStorage::fetchTypeSourceId(TypeId typeId) const
{
return s->selectSourceIdByTypeIdStatement.value<SourceId>(typeId);
}
TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
Storage::Synchronization::TypeNameKind kind) const Storage::Synchronization::TypeNameKind kind) const
{ {
@@ -4334,9 +4606,10 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
TypeId typeId; TypeId typeId;
if (kind == Storage::Synchronization::TypeNameKind::Exported) { if (kind == Storage::Synchronization::TypeNameKind::Exported) {
typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<TypeId>(typeNameId); typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value<UnresolvedTypeId>(typeNameId);
} else { } else {
typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<TypeId>(typeNameId); typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<UnresolvedTypeId>(
typeNameId);
} }
tracer.end(keyValue("type id", typeId)); tracer.end(keyValue("type id", typeId));

View File

@@ -39,6 +39,8 @@ class ProjectStorage final : public ProjectStorageInterface
using Database = Sqlite::Database; using Database = Sqlite::Database;
friend Storage::Info::CommonTypeCache<ProjectStorageType>; friend Storage::Info::CommonTypeCache<ProjectStorageType>;
enum class Relink { No, Yes };
public: public:
ProjectStorage(Database &database, ProjectStorage(Database &database,
ProjectStorageErrorNotifierInterface &errorNotifier, ProjectStorageErrorNotifierInterface &errorNotifier,
@@ -49,6 +51,11 @@ public:
void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override; void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override;
void setErrorNotifier(ProjectStorageErrorNotifierInterface &errorNotifier)
{
this->errorNotifier = &errorNotifier;
}
void addObserver(ProjectStorageObserver *observer) override; void addObserver(ProjectStorageObserver *observer) override;
void removeObserver(ProjectStorageObserver *observer) override; void removeObserver(ProjectStorageObserver *observer) override;
@@ -443,6 +450,11 @@ private:
return first.typeId < second.typeId; return first.typeId < second.typeId;
} }
friend bool operator==(Prototype first, Prototype second)
{
return first.typeId == second.typeId;
}
template<typename String> template<typename String>
friend void convertToString(String &string, const Prototype &prototype) friend void convertToString(String &string, const Prototype &prototype)
{ {
@@ -575,7 +587,9 @@ private:
Storage::Imports &moduleDependencies, Storage::Imports &moduleDependencies,
const SourceIds &updatedModuleDependencySourceIds, const SourceIds &updatedModuleDependencySourceIds,
Storage::Synchronization::ModuleExportedImports &moduleExportedImports, Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
const ModuleIds &updatedModuleIds); const ModuleIds &updatedModuleIds,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
void synchromizeModuleExportedImports( void synchromizeModuleExportedImports(
Storage::Synchronization::ModuleExportedImports &moduleExportedImports, Storage::Synchronization::ModuleExportedImports &moduleExportedImports,
@@ -593,9 +607,14 @@ private:
PropertyDeclarations &relinkablePropertyDeclarations); PropertyDeclarations &relinkablePropertyDeclarations);
void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes);
void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName,
TypeId typeId,
Prototypes &relinkablePrototypes);
void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions); void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions);
void handleExtensionsWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName,
TypeId typeId,
Prototypes &relinkableExtensions);
void deleteType(TypeId typeId, void deleteType(TypeId typeId,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations,
PropertyDeclarations &relinkablePropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations,
@@ -611,32 +630,7 @@ private:
template<typename Callable> template<typename Callable>
void relinkPrototypes(Prototypes &relinkablePrototypes, void relinkPrototypes(Prototypes &relinkablePrototypes,
const TypeIds &deletedTypeIds, const TypeIds &deletedTypeIds,
Callable updateStatement) Callable updateStatement);
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"relink prototypes"_t,
projectStorageCategory(),
keyValue("relinkable prototypes", relinkablePrototypes),
keyValue("deleted type ids", deletedTypeIds)};
std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end());
Utils::set_greedy_difference(
relinkablePrototypes.cbegin(),
relinkablePrototypes.cend(),
deletedTypeIds.begin(),
deletedTypeIds.end(),
[&](const Prototype &prototype) {
TypeId prototypeId = fetchTypeId(prototype.prototypeNameId);
if (!prototypeId)
throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)};
updateStatement(prototype.typeId, prototypeId);
checkForPrototypeChainCycle(prototype.typeId);
},
TypeCompare<Prototype>{});
}
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds,
const SourceIds &updatedSourceIds, const SourceIds &updatedSourceIds,
@@ -751,14 +745,32 @@ private:
Storage::Synchronization::Types &types, Storage::Synchronization::Types &types,
AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); AliasPropertyDeclarations &relinkableAliasPropertyDeclarations);
void handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId,
TypeId prototypeId,
Prototypes &relinkablePrototypes);
void handlePrototypesAndExtensionsWithSourceId(SourceId sourceId,
TypeId prototypeId,
TypeId extensionId,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
void handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId,
TypeId extensionId,
Prototypes &relinkableExtensions);
ImportId insertDocumentImport(const Storage::Import &import, ImportId insertDocumentImport(const Storage::Import &import,
Storage::Synchronization::ImportKind importKind, Storage::Synchronization::ImportKind importKind,
ModuleId sourceModuleId, ModuleId sourceModuleId,
ImportId parentImportId); ImportId parentImportId,
Relink forceRelink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
void synchronizeDocumentImports(Storage::Imports &imports, void synchronizeDocumentImports(Storage::Imports &imports,
const SourceIds &updatedSourceIds, const SourceIds &updatedSourceIds,
Storage::Synchronization::ImportKind importKind); Storage::Synchronization::ImportKind importKind,
Relink forceRelink,
Prototypes &relinkablePrototypes,
Prototypes &relinkableExtensions);
static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters); static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations &parameters);
@@ -904,6 +916,7 @@ private:
TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; TypeId fetchTypeId(ImportedTypeNameId typeNameId) const;
Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const; Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const;
SourceId fetchTypeSourceId(TypeId typeId) const;
TypeId fetchTypeId(ImportedTypeNameId typeNameId, TypeId fetchTypeId(ImportedTypeNameId typeNameId,
Storage::Synchronization::TypeNameKind kind) const; Storage::Synchronization::TypeNameKind kind) const;
@@ -970,7 +983,7 @@ private:
public: public:
Database &database; Database &database;
ProjectStorageErrorNotifierInterface &errorNotifier; ProjectStorageErrorNotifierInterface *errorNotifier = nullptr; // cannot be null
Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction; Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction;
std::unique_ptr<Initializer> initializer; std::unique_ptr<Initializer> initializer;
mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}};

View File

@@ -9,6 +9,7 @@
class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface
{ {
public:
MOCK_METHOD(void, MOCK_METHOD(void,
typeNameCannotBeResolved, typeNameCannotBeResolved,
(Utils::SmallStringView typeName, QmlDesigner::SourceId souceId), (Utils::SmallStringView typeName, QmlDesigner::SourceId souceId),

View File

@@ -833,8 +833,9 @@ std::ostream &operator<<(std::ostream &out, const Type &type)
{ {
using std::operator<<; using std::operator<<;
using Utils::operator<<; using Utils::operator<<;
return out << "( typename: \"" << type.typeName << "\", prototype: " << type.prototype << ", " return out << "( typename: \"" << type.typeName << "\", prototype: {\"" << type.prototype
<< type.prototypeId << ", " << type.traits << ", source: " << type.sourceId << "\", " << type.prototypeId << "}, " << "\", extension: {\"" << type.extension
<< "\", " << type.extensionId << "}, " << type.traits << ", source: " << type.sourceId
<< ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations << ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations
<< ", functions: " << type.functionDeclarations << ", functions: " << type.functionDeclarations
<< ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel << ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel

View File

@@ -205,6 +205,23 @@ MATCHER_P4(IsInfoPropertyDeclaration,
&& propertyDeclaration.traits == traits; && propertyDeclaration.traits == traits;
} }
auto IsUnresolvedTypeId()
{
return Property(&QmlDesigner::TypeId::internalId, -1);
}
template<typename Matcher>
auto IsPrototypeId(const Matcher &matcher)
{
return Field(&Storage::Synchronization::Type::prototypeId, matcher);
}
template<typename Matcher>
auto IsExtensionId(const Matcher &matcher)
{
return Field(&Storage::Synchronization::Type::extensionId, matcher);
}
class HasNameMatcher class HasNameMatcher
{ {
public: public:
@@ -286,6 +303,8 @@ protected:
static void TearDownTestSuite() { staticData.reset(); } static void TearDownTestSuite() { staticData.reset(); }
ProjectStorage() { storage.setErrorNotifier(errorNotifierMock); }
~ProjectStorage() { storage.resetForTestsOnly(); } ~ProjectStorage() { storage.resetForTestsOnly(); }
template<typename Range> template<typename Range>
@@ -312,6 +331,32 @@ protected:
storage.fetchSourceId(sourceContextId3, "bar"); storage.fetchSourceId(sourceContextId3, "bar");
} }
auto createVerySimpleSynchronizationPackage()
{
SynchronizationPackage package;
package.types.push_back(Storage::Synchronization::Type{
"QQuickItem",
Storage::Synchronization::ImportedType{},
Storage::Synchronization::ImportedType{},
TypeTraitsKind::Reference,
sourceId1,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}});
package.types.push_back(Storage::Synchronization::Type{
"QObject",
Storage::Synchronization::ImportedType{},
Storage::Synchronization::ImportedType{},
TypeTraitsKind::Reference,
sourceId2,
{Storage::Synchronization::ExportedType{qmlModuleId, "Object"},
Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}});
package.updatedSourceIds = {sourceId1, sourceId2};
return package;
}
auto createSimpleSynchronizationPackage() auto createSimpleSynchronizationPackage()
{ {
SynchronizationPackage package; SynchronizationPackage package;
@@ -1142,7 +1187,7 @@ protected:
inline static std::unique_ptr<StaticData> staticData; inline static std::unique_ptr<StaticData> staticData;
Sqlite::Database &database = staticData->database; Sqlite::Database &database = staticData->database;
QmlDesigner::ProjectStorage &storage = staticData->storage; QmlDesigner::ProjectStorage &storage = staticData->storage;
ProjectStorageErrorNotifierMock &errorNotifierMock = staticData->errorNotifierMock; NiceMock<ProjectStorageErrorNotifierMock> errorNotifierMock;
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{storage}; QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage> sourcePathCache{storage};
QmlDesigner::SourcePathView path1{"/path1/to"}; QmlDesigner::SourcePathView path1{"/path1/to"};
QmlDesigner::SourcePathView path2{"/path2/to"}; QmlDesigner::SourcePathView path2{"/path2/to"};
@@ -1486,22 +1531,193 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_
"QQuickItem")))))); "QQuickItem"))))));
} }
TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_prototype_name) TEST_F(ProjectStorage,
synchronize_types_adds_unknown_prototype_which_notifies_about_unresolved_type_name)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
} }
TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_extension_name) TEST_F(ProjectStorage, synchronize_types_adds_unknown_prototype_as_unresolved_type_id)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_prototype_to_unresolved_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_unresolved_prototype_indirectly_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
}
TEST_F(ProjectStorage, synchronize_types_updates_unresolved_extension_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_extension_to_unresolved_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_unresolved_extension_indirectly_after_exported_type_name_is_added)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
storage.synchronize(package);
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage,
synchronize_types_updates_invalid_extension_indirectly_after_exported_type_name_is_removed)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
storage.synchronize(std::move(package));
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage,
synchronize_types_updates_extension_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved)
{
auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec");
storage.synchronize(package);
package.types[1].exportedTypes.pop_back();
package.types.erase(package.types.begin());
package.updatedSourceIds = {sourceId2};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
}
TEST_F(ProjectStorage, synchronize_types_adds_extension_which_notifies_about_unresolved_type_name)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"};
ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1));
storage.synchronize(std::move(package));
} }
TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module) TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
@@ -1774,7 +1990,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension)
IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); IsExportedType(qtQuickNativeModuleId, "QQuickItem"))))));
} }
TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype) TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_prototype)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types = {Storage::Synchronization::Type{ package.types = {Storage::Synchronization::Type{
@@ -1786,10 +2002,12 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype)
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension) TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_extension)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types = {Storage::Synchronization::Type{ package.types = {Storage::Synchronization::Type{
@@ -1801,7 +2019,9 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension)
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"},
Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module) TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module)
@@ -3670,7 +3890,8 @@ TEST_F(ProjectStorage, change_extension_type_module_id)
TypeTraitsKind::Reference))); TypeTraitsKind::Reference)));
} }
TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws) TEST_F(ProjectStorage,
change_qualified_prototype_type_module_id_notifies_that_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
@@ -3678,12 +3899,12 @@ TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws)
storage.synchronize(package); storage.synchronize(package);
package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; package.types[1].exportedTypes[0].moduleId = qtQuickModuleId;
ASSERT_THROW(storage.synchronize( EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
} }
TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws) TEST_F(ProjectStorage, change_qualified_extension_type_module_id_notifies_cannot_resolve)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype); std::swap(package.types.front().extension, package.types.front().prototype);
@@ -3692,9 +3913,9 @@ TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws)
storage.synchronize(package); storage.synchronize(package);
package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; package.types[1].exportedTypes[0].moduleId = qtQuickModuleId;
ASSERT_THROW(storage.synchronize( EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
} }
TEST_F(ProjectStorage, change_qualified_prototype_type_module_id) TEST_F(ProjectStorage, change_qualified_prototype_type_module_id)
@@ -3798,9 +4019,9 @@ TEST_F(ProjectStorage, change_prototype_type_name_throws_for_wrong_native_protot
package.types[1].exportedTypes[2].name = "QObject3"; package.types[1].exportedTypes[2].name = "QObject3";
package.types[1].typeName = "QObject3"; package.types[1].typeName = "QObject3";
ASSERT_THROW(storage.synchronize( EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
} }
TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extension_type_name) TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extension_type_name)
@@ -3813,9 +4034,9 @@ TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extens
package.types[1].exportedTypes[2].name = "QObject3"; package.types[1].exportedTypes[2].name = "QObject3";
package.types[1].typeName = "QObject3"; package.types[1].typeName = "QObject3";
ASSERT_THROW(storage.synchronize( EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}),
QmlDesigner::TypeNameDoesNotExists); storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}});
} }
TEST_F(ProjectStorage, throw_for_prototype_chain_cycles) TEST_F(ProjectStorage, throw_for_prototype_chain_cycles)
@@ -4117,23 +4338,29 @@ TEST_F(ProjectStorage, qualified_extension)
TypeTraitsKind::Reference))); TypeTraitsKind::Reference)));
} }
TEST_F(ProjectStorage, qualified_prototype_upper_down_the_module_chain_throws) TEST_F(ProjectStorage,
qualified_prototype_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, qualified_extension_upper_down_the_module_chain_throws) TEST_F(ProjectStorage,
qualified_extension_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype); std::swap(package.types.front().extension, package.types.front().prototype);
package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ package.types[0].extension = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain) TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain)
@@ -4189,7 +4416,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain)
TypeTraitsKind::Reference))); TypeTraitsKind::Reference)));
} }
TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
@@ -4205,10 +4432,12 @@ TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws)
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
package.updatedSourceIds.push_back(sourceId3); package.updatedSourceIds.push_back(sourceId3);
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws) TEST_F(ProjectStorage, qualified_extension_with_wrong_version_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype); std::swap(package.types.front().extension, package.types.front().prototype);
@@ -4225,7 +4454,9 @@ TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws)
package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3);
package.updatedSourceIds.push_back(sourceId3); package.updatedSourceIds.push_back(sourceId3);
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, qualified_prototype_with_version) TEST_F(ProjectStorage, qualified_prototype_with_version)
@@ -4338,23 +4569,29 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain)
TypeTraitsKind::Reference))); TypeTraitsKind::Reference)));
} }
TEST_F(ProjectStorage, qualified_prototype_with_version_down_the_proto_type_chain_throws) TEST_F(ProjectStorage,
qualified_prototype_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}}; "Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, qualified_extension_with_version_down_the_proto_type_chain_throws) TEST_F(ProjectStorage,
qualified_extension_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
std::swap(package.types.front().extension, package.types.front().prototype); std::swap(package.types.front().extension, package.types.front().prototype);
package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ package.types[0].extension = Storage::Synchronization::QualifiedImportedType{
"Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}}; "Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}};
ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronize(package);
} }
TEST_F(ProjectStorage, qualified_property_declaration_type_name) TEST_F(ProjectStorage, qualified_property_declaration_type_name)
@@ -4659,7 +4896,7 @@ TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_im
} }
TEST_F(ProjectStorage, TEST_F(ProjectStorage,
fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_throws) fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSynchronizationPackageWithVersions()}; auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package); storage.synchronize(package);
@@ -4673,12 +4910,13 @@ TEST_F(ProjectStorage,
Storage::Version{}}}}; Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2));
QmlDesigner::TypeNameDoesNotExists);
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
} }
TEST_F(ProjectStorage, TEST_F(ProjectStorage,
fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_throws) fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSynchronizationPackageWithVersions()}; auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package); storage.synchronize(package);
@@ -4691,11 +4929,12 @@ TEST_F(ProjectStorage,
sourceId2, sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2));
QmlDesigner::TypeNameDoesNotExists);
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
} }
TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws) TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSynchronizationPackageWithVersions()}; auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package); storage.synchronize(package);
@@ -4709,11 +4948,13 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws)
Storage::Version{}}}}; Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
QmlDesigner::TypeNameDoesNotExists);
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
} }
TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throws) TEST_F(ProjectStorage,
fetch_low_minor_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSynchronizationPackageWithVersions()}; auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package); storage.synchronize(package);
@@ -4726,8 +4967,9 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throw
sourceId2, sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
QmlDesigner::TypeNameDoesNotExists);
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
} }
TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type) TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type)
@@ -4775,7 +5017,8 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type)
TypeTraitsKind::Reference))); TypeTraitsKind::Reference)));
} }
TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws) TEST_F(ProjectStorage,
fetch_different_major_version_for_imported_type_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSynchronizationPackageWithVersions()}; auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package); storage.synchronize(package);
@@ -4789,11 +5032,13 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws)
Storage::Version{}}}}; Storage::Version{}}}};
Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2}; Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
QmlDesigner::TypeNameDoesNotExists);
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
} }
TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type_throws) TEST_F(ProjectStorage,
fetch_different_major_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved)
{ {
auto package{createSynchronizationPackageWithVersions()}; auto package{createSynchronizationPackageWithVersions()};
storage.synchronize(package); storage.synchronize(package);
@@ -4806,8 +5051,9 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type
sourceId2, sourceId2,
{Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}};
ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2));
QmlDesigner::TypeNameDoesNotExists);
storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}});
} }
TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type) TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type)
@@ -7248,6 +7494,46 @@ TEST_F(ProjectStorage, synchronize_document_imports_adds_import)
ASSERT_TRUE(storage.importId(imports.back())); ASSERT_TRUE(storage.importId(imports.back()));
} }
TEST_F(ProjectStorage,
synchronize_document_imports_removes_import_notifies_that_type_name_cannot_be_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
storage.synchronize(package);
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1));
storage.synchronizeDocumentImports({}, sourceId1);
}
TEST_F(ProjectStorage, synchronize_document_imports_removes_import_which_makes_prototype_unresolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
storage.synchronize(package);
storage.synchronizeDocumentImports({}, sourceId1);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prototype_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"};
storage.synchronize(package);
Storage::Imports imports;
imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
storage.synchronizeDocumentImports(imports, sourceId1);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage, get_exported_type_names) TEST_F(ProjectStorage, get_exported_type_names)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
@@ -7996,4 +8282,105 @@ TEST_F(ProjectStorage, get_no_hair_ids_for_invalid_type_id)
ASSERT_THAT(heirIds, IsEmpty()); ASSERT_THAT(heirIds, IsEmpty());
} }
TEST_F(ProjectStorage,
removed_document_import_notifies_for_prototypes_that_type_name_cannot_be_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage,
removed_document_import_notifies_for_extensions_that_type_name_cannot_be_resolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, removed_document_import_changes_prototype_to_unresolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
Field(&Storage::Synchronization::Type::prototypeId, IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved)
{
auto package{createVerySimpleSynchronizationPackage()};
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.clear();
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
Field(&Storage::Synchronization::Type::extensionId, IsUnresolvedTypeId()));
}
TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype)
{
auto package{createVerySimpleSynchronizationPackage()};
package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsPrototypeId(fetchTypeId(sourceId2, "QObject")));
}
TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension)
{
auto package{createVerySimpleSynchronizationPackage()};
package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"};
storage.synchronize(package);
package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1);
package.types.clear();
package.updatedSourceIds.clear();
package.updatedModuleDependencySourceIds = {sourceId1};
storage.synchronize(package);
ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")),
IsExtensionId(fetchTypeId(sourceId2, "QObject")));
}
} // namespace } // namespace

View File

@@ -269,6 +269,21 @@ TEST_F(SqliteStatement, bind_int_id)
ASSERT_THAT(readStatement.fetchIntValue(0), 42); ASSERT_THAT(readStatement.fetchIntValue(0), 42);
} }
TEST_F(SqliteStatement, bind_special_state_id)
{
enum class SpecialIdState { Unresolved = -1 };
constexpr TestIntId unresolvedTypeId = TestIntId::createSpecialState(SpecialIdState::Unresolved);
SqliteTestStatement<0, 1> statement("INSERT INTO test VALUES ('id', 323, ?)", database);
statement.bind(1, unresolvedTypeId);
statement.next();
SqliteTestStatement<1, 1> readStatement("SELECT value FROM test WHERE name='id'", database);
readStatement.next();
ASSERT_THAT(readStatement.fetchType(0), Sqlite::Type::Integer);
ASSERT_THAT(readStatement.fetchIntValue(0), -1);
}
TEST_F(SqliteStatement, bind_invalid_long_long_id_to_null) TEST_F(SqliteStatement, bind_invalid_long_long_id_to_null)
{ {
TestLongLongId id; TestLongLongId id;