forked from qt-creator/qt-creator
QmlDesiger: ProjectStorage provides properties for typeId
It returns all properties of the type and the prototype chain in a sorted order. Task-nubmer: QDS-7276 Change-Id: I95c16abd16d9d0f1fdf68d3425780a888dc056b7 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -149,6 +149,18 @@ public:
|
|||||||
.template valueWithTransaction<TypeId>(&moduleId, exportedTypeName);
|
.template valueWithTransaction<TypeId>(&moduleId, exportedTypeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropertyDeclarationIds propertyIds(TypeId typeId) const
|
||||||
|
{
|
||||||
|
return selectPropertyDeclarationIdsForTypeStatement
|
||||||
|
.template valuesWithTransaction<PropertyDeclarationId>(32, &typeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const
|
||||||
|
{
|
||||||
|
return selectPropertyNameStatement.template optionalValueWithTransaction<Utils::SmallString>(
|
||||||
|
&propertyDeclarationId);
|
||||||
|
}
|
||||||
|
|
||||||
PropertyDeclarationId fetchPropertyDeclarationByTypeIdAndName(TypeId typeId,
|
PropertyDeclarationId fetchPropertyDeclarationByTypeIdAndName(TypeId typeId,
|
||||||
Utils::SmallStringView name)
|
Utils::SmallStringView name)
|
||||||
{
|
{
|
||||||
@@ -2658,7 +2670,7 @@ public:
|
|||||||
"aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, "
|
"aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, "
|
||||||
"propertyImportedTypeNameId",
|
"propertyImportedTypeNameId",
|
||||||
database};
|
database};
|
||||||
ReadStatement<1, 1> selectPropertyNameStatement{
|
mutable ReadStatement<1, 1> selectPropertyNameStatement{
|
||||||
"SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
|
"SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
|
||||||
WriteStatement<2> updatePropertyDeclarationTypeStatement{
|
WriteStatement<2> updatePropertyDeclarationTypeStatement{
|
||||||
"UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
|
"UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database};
|
||||||
@@ -2845,6 +2857,16 @@ public:
|
|||||||
" moduleExportedImportId "
|
" moduleExportedImportId "
|
||||||
"FROM imports",
|
"FROM imports",
|
||||||
database};
|
database};
|
||||||
|
mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForTypeStatement{
|
||||||
|
"WITH RECURSIVE "
|
||||||
|
" typeChain(typeId) AS ("
|
||||||
|
" VALUES(?1)"
|
||||||
|
" UNION ALL "
|
||||||
|
" SELECT prototypeId FROM types JOIN typeChain "
|
||||||
|
" USING(typeId) WHERE prototypeId IS NOT NULL)"
|
||||||
|
"SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations "
|
||||||
|
" USING(typeId) ORDER BY propertyDeclarationId",
|
||||||
|
database};
|
||||||
};
|
};
|
||||||
extern template class ProjectStorage<Sqlite::Database>;
|
extern template class ProjectStorage<Sqlite::Database>;
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
|||||||
@@ -176,6 +176,51 @@ MATCHER_P4(IsPropertyDeclaration,
|
|||||||
&& propertyDeclaration.traits == traits;
|
&& propertyDeclaration.traits == traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HasNameMatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using is_gtest_matcher = void;
|
||||||
|
|
||||||
|
HasNameMatcher(const QmlDesigner::ProjectStorage<Sqlite::Database> &storage,
|
||||||
|
Utils::SmallStringView name)
|
||||||
|
: storage{storage}
|
||||||
|
, name{name}
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool MatchAndExplain(QmlDesigner::PropertyDeclarationId id, std::ostream *listener) const
|
||||||
|
{
|
||||||
|
auto propertyName = storage.propertyName(id);
|
||||||
|
bool success = propertyName && *propertyName == name;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (listener) {
|
||||||
|
if (propertyName)
|
||||||
|
*listener << "name is '" << *propertyName << "', not '" << name << "'";
|
||||||
|
else
|
||||||
|
*listener << "there is no '" << name << "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeTo(std::ostream *os) const { *os << "is '" << name << "'"; }
|
||||||
|
|
||||||
|
void DescribeNegationTo(std::ostream *os) const { *os << "is not '" << name << "'"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QmlDesigner::ProjectStorage<Sqlite::Database> &storage;
|
||||||
|
Utils::SmallStringView name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HasName(name) Matcher<QmlDesigner::PropertyDeclarationId>(HasNameMatcher{storage, name})
|
||||||
|
|
||||||
|
MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted"))
|
||||||
|
{
|
||||||
|
return std::is_sorted(begin(arg), end(arg));
|
||||||
|
}
|
||||||
|
|
||||||
class ProjectStorage : public testing::Test
|
class ProjectStorage : public testing::Test
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -577,6 +622,59 @@ protected:
|
|||||||
return package;
|
return package;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto createPackageWithProperties()
|
||||||
|
{
|
||||||
|
SynchronizationPackage package;
|
||||||
|
|
||||||
|
package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1);
|
||||||
|
|
||||||
|
package.types.push_back(Storage::Type{
|
||||||
|
"QObject",
|
||||||
|
Storage::ImportedType{},
|
||||||
|
TypeAccessSemantics::Reference,
|
||||||
|
sourceId1,
|
||||||
|
{Storage::ExportedType{qmlModuleId, "Object", Storage::Version{}}},
|
||||||
|
{Storage::PropertyDeclaration{"data",
|
||||||
|
Storage::ImportedType{"Object"},
|
||||||
|
Storage::PropertyDeclarationTraits::IsList},
|
||||||
|
Storage::PropertyDeclaration{"children",
|
||||||
|
Storage::ImportedType{"Object"},
|
||||||
|
Storage::PropertyDeclarationTraits::IsList
|
||||||
|
| Storage::PropertyDeclarationTraits::IsReadOnly}}});
|
||||||
|
package.types.push_back(Storage::Type{
|
||||||
|
"QObject2",
|
||||||
|
Storage::ImportedType{"Object"},
|
||||||
|
TypeAccessSemantics::Reference,
|
||||||
|
sourceId1,
|
||||||
|
{Storage::ExportedType{qmlModuleId, "Object2", Storage::Version{}}},
|
||||||
|
{Storage::PropertyDeclaration{"data2",
|
||||||
|
Storage::ImportedType{"Object3"},
|
||||||
|
Storage::PropertyDeclarationTraits::IsList},
|
||||||
|
Storage::PropertyDeclaration{"children2",
|
||||||
|
Storage::ImportedType{"Object3"},
|
||||||
|
Storage::PropertyDeclarationTraits::IsList
|
||||||
|
| Storage::PropertyDeclarationTraits::IsReadOnly}}});
|
||||||
|
package.types.push_back(Storage::Type{
|
||||||
|
"QObject3",
|
||||||
|
Storage::ImportedType{"Object2"},
|
||||||
|
TypeAccessSemantics::Reference,
|
||||||
|
sourceId1,
|
||||||
|
{Storage::ExportedType{qmlModuleId, "Object3", Storage::Version{}}},
|
||||||
|
{Storage::PropertyDeclaration{"data3",
|
||||||
|
Storage::ImportedType{"Object2"},
|
||||||
|
Storage::PropertyDeclarationTraits::IsList},
|
||||||
|
Storage::PropertyDeclaration{"children3",
|
||||||
|
Storage::ImportedType{"Object2"},
|
||||||
|
Storage::PropertyDeclarationTraits::IsList
|
||||||
|
| Storage::PropertyDeclarationTraits::IsReadOnly}}});
|
||||||
|
|
||||||
|
package.updatedSourceIds.push_back(sourceId1);
|
||||||
|
|
||||||
|
shuffle(package.types);
|
||||||
|
|
||||||
|
return package;
|
||||||
|
}
|
||||||
|
|
||||||
auto createModuleExportedImportSynchronizationPackage()
|
auto createModuleExportedImportSynchronizationPackage()
|
||||||
{
|
{
|
||||||
SynchronizationPackage package;
|
SynchronizationPackage package;
|
||||||
@@ -5047,4 +5145,58 @@ TEST_F(ProjectStorage, GetNoTypeIdWithCompleteVersionForWrongMajorVersion)
|
|||||||
ASSERT_FALSE(typeId);
|
ASSERT_FALSE(typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorage, GetProperties)
|
||||||
|
{
|
||||||
|
auto package{createPackageWithProperties()};
|
||||||
|
storage.synchronize(package);
|
||||||
|
auto itemTypeId = fetchTypeId(sourceId1, "QObject3");
|
||||||
|
|
||||||
|
auto propertyIds = storage.propertyIds(itemTypeId);
|
||||||
|
|
||||||
|
ASSERT_THAT(propertyIds,
|
||||||
|
UnorderedElementsAre(HasName("data"),
|
||||||
|
HasName("children"),
|
||||||
|
HasName("data2"),
|
||||||
|
HasName("children2"),
|
||||||
|
HasName("data3"),
|
||||||
|
HasName("children3")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorage, GetPropertiesAreReturnedSorted)
|
||||||
|
{
|
||||||
|
auto package{createPackageWithProperties()};
|
||||||
|
storage.synchronize(package);
|
||||||
|
auto itemTypeId = fetchTypeId(sourceId1, "QObject3");
|
||||||
|
|
||||||
|
auto propertyIds = storage.propertyIds(itemTypeId);
|
||||||
|
|
||||||
|
ASSERT_THAT(propertyIds, IsSorted());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorage, GetNoPropertiesPropertiesFromDerivedTypes)
|
||||||
|
{
|
||||||
|
auto package{createPackageWithProperties()};
|
||||||
|
storage.synchronize(package);
|
||||||
|
auto itemTypeId = fetchTypeId(sourceId1, "QObject2");
|
||||||
|
|
||||||
|
auto propertyIds = storage.propertyIds(itemTypeId);
|
||||||
|
|
||||||
|
ASSERT_THAT(propertyIds,
|
||||||
|
UnorderedElementsAre(HasName("data"),
|
||||||
|
HasName("children"),
|
||||||
|
HasName("data2"),
|
||||||
|
HasName("children2")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ProjectStorage, GetNoPropertiesForWrongTypeId)
|
||||||
|
{
|
||||||
|
auto package{createPackageWithProperties()};
|
||||||
|
storage.synchronize(package);
|
||||||
|
auto itemTypeId = fetchTypeId(sourceId1, "WrongObject");
|
||||||
|
|
||||||
|
auto propertyIds = storage.propertyIds(itemTypeId);
|
||||||
|
|
||||||
|
ASSERT_THAT(propertyIds, IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user