forked from qt-creator/qt-creator
QmlDesigner: Synchronize type
A type should not removed but all it's fields should be reseted. This has the advantage that if you remove a file and add it again later the id is stable. Change-Id: I3a1f51e14195cff2fc39681acdf7072d1a2e4616 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -63,8 +63,12 @@ public:
|
||||
TypeIds updatedTypeIds;
|
||||
updatedTypeIds.reserve(types.size());
|
||||
|
||||
for (auto &&type : types)
|
||||
for (auto &&type : types) {
|
||||
if (!type.sourceId)
|
||||
throw TypeHasInvalidSourceId{};
|
||||
|
||||
updatedTypeIds.push_back(syncType(type));
|
||||
}
|
||||
|
||||
deleteNotUpdatedTypes(updatedTypeIds, sourceIds);
|
||||
|
||||
@@ -262,8 +266,17 @@ private:
|
||||
return &sourceId;
|
||||
});
|
||||
|
||||
deleteNotUpdatedTypesInSourcesStatement.write(Utils::span(sourceIdValues),
|
||||
Utils::span(updatedTypeIdValues));
|
||||
auto removedTypeIds = selectNotUpdatedTypesInSourcesStatement.template range<TypeId>(
|
||||
Utils::span(sourceIdValues), Utils::span(updatedTypeIdValues));
|
||||
|
||||
for (TypeId typeId : removedTypeIds) {
|
||||
resetTypeStatement.write(&typeId);
|
||||
deleteExportTypesByTypeIdStatement.write(&typeId);
|
||||
deleteEnumerationDeclarationByTypeIdStatement.write(&typeId);
|
||||
deletePropertyDeclarationByTypeIdStatement.write(&typeId);
|
||||
deleteFunctionDeclarationByTypeIdStatement.write(&typeId);
|
||||
deleteSignalDeclarationByTypeIdStatement.write(&typeId);
|
||||
}
|
||||
}
|
||||
|
||||
void upsertExportedType(Utils::SmallStringView qualifiedName, Storage::Version version, TypeId typeId)
|
||||
@@ -689,7 +702,6 @@ private:
|
||||
Sqlite::ForeignKeyAction::Restrict,
|
||||
Sqlite::ForeignKeyAction::Restrict,
|
||||
Sqlite::Enforment::Deferred);
|
||||
table.addColumn("defaultProperty");
|
||||
|
||||
table.addUniqueIndex({nameColumn});
|
||||
|
||||
@@ -864,9 +876,21 @@ public:
|
||||
"SELECT name, typeId, (SELECT name FROM types WHERE typeId=outerTypes.prototypeId),"
|
||||
"accessSemantics, ifnull(sourceId, -1) FROM types AS outerTypes",
|
||||
database};
|
||||
WriteStatement deleteNotUpdatedTypesInSourcesStatement{
|
||||
"DELETE FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2)) OR sourceId "
|
||||
"IS NULL",
|
||||
ReadStatement<1> selectNotUpdatedTypesInSourcesStatement{
|
||||
"SELECT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2))",
|
||||
database};
|
||||
WriteStatement deleteExportTypesByTypeIdStatement{"DELETE FROM exportedTypes WHERE typeId=?",
|
||||
database};
|
||||
WriteStatement deleteEnumerationDeclarationByTypeIdStatement{
|
||||
"DELETE FROM enumerationDeclarations WHERE typeId=?", database};
|
||||
WriteStatement deletePropertyDeclarationByTypeIdStatement{
|
||||
"DELETE FROM propertyDeclarations WHERE typeId=?", database};
|
||||
WriteStatement deleteFunctionDeclarationByTypeIdStatement{
|
||||
"DELETE FROM functionDeclarations WHERE typeId=?", database};
|
||||
WriteStatement deleteSignalDeclarationByTypeIdStatement{
|
||||
"DELETE FROM signalDeclarations WHERE typeId=?", database};
|
||||
WriteStatement resetTypeStatement{
|
||||
"UPDATE types SET accessSemantics=NULL, sourceId=NULL, prototypeId=NULL WHERE typeId=?",
|
||||
database};
|
||||
mutable ReadStatement<3> selectPropertyDeclarationsByTypeIdStatement{
|
||||
"SELECT name, (SELECT name FROM types WHERE typeId=propertyDeclarations.propertyTypeId),"
|
||||
|
@@ -65,4 +65,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TypeHasInvalidSourceId : std::exception
|
||||
{
|
||||
public:
|
||||
const char *what() const noexcept override { return "The source id is invalid!"; }
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -82,6 +82,19 @@ MATCHER_P4(IsStorageType,
|
||||
&& type.accessSemantics == accessSemantics && type.sourceId == sourceId;
|
||||
}
|
||||
|
||||
MATCHER_P3(IsStorageTypeWithInvalidSourceId,
|
||||
typeName,
|
||||
prototype,
|
||||
accessSemantics,
|
||||
std::string(negation ? "isn't " : "is ")
|
||||
+ PrintToString(Storage::Type{typeName, prototype, accessSemantics, SourceId{}}))
|
||||
{
|
||||
const Storage::Type &type = arg;
|
||||
|
||||
return type.typeName == typeName && type.prototype == prototype
|
||||
&& type.accessSemantics == accessSemantics && !type.sourceId.isValid();
|
||||
}
|
||||
|
||||
MATCHER_P2(IsExportedType,
|
||||
qualifiedTypeName,
|
||||
version,
|
||||
@@ -415,16 +428,6 @@ TEST_F(ProjectStorage,
|
||||
storage.fetchSourceContextId("/other/unknow/path");
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, SynchronizeTypesCalls)
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(databaseMock, immediateBegin());
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.synchronizeTypes({}, {});
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorage, FetchTypeByTypeIdCalls)
|
||||
{
|
||||
InSequence s;
|
||||
@@ -1271,8 +1274,9 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesInsertTypeIntoPrototypeChain)
|
||||
IsExportedType("QtQuick.Item", Storage::Version{5, 15}))))));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, SynchronizeTypesThrowsForMissingPrototype)
|
||||
TEST_F(ProjectStorageSlowTest, SynchronizeTypesDontThrowsForMissingPrototype)
|
||||
{
|
||||
sourceId1 = sourcePathCache.sourceId(path1);
|
||||
Storage::Types types{
|
||||
Storage::Type{"QQuickItem",
|
||||
"QObject",
|
||||
@@ -1280,19 +1284,10 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesThrowsForMissingPrototype)
|
||||
sourceId1,
|
||||
{Storage::ExportedType{"QtQuick.Item", Storage::Version{5, 15}}}}};
|
||||
|
||||
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1}), Sqlite::ConstraintPreventsModification);
|
||||
ASSERT_NO_THROW(storage.synchronizeTypes(types, {sourceId1}));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, SynchronizeTypesThrowsForWrongPrototypeChange)
|
||||
{
|
||||
Storage::Types types{createTypes()};
|
||||
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||
types[0].prototype = "QQuickObject";
|
||||
|
||||
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1}), Sqlite::ConstraintPreventsModification);
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, DontAddTypeWithInvalidSourceId)
|
||||
TEST_F(ProjectStorageSlowTest, TypeWithInvalidSourceIdThrows)
|
||||
{
|
||||
Storage::Types types{
|
||||
Storage::Type{"QQuickItem",
|
||||
@@ -1301,9 +1296,51 @@ TEST_F(ProjectStorageSlowTest, DontAddTypeWithInvalidSourceId)
|
||||
SourceId{},
|
||||
{Storage::ExportedType{"QtQuick.Item", Storage::Version{5, 15}}}}};
|
||||
|
||||
storage.synchronizeTypes(types, {});
|
||||
ASSERT_THROW(storage.synchronizeTypes(types, {}), QmlDesigner::TypeHasInvalidSourceId);
|
||||
}
|
||||
|
||||
ASSERT_THAT(storage.fetchTypes(), IsEmpty());
|
||||
TEST_F(ProjectStorageSlowTest, ResetsTypeIfTypeIsRemoved)
|
||||
{
|
||||
Storage::Types types{createTypes()};
|
||||
types[1].enumerationDeclarations = types[0].enumerationDeclarations;
|
||||
types[1].propertyDeclarations = types[0].propertyDeclarations;
|
||||
types[1].functionDeclarations = types[0].functionDeclarations;
|
||||
types[1].signalDeclarations = types[0].signalDeclarations;
|
||||
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||
types.pop_back();
|
||||
|
||||
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||
|
||||
ASSERT_THAT(storage.fetchTypes(),
|
||||
Contains(
|
||||
AllOf(IsStorageTypeWithInvalidSourceId("QObject", "", TypeAccessSemantics::Invalid),
|
||||
Field(&Storage::Type::exportedTypes, IsEmpty()),
|
||||
Field(&Storage::Type::enumerationDeclarations, IsEmpty()),
|
||||
Field(&Storage::Type::propertyDeclarations, IsEmpty()),
|
||||
Field(&Storage::Type::functionDeclarations, IsEmpty()),
|
||||
Field(&Storage::Type::signalDeclarations, IsEmpty()))));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, DontResetsTypeIfSourceIdIsNotSynchronized)
|
||||
{
|
||||
Storage::Types types{createTypes()};
|
||||
types[1].enumerationDeclarations = types[0].enumerationDeclarations;
|
||||
types[1].propertyDeclarations = types[0].propertyDeclarations;
|
||||
types[1].functionDeclarations = types[0].functionDeclarations;
|
||||
types[1].signalDeclarations = types[0].signalDeclarations;
|
||||
storage.synchronizeTypes(types, {sourceId1, sourceId2});
|
||||
types.pop_back();
|
||||
|
||||
storage.synchronizeTypes(types, {sourceId1});
|
||||
|
||||
ASSERT_THAT(storage.fetchTypes(),
|
||||
Contains(AllOf(IsStorageType("QObject", "", TypeAccessSemantics::Reference, sourceId2),
|
||||
Field(&Storage::Type::exportedTypes, Not(IsEmpty())),
|
||||
Field(&Storage::Type::exportedTypes, Not(IsEmpty())),
|
||||
Field(&Storage::Type::enumerationDeclarations, Not(IsEmpty())),
|
||||
Field(&Storage::Type::propertyDeclarations, Not(IsEmpty())),
|
||||
Field(&Storage::Type::functionDeclarations, Not(IsEmpty())),
|
||||
Field(&Storage::Type::signalDeclarations, Not(IsEmpty())))));
|
||||
}
|
||||
|
||||
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddPropertyDeclarations)
|
||||
|
Reference in New Issue
Block a user