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;
|
TypeIds updatedTypeIds;
|
||||||
updatedTypeIds.reserve(types.size());
|
updatedTypeIds.reserve(types.size());
|
||||||
|
|
||||||
for (auto &&type : types)
|
for (auto &&type : types) {
|
||||||
|
if (!type.sourceId)
|
||||||
|
throw TypeHasInvalidSourceId{};
|
||||||
|
|
||||||
updatedTypeIds.push_back(syncType(type));
|
updatedTypeIds.push_back(syncType(type));
|
||||||
|
}
|
||||||
|
|
||||||
deleteNotUpdatedTypes(updatedTypeIds, sourceIds);
|
deleteNotUpdatedTypes(updatedTypeIds, sourceIds);
|
||||||
|
|
||||||
@@ -262,8 +266,17 @@ private:
|
|||||||
return &sourceId;
|
return &sourceId;
|
||||||
});
|
});
|
||||||
|
|
||||||
deleteNotUpdatedTypesInSourcesStatement.write(Utils::span(sourceIdValues),
|
auto removedTypeIds = selectNotUpdatedTypesInSourcesStatement.template range<TypeId>(
|
||||||
Utils::span(updatedTypeIdValues));
|
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)
|
void upsertExportedType(Utils::SmallStringView qualifiedName, Storage::Version version, TypeId typeId)
|
||||||
@@ -689,7 +702,6 @@ private:
|
|||||||
Sqlite::ForeignKeyAction::Restrict,
|
Sqlite::ForeignKeyAction::Restrict,
|
||||||
Sqlite::ForeignKeyAction::Restrict,
|
Sqlite::ForeignKeyAction::Restrict,
|
||||||
Sqlite::Enforment::Deferred);
|
Sqlite::Enforment::Deferred);
|
||||||
table.addColumn("defaultProperty");
|
|
||||||
|
|
||||||
table.addUniqueIndex({nameColumn});
|
table.addUniqueIndex({nameColumn});
|
||||||
|
|
||||||
@@ -864,9 +876,21 @@ public:
|
|||||||
"SELECT name, typeId, (SELECT name FROM types WHERE typeId=outerTypes.prototypeId),"
|
"SELECT name, typeId, (SELECT name FROM types WHERE typeId=outerTypes.prototypeId),"
|
||||||
"accessSemantics, ifnull(sourceId, -1) FROM types AS outerTypes",
|
"accessSemantics, ifnull(sourceId, -1) FROM types AS outerTypes",
|
||||||
database};
|
database};
|
||||||
WriteStatement deleteNotUpdatedTypesInSourcesStatement{
|
ReadStatement<1> selectNotUpdatedTypesInSourcesStatement{
|
||||||
"DELETE FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2)) OR sourceId "
|
"SELECT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2))",
|
||||||
"IS NULL",
|
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};
|
database};
|
||||||
mutable ReadStatement<3> selectPropertyDeclarationsByTypeIdStatement{
|
mutable ReadStatement<3> selectPropertyDeclarationsByTypeIdStatement{
|
||||||
"SELECT name, (SELECT name FROM types WHERE typeId=propertyDeclarations.propertyTypeId),"
|
"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
|
} // namespace QmlDesigner
|
||||||
|
@@ -82,6 +82,19 @@ MATCHER_P4(IsStorageType,
|
|||||||
&& type.accessSemantics == accessSemantics && type.sourceId == sourceId;
|
&& 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,
|
MATCHER_P2(IsExportedType,
|
||||||
qualifiedTypeName,
|
qualifiedTypeName,
|
||||||
version,
|
version,
|
||||||
@@ -415,16 +428,6 @@ TEST_F(ProjectStorage,
|
|||||||
storage.fetchSourceContextId("/other/unknow/path");
|
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)
|
TEST_F(ProjectStorage, FetchTypeByTypeIdCalls)
|
||||||
{
|
{
|
||||||
InSequence s;
|
InSequence s;
|
||||||
@@ -1271,8 +1274,9 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesInsertTypeIntoPrototypeChain)
|
|||||||
IsExportedType("QtQuick.Item", Storage::Version{5, 15}))))));
|
IsExportedType("QtQuick.Item", Storage::Version{5, 15}))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ProjectStorageSlowTest, SynchronizeTypesThrowsForMissingPrototype)
|
TEST_F(ProjectStorageSlowTest, SynchronizeTypesDontThrowsForMissingPrototype)
|
||||||
{
|
{
|
||||||
|
sourceId1 = sourcePathCache.sourceId(path1);
|
||||||
Storage::Types types{
|
Storage::Types types{
|
||||||
Storage::Type{"QQuickItem",
|
Storage::Type{"QQuickItem",
|
||||||
"QObject",
|
"QObject",
|
||||||
@@ -1280,19 +1284,10 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesThrowsForMissingPrototype)
|
|||||||
sourceId1,
|
sourceId1,
|
||||||
{Storage::ExportedType{"QtQuick.Item", Storage::Version{5, 15}}}}};
|
{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)
|
TEST_F(ProjectStorageSlowTest, TypeWithInvalidSourceIdThrows)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Storage::Types types{
|
Storage::Types types{
|
||||||
Storage::Type{"QQuickItem",
|
Storage::Type{"QQuickItem",
|
||||||
@@ -1301,9 +1296,51 @@ TEST_F(ProjectStorageSlowTest, DontAddTypeWithInvalidSourceId)
|
|||||||
SourceId{},
|
SourceId{},
|
||||||
{Storage::ExportedType{"QtQuick.Item", Storage::Version{5, 15}}}}};
|
{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)
|
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddPropertyDeclarations)
|
||||||
|
Reference in New Issue
Block a user