QmlDesigner: Adding missing property error notifier

Alias properties are quite often broken. So we have to handle that
broken aliases.

Task-number: QDS-12761
Change-Id: Id9b53b98080733e5939de6b3761c923fa35e1dcf
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2024-07-17 18:01:19 +02:00
parent 6daee01869
commit a29ff454a6
9 changed files with 274 additions and 98 deletions

View File

@@ -3165,21 +3165,25 @@ void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations &
auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); auto typeId = fetchTypeId(alias.aliasImportedTypeNameId);
if (typeId) { if (typeId) {
auto [propertyImportedTypeNameId, propertyTypeId, aliasId, propertyTraits] auto propertyDeclaration = fetchPropertyDeclarationByTypeIdAndNameUngarded(
= fetchPropertyDeclarationByTypeIdAndNameUngarded(typeId, alias.aliasPropertyName); typeId, alias.aliasPropertyName);
if (propertyDeclaration) {
auto [propertyImportedTypeNameId, propertyTypeId, aliasId, propertyTraits] = *propertyDeclaration;
s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, s->updatePropertyDeclarationWithAliasAndTypeStatement
propertyTypeId, .write(alias.propertyDeclarationId,
propertyTraits, propertyTypeId,
propertyImportedTypeNameId, propertyTraits,
aliasId); propertyImportedTypeNameId,
} else { aliasId);
errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName( return;
alias.aliasImportedTypeNameId), }
fetchTypeSourceId(alias.typeId));
s->resetAliasPropertyDeclarationStatement.write(alias.propertyDeclarationId,
Storage::PropertyDeclarationTraits{});
} }
errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(alias.aliasImportedTypeNameId),
fetchTypeSourceId(alias.typeId));
s->resetAliasPropertyDeclarationStatement.write(alias.propertyDeclarationId,
Storage::PropertyDeclarationTraits{});
}, },
TypeCompare<AliasPropertyDeclaration>{}); TypeCompare<AliasPropertyDeclaration>{});
} }
@@ -3321,7 +3325,10 @@ PropertyDeclarationId ProjectStorage::fetchAliasId(TypeId aliasTypeId,
auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName);
return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, if (!stemAlias)
return PropertyDeclarationId{};
return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias->propertyTypeId,
aliasPropertyNameTail); aliasPropertyNameTail);
} }
@@ -3341,10 +3348,22 @@ void ProjectStorage::linkAliasPropertyDeclarationAliasIds(
aliasDeclaration.aliasPropertyName, aliasDeclaration.aliasPropertyName,
aliasDeclaration.aliasPropertyNameTail); aliasDeclaration.aliasPropertyNameTail);
s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement if (aliasId) {
.write(aliasDeclaration.propertyDeclarationId, s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement
aliasId, .write(aliasDeclaration.propertyDeclarationId,
aliasDeclaration.aliasImportedTypeNameId); aliasId,
aliasDeclaration.aliasImportedTypeNameId);
} else {
s->resetAliasPropertyDeclarationStatement.write(aliasDeclaration.propertyDeclarationId,
Storage::PropertyDeclarationTraits{});
s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement
.write(aliasDeclaration.propertyDeclarationId,
TypeId{},
Storage::PropertyDeclarationTraits{});
errorNotifier->propertyNameDoesNotExists(aliasDeclaration.composedProperyName(),
aliasDeclaration.sourceId);
}
} else if (raiseError == RaiseError::Yes) { } else if (raiseError == RaiseError::Yes) {
errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName( errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(
aliasDeclaration.aliasImportedTypeNameId), aliasDeclaration.aliasImportedTypeNameId),
@@ -4516,10 +4535,18 @@ void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &type
keyValue("view", view)}; keyValue("view", view)};
PropertyDeclarationId valueDefaultPropertyId; PropertyDeclarationId valueDefaultPropertyId;
if (value.defaultPropertyName.size()) if (value.defaultPropertyName.size()) {
valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded(value.typeId, auto defaultPropertyDeclaration = fetchPropertyDeclarationByTypeIdAndNameUngarded(
value.defaultPropertyName) value.typeId, value.defaultPropertyName);
.propertyDeclarationId;
if (defaultPropertyDeclaration) {
valueDefaultPropertyId = defaultPropertyDeclaration->propertyDeclarationId;
} else {
errorNotifier->missingDefaultProperty(value.typeName,
value.defaultPropertyName,
value.sourceId);
}
}
if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId))
return Sqlite::UpdateChange::No; return Sqlite::UpdateChange::No;
@@ -4563,10 +4590,8 @@ void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::T
PropertyDeclarationId valueDefaultPropertyId; PropertyDeclarationId valueDefaultPropertyId;
if (value.defaultPropertyName.size()) { if (value.defaultPropertyName.size()) {
auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( valueDefaultPropertyId = fetchPropertyDeclarationIdByTypeIdAndNameUngarded(
value.typeId, value.defaultPropertyName); value.typeId, value.defaultPropertyName);
if (optionalValueDefaultPropertyId)
valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId;
} }
if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId))
@@ -4814,8 +4839,8 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId,
} }
std::optional<ProjectStorage::FetchPropertyDeclarationResult> std::optional<ProjectStorage::FetchPropertyDeclarationResult>
ProjectStorage::fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, ProjectStorage::fetchPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId,
Utils::SmallStringView name) Utils::SmallStringView name)
{ {
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t, NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t,
@@ -4833,24 +4858,6 @@ ProjectStorage::fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId t
return propertyDeclaration; return propertyDeclaration;
} }
ProjectStorage::FetchPropertyDeclarationResult ProjectStorage::fetchPropertyDeclarationByTypeIdAndNameUngarded(
TypeId typeId, Utils::SmallStringView name)
{
using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t,
projectStorageCategory(),
keyValue("type id", typeId),
keyValue("property name", name)};
auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, name);
tracer.end(keyValue("property declaration", propertyDeclaration));
if (propertyDeclaration)
return *propertyDeclaration;
throw PropertyNameDoesNotExists{};
}
PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameUngarded( PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameUngarded(
TypeId typeId, Utils::SmallStringView name) TypeId typeId, Utils::SmallStringView name)
{ {
@@ -4864,10 +4871,7 @@ PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameU
tracer.end(keyValue("property declaration id", propertyDeclarationId)); tracer.end(keyValue("property declaration id", propertyDeclarationId));
if (propertyDeclarationId) return propertyDeclarationId;
return propertyDeclarationId;
throw PropertyNameDoesNotExists{};
} }
SourceContextId ProjectStorage::readSourceContextId(Utils::SmallStringView sourceContextPath) SourceContextId ProjectStorage::readSourceContextId(Utils::SmallStringView sourceContextPath)

View File

@@ -411,6 +411,14 @@ private:
convertToString(string, dict); convertToString(string, dict);
} }
Utils::PathString composedProperyName() const
{
if (aliasPropertyNameTail.empty())
return aliasPropertyName;
return Utils::PathString::join({aliasPropertyName, ".", aliasPropertyNameTail});
}
public: public:
TypeId typeId; TypeId typeId;
PropertyDeclarationId propertyDeclarationId; PropertyDeclarationId propertyDeclarationId;
@@ -991,10 +999,7 @@ private:
Storage::PropertyDeclarationTraits propertyTraits; Storage::PropertyDeclarationTraits propertyTraits;
}; };
std::optional<FetchPropertyDeclarationResult> fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( std::optional<FetchPropertyDeclarationResult> fetchPropertyDeclarationByTypeIdAndNameUngarded(
TypeId typeId, Utils::SmallStringView name);
FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded(
TypeId typeId, Utils::SmallStringView name); TypeId typeId, Utils::SmallStringView name);
PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId, PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId,

View File

@@ -14,4 +14,20 @@ void ProjectStorageErrorNotifier::typeNameCannotBeResolved(Utils::SmallStringVie
<< " in file: " << m_pathCache.sourcePath(sourceId).toStringView(); << " in file: " << m_pathCache.sourcePath(sourceId).toStringView();
} }
void ProjectStorageErrorNotifier::missingDefaultProperty(Utils::SmallStringView typeName,
Utils::SmallStringView propertyName,
SourceId sourceId)
{
qDebug() << "Missing default property: " << propertyName << " in type: " << typeName
<< " in file: " << m_pathCache.sourcePath(sourceId).toStringView();
}
void ProjectStorageErrorNotifier::propertyNameDoesNotExists(Utils::SmallStringView propertyName,
SourceId sourceId)
{
qDebug() << "Missing property: " << propertyName
<< " in file: " << m_pathCache.sourcePath(sourceId).toStringView();
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -16,7 +16,11 @@ public:
: m_pathCache{pathCache} : m_pathCache{pathCache}
{} {}
void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) override; void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId sourceId) override;
void missingDefaultProperty(Utils::SmallStringView typeName,
Utils::SmallStringView propertyName,
SourceId sourceId) override;
void propertyNameDoesNotExists(Utils::SmallStringView propertyName, SourceId sourceId) override;
private: private:
PathCacheType &m_pathCache; PathCacheType &m_pathCache;

View File

@@ -18,7 +18,12 @@ public:
ProjectStorageErrorNotifierInterface(const ProjectStorageErrorNotifierInterface &) = delete; ProjectStorageErrorNotifierInterface(const ProjectStorageErrorNotifierInterface &) = delete;
ProjectStorageErrorNotifierInterface &operator=(const ProjectStorageErrorNotifierInterface &) = delete; ProjectStorageErrorNotifierInterface &operator=(const ProjectStorageErrorNotifierInterface &) = delete;
virtual void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) = 0; virtual void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId sourceId) = 0;
virtual void missingDefaultProperty(Utils::SmallStringView typeName,
Utils::SmallStringView propertyName,
SourceId sourceId)
= 0;
virtual void propertyNameDoesNotExists(Utils::SmallStringView propertyName, SourceId sourceId) = 0;
protected: protected:
~ProjectStorageErrorNotifierInterface() = default; ~ProjectStorageErrorNotifierInterface() = default;

View File

@@ -98,16 +98,6 @@ TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId
keyValue("source id", sourceId)); keyValue("source id", sourceId));
} }
PropertyNameDoesNotExists::PropertyNameDoesNotExists()
{
category().threadEvent("PropertyNameDoesNotExists"_t);
}
const char *PropertyNameDoesNotExists::what() const noexcept
{
return "The property name does not exist!";
}
PrototypeChainCycle::PrototypeChainCycle() PrototypeChainCycle::PrototypeChainCycle()
{ {
category().threadEvent("PrototypeChainCycle"_t); category().threadEvent("PrototypeChainCycle"_t);

View File

@@ -95,13 +95,6 @@ public:
TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId = SourceId{}); TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId = SourceId{});
}; };
class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError
{
public:
PropertyNameDoesNotExists();
const char *what() const noexcept override;
};
class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError
{ {
public: public:

View File

@@ -12,6 +12,16 @@ class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorN
public: public:
MOCK_METHOD(void, MOCK_METHOD(void,
typeNameCannotBeResolved, typeNameCannotBeResolved,
(Utils::SmallStringView typeName, QmlDesigner::SourceId souceId), (Utils::SmallStringView typeName, QmlDesigner::SourceId sourceId),
(override));
MOCK_METHOD(void,
missingDefaultProperty,
(Utils::SmallStringView typeName,
Utils::SmallStringView propertyName,
QmlDesigner::SourceId sourceId),
(override));
MOCK_METHOD(void,
propertyNameDoesNotExists,
(Utils::SmallStringView propertyName, QmlDesigner::SourceId sourceId),
(override)); (override));
}; };

View File

@@ -216,6 +216,11 @@ auto IsNullTypeId()
return Property(&QmlDesigner::TypeId::isNull, true); return Property(&QmlDesigner::TypeId::isNull, true);
} }
auto IsNullPropertyDeclarationId()
{
return Property(&QmlDesigner::PropertyDeclarationId::isNull, true);
}
template<typename Matcher> template<typename Matcher>
auto HasPrototypeId(const Matcher &matcher) auto HasPrototypeId(const Matcher &matcher)
{ {
@@ -1182,6 +1187,19 @@ protected:
return storage.fetchTypeByTypeId(storage.fetchTypeIdByName(sourceId, name)); return storage.fetchTypeByTypeId(storage.fetchTypeIdByName(sourceId, name));
} }
auto defaultPropertyDeclarationId(SourceId sourceId, Utils::SmallStringView typeName)
{
return storage.defaultPropertyDeclarationId(storage.fetchTypeIdByName(sourceId, typeName));
}
auto propertyDeclarationId(SourceId sourceId,
Utils::SmallStringView typeName,
Utils::SmallStringView propertyName)
{
return storage.propertyDeclarationId(storage.fetchTypeIdByName(sourceId, typeName),
propertyName);
}
static auto &findType(Storage::Synchronization::SynchronizationPackage &package, static auto &findType(Storage::Synchronization::SynchronizationPackage &package,
Utils::SmallStringView name) Utils::SmallStringView name)
{ {
@@ -3508,17 +3526,46 @@ TEST_F(ProjectStorage, synchronize_types_fixes_null_type_id_after_add_alias_decl
Contains(IsPropertyDeclaration("items", fetchTypeId(sourceId1, "QQuickItem"))))); Contains(IsPropertyDeclaration("items", fetchTypeId(sourceId1, "QQuickItem")))));
} }
TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_throws_for_wrong_property_name) TEST_F(ProjectStorage, synchronize_types_add_alias_declarations_notifies_error_for_wrong_property_name)
{ {
auto package{createSynchronizationPackageWithAliases()}; auto package{createSynchronizationPackageWithAliases()};
package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong"; package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong";
ASSERT_THROW(storage.synchronize(SynchronizationPackage{package.imports, EXPECT_CALL(errorNotifierMock, propertyNameDoesNotExists(Eq("childrenWrong"), sourceId3))
package.types, .Times(AtLeast(1));
{sourceId4},
package.moduleDependencies, storage.synchronize(
{sourceId4}}), {package.imports, package.types, {sourceId4}, package.moduleDependencies, {sourceId4}});
QmlDesigner::PropertyNameDoesNotExists); }
TEST_F(ProjectStorage,
synchronize_types_add_alias_declarations_returns_invalid_type_for_wrong_property_name)
{
auto package{createSynchronizationPackageWithAliases()};
package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong";
storage.synchronize(
{package.imports, package.types, {sourceId4}, package.moduleDependencies, {sourceId4}});
ASSERT_THAT(fetchType(sourceId3, "QAliasItem"),
PropertyDeclarations(Contains(IsPropertyDeclaration("items", IsNullTypeId()))));
}
TEST_F(ProjectStorage,
synchronize_types_update_alias_declarations_returns_item_type_for_fixed_property_name)
{
auto package{createSynchronizationPackageWithAliases()};
package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong";
storage.synchronize(
{package.imports, package.types, {sourceId4}, package.moduleDependencies, {sourceId4}});
package.types[2].propertyDeclarations[1].aliasPropertyName = "children";
storage.synchronize(
{importsSourceId3 + moduleDependenciesSourceId3, {package.types[2]}, {sourceId3}});
ASSERT_THAT(fetchType(sourceId3, "QAliasItem"),
PropertyDeclarations(
Contains(IsPropertyDeclaration("items", fetchTypeId(sourceId1, "QQuickItem")))));
} }
TEST_F(ProjectStorage, synchronize_types_change_alias_declarations_type_name) TEST_F(ProjectStorage, synchronize_types_change_alias_declarations_type_name)
@@ -6540,33 +6587,94 @@ TEST_F(ProjectStorage,
PropertyDeclarations(Not(Contains(IsPropertyDeclaration("objects", IsNullTypeId()))))); PropertyDeclarations(Not(Contains(IsPropertyDeclaration("objects", IsNullTypeId())))));
} }
TEST_F(ProjectStorage, synchronize_types_add_indirect_alias_declarations_throws_for_wrong_property_name) TEST_F(ProjectStorage,
synchronize_types_add_indirect_alias_declarations_notifies_error_for_wrong_property_name)
{ {
auto package{createSynchronizationPackageWithIndirectAliases()}; auto package{createSynchronizationPackageWithIndirectAliases()};
storage.synchronize(package); storage.synchronize(package);
package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong"; package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong";
ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId3, EXPECT_CALL(errorNotifierMock, propertyNameDoesNotExists(Eq("childrenWrong.objects"), sourceId3));
{package.types[2]},
{sourceId3}, storage.synchronize(
moduleDependenciesSourceId3, {importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}});
{sourceId3}}),
QmlDesigner::PropertyNameDoesNotExists);
} }
TEST_F(ProjectStorage, TEST_F(ProjectStorage,
synchronize_types_add_indirect_alias_declarations_throws_for_wrong_property_name_tail) synchronize_types_add_indirect_alias_declarations_returns_null_type_id_for_wrong_property_name)
{
auto package{createSynchronizationPackageWithIndirectAliases()};
storage.synchronize(package);
package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong";
storage.synchronize(
{importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}});
ASSERT_THAT(fetchType(sourceId3, "QAliasItem"),
PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId()))));
}
TEST_F(ProjectStorage,
synchronize_types_updated_indirect_alias_declarations_returns_item_type_id_for_wrong_property_name)
{
auto package{createSynchronizationPackageWithIndirectAliases()};
storage.synchronize(package);
package.types[2].propertyDeclarations[1].aliasPropertyName = "childrenWrong";
storage.synchronize(
{importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}});
package.types[2].propertyDeclarations[1].aliasPropertyName = "children";
storage.synchronize(
{importsSourceId3 + moduleDependenciesSourceId3, {package.types[2]}, {sourceId3}});
ASSERT_THAT(fetchType(sourceId3, "QAliasItem"),
PropertyDeclarations(
Contains(IsPropertyDeclaration("objects", fetchTypeId(sourceId2, "QObject")))));
}
TEST_F(ProjectStorage,
synchronize_types_add_indirect_alias_declarations_notifies_error_for_wrong_property_name_tail)
{ {
auto package{createSynchronizationPackageWithIndirectAliases()}; auto package{createSynchronizationPackageWithIndirectAliases()};
storage.synchronize(package); storage.synchronize(package);
package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "objectsWrong"; package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "objectsWrong";
ASSERT_THROW(storage.synchronize(SynchronizationPackage{importsSourceId3, EXPECT_CALL(errorNotifierMock, propertyNameDoesNotExists(Eq("children.objectsWrong"), sourceId3));
{package.types[2]},
{sourceId3}, storage.synchronize(SynchronizationPackage{
moduleDependenciesSourceId3, importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}});
{sourceId3}}), }
QmlDesigner::PropertyNameDoesNotExists);
TEST_F(ProjectStorage,
synchronize_types_add_indirect_alias_declarations_sets_property_declaration_id_to_null_for_the_wrong_property_name_tail)
{
auto package{createSynchronizationPackageWithIndirectAliases()};
storage.synchronize(package);
package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "objectsWrong";
storage.synchronize(SynchronizationPackage{
importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}});
ASSERT_THAT(fetchType(sourceId3, "QAliasItem"),
PropertyDeclarations(Contains(IsPropertyDeclaration("objects", IsNullTypeId()))));
}
TEST_F(ProjectStorage,
synchronize_types_updates_indirect_alias_declarations_fixed_property_declaration_id_for_property_name_tail)
{
auto package{createSynchronizationPackageWithIndirectAliases()};
storage.synchronize(package);
package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "objectsWrong";
storage.synchronize(
{importsSourceId3, {package.types[2]}, {sourceId3}, moduleDependenciesSourceId3, {sourceId3}});
package.types[2].propertyDeclarations[1].aliasPropertyNameTail = "objects";
storage.synchronize(
{importsSourceId3 + moduleDependenciesSourceId3, {package.types[2]}, {sourceId3}});
ASSERT_THAT(fetchType(sourceId3, "QAliasItem"),
PropertyDeclarations(Contains(
IsPropertyDeclaration("objects", Eq(fetchTypeId(sourceId2, "QObject"))))));
} }
TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declaration_type_name) TEST_F(ProjectStorage, synchronize_types_change_indirect_alias_declaration_type_name)
@@ -7407,23 +7515,64 @@ TEST_F(ProjectStorage, synchronize_to_removed_default_property)
Contains(AllOf(HasTypeName(Eq("QQuickItem")), HasDefaultPropertyName(IsEmpty())))); Contains(AllOf(HasTypeName(Eq("QQuickItem")), HasDefaultPropertyName(IsEmpty()))));
} }
TEST_F(ProjectStorage, synchronize_default_property_throws_for_missing_default_property) TEST_F(ProjectStorage, synchronize_default_property_notifies_error_for_missing_default_property)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types.front().defaultPropertyName = "child"; package.types.front().defaultPropertyName = "child";
ASSERT_THROW(storage.synchronize(package), QmlDesigner::PropertyNameDoesNotExists); EXPECT_CALL(errorNotifierMock, missingDefaultProperty(Eq("QQuickItem"), Eq("child"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage, gets_null_default_property_id_for_broken_default_property_name)
{
auto package{createSimpleSynchronizationPackage()};
package.types.front().defaultPropertyName = "child";
storage.synchronize(package);
ASSERT_THAT(defaultPropertyDeclarationId(sourceId1, "QQuickItem"), IsNullPropertyDeclarationId());
}
TEST_F(ProjectStorage, synchronize_default_fixes_default_property_name)
{
auto package{createSimpleSynchronizationPackage()};
package.types.front().defaultPropertyName = "child";
storage.synchronize(package);
package.types.front().defaultPropertyName = "data";
storage.synchronize(package);
ASSERT_THAT(defaultPropertyDeclarationId(sourceId1, "QQuickItem"),
Eq(propertyDeclarationId(sourceId1, "QQuickItem", "data")));
} }
TEST_F(ProjectStorage, TEST_F(ProjectStorage,
synchronize_default_property_throws_for_removing_property_without_changing_default_property) synchronize_default_property_notifies_error_for_removing_property_without_changing_default_property)
{ {
auto package{createSimpleSynchronizationPackage()}; auto package{createSimpleSynchronizationPackage()};
package.types.front().defaultPropertyName = "children"; package.types.front().defaultPropertyName = "children";
storage.synchronize(package); storage.synchronize(package);
removeProperty(package, "QQuickItem", "children"); removeProperty(package, "QQuickItem", "children");
ASSERT_THROW(storage.synchronize(package), QmlDesigner::PropertyNameDoesNotExists); EXPECT_CALL(errorNotifierMock,
missingDefaultProperty(Eq("QQuickItem"), Eq("children"), sourceId1));
storage.synchronize(package);
}
TEST_F(ProjectStorage,
synchronize_default_property_has_null_id_for_removing_property_without_changing_default_property)
{
auto package{createSimpleSynchronizationPackage()};
package.types.front().defaultPropertyName = "children";
storage.synchronize(package);
removeProperty(package, "QQuickItem", "children");
storage.synchronize(package);
ASSERT_THAT(defaultPropertyDeclarationId(sourceId1, "QQuickItem"), IsNullPropertyDeclarationId());
} }
TEST_F(ProjectStorage, synchronize_changes_default_property_and_removes_old_default_property) TEST_F(ProjectStorage, synchronize_changes_default_property_and_removes_old_default_property)