forked from qt-creator/qt-creator
QmlDesigner: Add callbacks for model resource management
As a node or property is removed there are now callbacks to generate node, properties and expressions which should removed or adapt too. Task-number: QDS-9766 Change-Id: I6d842006a6282af00ff644ffaa0f3102e14f13fa Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -341,6 +341,7 @@ extend_qtc_library(QmlDesignerCore
|
||||
modelnodepositionrecalculator.cpp
|
||||
modelnodepositionrecalculator.h
|
||||
modelnodepositionstorage.cpp
|
||||
modelresourcemanagementinterface.h
|
||||
modeltotextmerger.cpp
|
||||
modeltotextmerger.h
|
||||
modelutils.cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <qmldesignercorelib_global.h>
|
||||
|
||||
#include <documentmessage.h>
|
||||
#include <model/modelresourcemanagementinterface.h>
|
||||
#include <projectstorage/projectstoragefwd.h>
|
||||
|
||||
#include <QMimeData>
|
||||
@@ -40,7 +41,7 @@ class RewriterView;
|
||||
class NodeInstanceView;
|
||||
class TextModifier;
|
||||
|
||||
using PropertyListType = QList<QPair<PropertyName, QVariant> >;
|
||||
using PropertyListType = QList<QPair<PropertyName, QVariant>>;
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT Model : public QObject
|
||||
{
|
||||
@@ -61,25 +62,34 @@ public:
|
||||
const TypeName &type,
|
||||
int major = 1,
|
||||
int minor = 1,
|
||||
Model *metaInfoProxyModel = nullptr);
|
||||
Model(const TypeName &typeName, int major = 1, int minor = 1, Model *metaInfoProxyModel = nullptr);
|
||||
Model *metaInfoProxyModel = nullptr,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {});
|
||||
Model(const TypeName &typeName,
|
||||
int major = 1,
|
||||
int minor = 1,
|
||||
Model *metaInfoProxyModel = nullptr,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {});
|
||||
|
||||
~Model();
|
||||
|
||||
static ModelPointer create(const TypeName &typeName,
|
||||
int major = 1,
|
||||
int minor = 1,
|
||||
Model *metaInfoProxyModel = nullptr)
|
||||
Model *metaInfoProxyModel = nullptr,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
|
||||
{
|
||||
return ModelPointer(new Model(typeName, major, minor, metaInfoProxyModel));
|
||||
return ModelPointer(
|
||||
new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)));
|
||||
}
|
||||
|
||||
static ModelPointer create(ProjectStorageType &projectStorage,
|
||||
const TypeName &typeName,
|
||||
int major = 1,
|
||||
int minor = 1)
|
||||
int minor = 1,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {})
|
||||
{
|
||||
return ModelPointer(new Model(projectStorage, typeName, major, minor));
|
||||
return ModelPointer(
|
||||
new Model(projectStorage, typeName, major, minor, nullptr, std::move(resourceManagement)));
|
||||
}
|
||||
|
||||
QUrl fileUrl() const;
|
||||
@@ -120,6 +130,8 @@ public:
|
||||
void attachView(AbstractView *view);
|
||||
void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView);
|
||||
|
||||
QList<ModelNode> allModelNodes() const;
|
||||
|
||||
// Editing sub-components:
|
||||
|
||||
// Imports:
|
||||
@@ -177,4 +189,4 @@ private:
|
||||
std::unique_ptr<Internal::ModelPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace QmlDesigner
|
||||
|
||||
@@ -270,6 +270,8 @@ QMLDESIGNERCORE_EXPORT bool operator !=(const ModelNode &firstNode, const ModelN
|
||||
QMLDESIGNERCORE_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode);
|
||||
QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode);
|
||||
QMLDESIGNERCORE_EXPORT QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode);
|
||||
|
||||
using ModelNodes = QList<ModelNode>;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QmlDesigner::ModelNode)
|
||||
|
||||
@@ -1619,16 +1619,20 @@ TypeName NodeMetaInfo::simplifiedTypeName() const
|
||||
|
||||
int NodeMetaInfo::majorVersion() const
|
||||
{
|
||||
if (isValid())
|
||||
return m_privateData->majorVersion();
|
||||
if constexpr (!useProjectStorage()) {
|
||||
if (isValid())
|
||||
return m_privateData->majorVersion();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int NodeMetaInfo::minorVersion() const
|
||||
{
|
||||
if (isValid())
|
||||
return m_privateData->minorVersion();
|
||||
if constexpr (!useProjectStorage()) {
|
||||
if (isValid())
|
||||
return m_privateData->minorVersion();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ AbstractProperty::AbstractProperty(const Internal::InternalPropertyPointer &prop
|
||||
m_model(model),
|
||||
m_view(view)
|
||||
{
|
||||
Q_ASSERT(!m_model || m_view);
|
||||
}
|
||||
|
||||
AbstractProperty::AbstractProperty(const AbstractProperty &property, AbstractView *view)
|
||||
|
||||
@@ -64,8 +64,12 @@ RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &ide
|
||||
|
||||
ModelNode AbstractView::createModelNode(const TypeName &typeName)
|
||||
{
|
||||
const NodeMetaInfo metaInfo = model()->metaInfo(typeName);
|
||||
return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
|
||||
if constexpr (useProjectStorage()) {
|
||||
return createModelNode(typeName, -1, -1);
|
||||
} else {
|
||||
const NodeMetaInfo metaInfo = model()->metaInfo(typeName);
|
||||
return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion());
|
||||
}
|
||||
}
|
||||
|
||||
ModelNode AbstractView::createModelNode(const TypeName &typeName,
|
||||
|
||||
@@ -56,7 +56,7 @@ void BindingProperty::setExpression(const QString &expression)
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->setBindingProperty(internalNode(), name(), expression);
|
||||
}
|
||||
@@ -341,7 +341,7 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName,
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->setDynamicBindingProperty(internalNode(), name(), typeName, expression);
|
||||
}
|
||||
|
||||
@@ -62,9 +62,11 @@ ModelPrivate::ModelPrivate(Model *model,
|
||||
const TypeName &typeName,
|
||||
int major,
|
||||
int minor,
|
||||
Model *metaInfoProxyModel)
|
||||
Model *metaInfoProxyModel,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
|
||||
: projectStorage{&projectStorage}
|
||||
, m_model{model}
|
||||
, m_resourceManagement{std::move(resourceManagement)}
|
||||
{
|
||||
m_metaInfoProxyModel = metaInfoProxyModel;
|
||||
|
||||
@@ -75,9 +77,14 @@ ModelPrivate::ModelPrivate(Model *model,
|
||||
m_currentTimelineNode = m_rootInternalNode;
|
||||
}
|
||||
|
||||
ModelPrivate::ModelPrivate(
|
||||
Model *model, const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel)
|
||||
ModelPrivate::ModelPrivate(Model *model,
|
||||
const TypeName &typeName,
|
||||
int major,
|
||||
int minor,
|
||||
Model *metaInfoProxyModel,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
|
||||
: m_model(model)
|
||||
, m_resourceManagement{std::move(resourceManagement)}
|
||||
{
|
||||
m_metaInfoProxyModel = metaInfoProxyModel;
|
||||
|
||||
@@ -279,7 +286,9 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName,
|
||||
notifyNodeCreated(newNode);
|
||||
|
||||
if (!newNode->propertyNameList().isEmpty())
|
||||
notifyVariantPropertiesChanged(newNode, newNode->propertyNameList(), AbstractView::PropertiesAdded);
|
||||
notifyVariantPropertiesChanged(newNode,
|
||||
newNode->propertyNameList(),
|
||||
AbstractView::PropertiesAdded);
|
||||
|
||||
return newNode;
|
||||
}
|
||||
@@ -303,16 +312,40 @@ EnabledViewRange ModelPrivate::enabledViews() const
|
||||
return EnabledViewRange{m_viewList};
|
||||
}
|
||||
|
||||
void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet)
|
||||
{
|
||||
for (const ModelNode &node : resourceSet.removeModelNodes) {
|
||||
if (node)
|
||||
removeNode(node.m_internalNode);
|
||||
}
|
||||
|
||||
for (const AbstractProperty &property : resourceSet.removeProperties) {
|
||||
if (property)
|
||||
removeProperty(property.m_internalNode->property(property.m_propertyName));
|
||||
}
|
||||
|
||||
for (const auto &[property, expression] : resourceSet.setExpressions) {
|
||||
if (property)
|
||||
setBindingProperty(property.m_internalNode, property.m_propertyName, expression);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node)
|
||||
{
|
||||
for (const InternalNodePointer &subNode : node->allSubNodes())
|
||||
removeNodeFromModel(subNode);
|
||||
}
|
||||
|
||||
void ModelPrivate::removeNodeAndRelatedResources(const InternalNodePointer &node)
|
||||
{
|
||||
if (m_resourceManagement)
|
||||
handleResourceSet(m_resourceManagement->removeNode(ModelNode{node, m_model, nullptr}));
|
||||
else
|
||||
removeNode(node);
|
||||
}
|
||||
|
||||
void ModelPrivate::removeNode(const InternalNodePointer &node)
|
||||
{
|
||||
Q_ASSERT(node);
|
||||
|
||||
AbstractView::PropertyChangeFlags propertyChangeFlags = AbstractView::NoAdditionalChanges;
|
||||
|
||||
notifyNodeAboutToBeRemoved(node);
|
||||
@@ -1088,6 +1121,15 @@ static QList<PropertyPair> toPropertyPairList(const QList<InternalPropertyPointe
|
||||
return propertyPairList;
|
||||
}
|
||||
|
||||
void ModelPrivate::removePropertyAndRelatedResources(const InternalPropertyPointer &property)
|
||||
{
|
||||
if (m_resourceManagement)
|
||||
handleResourceSet(
|
||||
m_resourceManagement->removeProperty(AbstractProperty{property, m_model, nullptr}));
|
||||
else
|
||||
removeProperty(property);
|
||||
}
|
||||
|
||||
void ModelPrivate::removeProperty(const InternalPropertyPointer &property)
|
||||
{
|
||||
notifyPropertiesAboutToBeRemoved({property});
|
||||
@@ -1408,13 +1450,19 @@ Model::Model(ProjectStorageType &projectStorage,
|
||||
const TypeName &typeName,
|
||||
int major,
|
||||
int minor,
|
||||
Model *metaInfoProxyModel)
|
||||
Model *metaInfoProxyModel,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
|
||||
: d(std::make_unique<Internal::ModelPrivate>(
|
||||
this, projectStorage, typeName, major, minor, metaInfoProxyModel))
|
||||
this, projectStorage, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)))
|
||||
{}
|
||||
|
||||
Model::Model(const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel)
|
||||
: d(std::make_unique<Internal::ModelPrivate>(this, typeName, major, minor, metaInfoProxyModel))
|
||||
Model::Model(const TypeName &typeName,
|
||||
int major,
|
||||
int minor,
|
||||
Model *metaInfoProxyModel,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement)
|
||||
: d(std::make_unique<Internal::ModelPrivate>(
|
||||
this, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement)))
|
||||
{}
|
||||
|
||||
Model::~Model() = default;
|
||||
@@ -2156,4 +2204,9 @@ void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify)
|
||||
d->detachView(view, emitNotify);
|
||||
}
|
||||
|
||||
QList<ModelNode> Model::allModelNodes() const
|
||||
{
|
||||
return QmlDesigner::toModelNodeList(d->allNodes(), nullptr);
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
||||
@@ -101,8 +101,14 @@ public:
|
||||
const TypeName &type,
|
||||
int major,
|
||||
int minor,
|
||||
Model *metaInfoProxyModel);
|
||||
ModelPrivate(Model *model, const TypeName &type, int major, int minor, Model *metaInfoProxyModel);
|
||||
Model *metaInfoProxyModel,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement);
|
||||
ModelPrivate(Model *model,
|
||||
const TypeName &type,
|
||||
int major,
|
||||
int minor,
|
||||
Model *metaInfoProxyModel,
|
||||
std::unique_ptr<ModelResourceManagementInterface> resourceManagement);
|
||||
|
||||
~ModelPrivate() override;
|
||||
|
||||
@@ -120,6 +126,7 @@ public:
|
||||
bool isRootNode = false);
|
||||
|
||||
/*factory methods for internal use in model and rewriter*/
|
||||
void removeNodeAndRelatedResources(const InternalNodePointer &node);
|
||||
void removeNode(const InternalNodePointer &node);
|
||||
void changeNodeId(const InternalNodePointer &node, const QString &id);
|
||||
void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion);
|
||||
@@ -235,6 +242,7 @@ public:
|
||||
//node state property manipulation
|
||||
void addProperty(const InternalNodePointer &node, const PropertyName &name);
|
||||
void setPropertyValue(const InternalNodePointer &node,const PropertyName &name, const QVariant &value);
|
||||
void removePropertyAndRelatedResources(const InternalPropertyPointer &property);
|
||||
void removeProperty(const InternalPropertyPointer &property);
|
||||
|
||||
void setBindingProperty(const InternalNodePointer &node, const PropertyName &name, const QString &expression);
|
||||
@@ -282,6 +290,7 @@ private:
|
||||
QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &nodeVector, AbstractView *view) const;
|
||||
QVector<InternalNodePointer> toInternalNodeVector(const QVector<ModelNode> &modelNodeVector) const;
|
||||
EnabledViewRange enabledViews() const;
|
||||
void handleResourceSet(const ModelResourceSet &resourceSet);
|
||||
|
||||
public:
|
||||
NotNullPointer<ProjectStorageType> projectStorage = nullptr;
|
||||
@@ -300,6 +309,7 @@ private:
|
||||
InternalNodePointer m_currentStateNode;
|
||||
InternalNodePointer m_rootInternalNode;
|
||||
InternalNodePointer m_currentTimelineNode;
|
||||
std::unique_ptr<ModelResourceManagementInterface> m_resourceManagement;
|
||||
QUrl m_fileUrl;
|
||||
QPointer<RewriterView> m_rewriterView;
|
||||
QPointer<NodeInstanceView> m_nodeInstanceView;
|
||||
|
||||
@@ -290,7 +290,7 @@ A node might become invalid if e.g. it or one of its ancestors is deleted.
|
||||
*/
|
||||
bool ModelNode::isValid() const
|
||||
{
|
||||
return !m_model.isNull() && !m_view.isNull() && m_internalNode && m_internalNode->isValid;
|
||||
return !m_model.isNull() && m_internalNode && m_internalNode->isValid;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -652,7 +652,7 @@ void ModelNode::removeProperty(const PropertyName &name) const
|
||||
return;
|
||||
|
||||
if (m_internalNode->hasProperty(name))
|
||||
model()->d->removeProperty(m_internalNode->property(name));
|
||||
model()->d->removePropertyAndRelatedResources(m_internalNode->property(name));
|
||||
}
|
||||
|
||||
/*! \brief removes this node from the node tree
|
||||
@@ -692,7 +692,7 @@ void ModelNode::destroy()
|
||||
return;
|
||||
|
||||
removeModelNodeFromSelection(*this);
|
||||
model()->d->removeNode(m_internalNode);
|
||||
model()->d->removeNodeAndRelatedResources(m_internalNode);
|
||||
}
|
||||
//\}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qmldesignercorelib_exports.h>
|
||||
|
||||
#include <bindingproperty.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
struct ModelResourceSet
|
||||
{
|
||||
struct SetExpression
|
||||
{
|
||||
BindingProperty property;
|
||||
QString expression;
|
||||
};
|
||||
|
||||
QList<ModelNode> removeModelNodes;
|
||||
QList<AbstractProperty> removeProperties;
|
||||
QList<SetExpression> setExpressions;
|
||||
};
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT ModelResourceManagementInterface
|
||||
{
|
||||
public:
|
||||
ModelResourceManagementInterface() = default;
|
||||
virtual ~ModelResourceManagementInterface() = default;
|
||||
|
||||
ModelResourceManagementInterface(const ModelResourceManagementInterface &) = delete;
|
||||
ModelResourceManagementInterface &operator=(const ModelResourceManagementInterface &) = delete;
|
||||
ModelResourceManagementInterface(ModelResourceManagementInterface &&) = default;
|
||||
ModelResourceManagementInterface &operator=(ModelResourceManagementInterface &&) = default;
|
||||
|
||||
virtual ModelResourceSet removeNode(const ModelNode &node) const = 0;
|
||||
virtual ModelResourceSet removeProperty(const AbstractProperty &property) const = 0;
|
||||
};
|
||||
} // namespace QmlDesigner
|
||||
@@ -71,7 +71,7 @@ void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNode
|
||||
return;
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
if (modelNode.hasParentProperty()) {
|
||||
Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty();
|
||||
|
||||
@@ -32,7 +32,7 @@ void NodeProperty::setModelNode(const ModelNode &modelNode)
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ void SignalHandlerProperty::setSource(const QString &source)
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalHandlerProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->setSignalHandlerProperty(internalNode(), name(), source);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ void SignalDeclarationProperty::setSignature(const QString &signature)
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalDeclarationProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->setSignalDeclarationProperty(internalNode(), name(), signature);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ void VariantProperty::setValue(const QVariant &value)
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->setVariantProperty(internalNode(), name(), value);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVa
|
||||
}
|
||||
|
||||
if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty())
|
||||
privateModel()->removeProperty(internalNode()->property(name()));
|
||||
privateModel()->removePropertyAndRelatedResources(internalNode()->property(name()));
|
||||
|
||||
privateModel()->setDynamicVariantProperty(internalNode(), name(), type, value);
|
||||
}
|
||||
|
||||
@@ -242,36 +242,37 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isBasedOn(TypeId id0) const override { return isBasedOn_(id0); }
|
||||
bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); }
|
||||
|
||||
bool isBasedOn(TypeId id0, TypeId id1) const override { return isBasedOn_(id0, id1); }
|
||||
bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); }
|
||||
|
||||
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2) const override
|
||||
bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override
|
||||
{
|
||||
return isBasedOn_(id0, id1, id2);
|
||||
return isBasedOn_(typeId, id1, id2);
|
||||
}
|
||||
|
||||
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3) const override
|
||||
bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override
|
||||
{
|
||||
return isBasedOn_(id0, id1, id2, id3);
|
||||
return isBasedOn_(typeId, id1, id2, id3);
|
||||
}
|
||||
|
||||
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override
|
||||
bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override
|
||||
{
|
||||
return isBasedOn_(id0, id1, id2, id3, id4);
|
||||
return isBasedOn_(typeId, id1, id2, id3, id4);
|
||||
}
|
||||
|
||||
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override
|
||||
bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override
|
||||
{
|
||||
return isBasedOn_(id0, id1, id2, id3, id4, id5);
|
||||
return isBasedOn_(typeId, id1, id2, id3, id4, id5);
|
||||
}
|
||||
|
||||
bool isBasedOn(TypeId id0, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override
|
||||
bool isBasedOn(
|
||||
TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const override
|
||||
{
|
||||
return isBasedOn_(id0, id1, id2, id3, id4, id5, id6);
|
||||
return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6);
|
||||
}
|
||||
|
||||
bool isBasedOn(TypeId id0,
|
||||
bool isBasedOn(TypeId typeId,
|
||||
TypeId id1,
|
||||
TypeId id2,
|
||||
TypeId id3,
|
||||
@@ -280,7 +281,7 @@ public:
|
||||
TypeId id6,
|
||||
TypeId id7) const override
|
||||
{
|
||||
return isBasedOn_(id0, id1, id2, id3, id4, id5, id6, id7);
|
||||
return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7);
|
||||
}
|
||||
|
||||
TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const
|
||||
|
||||
@@ -36,7 +36,6 @@ public:
|
||||
propertyName(PropertyDeclarationId propertyDeclarationId) const = 0;
|
||||
virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0;
|
||||
virtual TypeIds prototypeIds(TypeId type) const = 0;
|
||||
virtual bool isBasedOn(TypeId) const = 0;
|
||||
virtual bool isBasedOn(TypeId, TypeId) const = 0;
|
||||
virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0;
|
||||
virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0;
|
||||
|
||||
@@ -56,6 +56,8 @@ add_qtc_test(unittest GTEST
|
||||
gtest-std-printing.h
|
||||
lastchangedrowid-test.cpp
|
||||
import-test.cpp
|
||||
model-test.cpp
|
||||
modelresourcemanagementmock.h
|
||||
matchingtext-test.cpp
|
||||
mockfutureinterface.h
|
||||
mockmutex.h
|
||||
@@ -66,6 +68,7 @@ add_qtc_test(unittest GTEST
|
||||
mocktimer.cpp mocktimer.h
|
||||
nodelistproperty-test.cpp
|
||||
processevents-utilities.cpp processevents-utilities.h
|
||||
projectstoragemock.cpp projectstoragemock.h
|
||||
sizedarray-test.cpp
|
||||
smallstring-test.cpp
|
||||
spydummy.cpp spydummy.h
|
||||
@@ -258,6 +261,7 @@ extend_qtc_test(unittest
|
||||
model/model.cpp
|
||||
model/model_p.h
|
||||
model/modelnode.cpp
|
||||
model/modelresourcemanagementinterface.h
|
||||
model/propertycontainer.cpp
|
||||
model/propertyparser.cpp
|
||||
model/nodeabstractproperty.cpp
|
||||
|
||||
@@ -73,19 +73,10 @@ class ListModelEditor : public testing::Test
|
||||
public:
|
||||
ListModelEditor()
|
||||
{
|
||||
setModuleId("QtQuick", modelId_QtQuick);
|
||||
setType(modelId_QtQuick, "Item", "data");
|
||||
designerModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1);
|
||||
setModuleId("QtQml.Models", modelId_QtQml_Models);
|
||||
setType(modelId_QtQml_Models, "ListModel", "children");
|
||||
setType(modelId_QtQml_Models, "ListElement", "children");
|
||||
componentModel = QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1);
|
||||
|
||||
designerModel->attachView(&mockView);
|
||||
|
||||
emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
|
||||
|
||||
setType(modelId_QtQuick, "ListView", "data");
|
||||
listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15);
|
||||
listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
|
||||
mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode);
|
||||
@@ -108,29 +99,6 @@ public:
|
||||
ON_CALL(goIntoComponentMock, Call(_)).WillByDefault([](ModelNode node) { return node; });
|
||||
}
|
||||
|
||||
void setModuleId(Utils::SmallStringView moduleName, ModuleId moduleId)
|
||||
{
|
||||
ON_CALL(projectStorageMock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
|
||||
}
|
||||
|
||||
void setType(ModuleId moduleId,
|
||||
Utils::SmallStringView typeName,
|
||||
Utils::SmallString defaultPeopertyName)
|
||||
{
|
||||
static int typeIdNumber = 0;
|
||||
TypeId typeId = TypeId::create(++typeIdNumber);
|
||||
|
||||
static int defaultPropertyIdNumber = 0;
|
||||
PropertyDeclarationId defaultPropertyId = PropertyDeclarationId::create(
|
||||
++defaultPropertyIdNumber);
|
||||
|
||||
ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
|
||||
ON_CALL(projectStorageMock, type(Eq(typeId)))
|
||||
.WillByDefault(Return(Info::Type{defaultPropertyId, {}}));
|
||||
ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId)))
|
||||
.WillByDefault(Return(defaultPeopertyName));
|
||||
}
|
||||
|
||||
using Entry = std::pair<QmlDesigner::PropertyName, QVariant>;
|
||||
|
||||
ModelNode createElement(std::initializer_list<Entry> entries, AbstractView &view, ModelNode listModel)
|
||||
@@ -211,9 +179,10 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
NiceMock<ProjectStorageMock> projectStorageMock;
|
||||
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
|
||||
NiceMock<MockFunction<ModelNode(const ModelNode &)>> goIntoComponentMock;
|
||||
QmlDesigner::ModelPointer designerModel;
|
||||
QmlDesigner::ModelPointer designerModel{
|
||||
QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item", 1, 1)};
|
||||
NiceMock<MockListModelEditorView> mockView;
|
||||
QmlDesigner::ListModelEditorModel model{
|
||||
[&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); },
|
||||
@@ -225,11 +194,10 @@ protected:
|
||||
ModelNode element1;
|
||||
ModelNode element2;
|
||||
ModelNode element3;
|
||||
QmlDesigner::ModelPointer componentModel;
|
||||
QmlDesigner::ModelPointer componentModel{
|
||||
QmlDesigner::Model::create(projectStorageMock, "QtQml.Models.ListModel", 1, 1)};
|
||||
NiceMock<MockListModelEditorView> mockComponentView;
|
||||
ModelNode componentElement;
|
||||
ModuleId modelId_QtQuick = ModuleId::create(1);
|
||||
ModuleId modelId_QtQml_Models = ModuleId::create(2);
|
||||
};
|
||||
|
||||
TEST_F(ListModelEditor, CreatePropertyNameSet)
|
||||
|
||||
@@ -26,10 +26,25 @@ public:
|
||||
const QmlDesigner::NodeAbstractProperty &oldPropertyParent,
|
||||
AbstractView::PropertyChangeFlags propertyChange),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void,
|
||||
propertiesRemoved,
|
||||
(const QList<QmlDesigner::AbstractProperty> &propertyList),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
propertiesAboutToBeRemoved,
|
||||
(const QList<QmlDesigner::AbstractProperty> &propertyList),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void,
|
||||
bindingPropertiesChanged,
|
||||
(const QList<QmlDesigner::BindingProperty> &propertyList,
|
||||
PropertyChangeFlags propertyChange),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
bindingPropertiesAboutToBeChanged,
|
||||
(const QList<QmlDesigner::BindingProperty> &propertyList),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void,
|
||||
nodeRemoved,
|
||||
@@ -37,4 +52,5 @@ public:
|
||||
const QmlDesigner::NodeAbstractProperty &parentProperty,
|
||||
AbstractView::PropertyChangeFlags propertyChange),
|
||||
(override));
|
||||
MOCK_METHOD(void, nodeAboutToBeRemoved, (const QmlDesigner::ModelNode &removedNode), (override));
|
||||
};
|
||||
|
||||
491
tests/unit/unittest/model-test.cpp
Normal file
491
tests/unit/unittest/model-test.cpp
Normal file
@@ -0,0 +1,491 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "googletest.h"
|
||||
|
||||
#include "mocklistmodeleditorview.h"
|
||||
#include "modelresourcemanagementmock.h"
|
||||
#include "projectstoragemock.h"
|
||||
|
||||
#include <designercore/include/bindingproperty.h>
|
||||
#include <designercore/include/model.h>
|
||||
#include <designercore/include/modelnode.h>
|
||||
#include <designercore/include/nodeabstractproperty.h>
|
||||
#include <designercore/include/nodelistproperty.h>
|
||||
#include <designercore/include/nodeproperty.h>
|
||||
#include <designercore/include/signalhandlerproperty.h>
|
||||
#include <designercore/include/variantproperty.h>
|
||||
|
||||
namespace {
|
||||
using QmlDesigner::AbstractProperty;
|
||||
using QmlDesigner::ModelNode;
|
||||
using QmlDesigner::ModelNodes;
|
||||
using QmlDesigner::ModelResourceSet;
|
||||
|
||||
template<typename Matcher>
|
||||
auto HasPropertyName(const Matcher &matcher)
|
||||
{
|
||||
return Property(&AbstractProperty::name, matcher);
|
||||
}
|
||||
|
||||
class Model : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
Model()
|
||||
{
|
||||
model.attachView(&viewMock);
|
||||
rootNode = viewMock.rootModelNode();
|
||||
ON_CALL(resourceManagementMock, removeNode(_)).WillByDefault([](const auto &node) {
|
||||
return ModelResourceSet{{node}, {}, {}};
|
||||
});
|
||||
ON_CALL(resourceManagementMock, removeProperty(_)).WillByDefault([](const auto &property) {
|
||||
return ModelResourceSet{{}, {property}, {}};
|
||||
});
|
||||
}
|
||||
|
||||
~Model() { model.detachView(&viewMock); }
|
||||
|
||||
auto createNodeWithParent(const ModelNode &parentNode)
|
||||
{
|
||||
auto node = viewMock.createModelNode("QtQuick.Item");
|
||||
parentNode.defaultNodeAbstractProperty().reparentHere(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
auto createProperty(const ModelNode &parentNode, QmlDesigner::PropertyName name)
|
||||
{
|
||||
auto property = parentNode.variantProperty(name);
|
||||
property.setValue(4);
|
||||
return property;
|
||||
}
|
||||
|
||||
protected:
|
||||
NiceMock<MockListModelEditorView> viewMock;
|
||||
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
|
||||
NiceMock<ModelResourceManagementMock> resourceManagementMock;
|
||||
QmlDesigner::Model model{projectStorageMock,
|
||||
"QtQuick.Item",
|
||||
-1,
|
||||
-1,
|
||||
nullptr,
|
||||
std::make_unique<ModelResourceManagementMockWrapper>(
|
||||
resourceManagementMock)};
|
||||
ModelNode rootNode;
|
||||
};
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingModelResourceManagementRemoveNode)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeNode(node));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemoveProperyIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.variantProperty("foo");
|
||||
property.setValue(4);
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.removeProperty("foo");
|
||||
}
|
||||
|
||||
TEST_F(Model, NodeAbstractPropertyReparentHereIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = rootNode.variantProperty("foo");
|
||||
property.setValue(4);
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.nodeListProperty("foo").reparentHere(node);
|
||||
}
|
||||
|
||||
TEST_F(Model, NodePropertySetModelNodeIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = rootNode.variantProperty("foo");
|
||||
property.setValue(4);
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.nodeProperty("foo").setModelNode(node);
|
||||
}
|
||||
|
||||
TEST_F(Model, VariantPropertySetValueIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
property.setExpression("blah");
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.variantProperty("foo").setValue(7);
|
||||
}
|
||||
|
||||
TEST_F(Model,
|
||||
VariantPropertySetDynamicTypeNameAndEnumerationIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
property.setExpression("blah");
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.variantProperty("foo").setDynamicTypeNameAndEnumeration("int", "Ha");
|
||||
}
|
||||
|
||||
TEST_F(Model, VariantPropertySetDynamicTypeNameAndValueIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
property.setExpression("blah");
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.variantProperty("foo").setDynamicTypeNameAndValue("int", 7);
|
||||
}
|
||||
|
||||
TEST_F(Model, BindingPropertySetExpressionIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.variantProperty("foo");
|
||||
property.setValue(4);
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.bindingProperty("foo").setExpression("blah");
|
||||
}
|
||||
|
||||
TEST_F(Model,
|
||||
BindingPropertySetDynamicTypeNameAndExpressionIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.variantProperty("foo");
|
||||
property.setValue(4);
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.bindingProperty("foo").setDynamicTypeNameAndExpression("int", "blah");
|
||||
}
|
||||
|
||||
TEST_F(Model, SignalHandlerPropertySetSourceIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
property.setExpression("blah");
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.signalHandlerProperty("foo").setSource("blah");
|
||||
}
|
||||
|
||||
TEST_F(Model, SignalDeclarationPropertySetSignatureIsCallingModelResourceManagementRemoveProperty)
|
||||
{
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
property.setExpression("blah");
|
||||
|
||||
EXPECT_CALL(resourceManagementMock, removeProperty(Eq(property)));
|
||||
|
||||
rootNode.signalDeclarationProperty("foo").setSignature("blah");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeAboutToBeRemoved)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto node2 = createNodeWithParent(rootNode);
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node, node2}, {}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
|
||||
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node2)));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeRemoved)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto node2 = createNodeWithParent(rootNode);
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node, node2}, {}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewNodeRemovedWithValidNodes)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto node2 = createNodeWithParent(rootNode);
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node, node2, ModelNode{}}, {}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesAboutToBeRemoved)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = createProperty(rootNode, "foo");
|
||||
auto property2 = createProperty(rootNode, "bar");
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node}, {property, property2}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property2))));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesRemoved)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = createProperty(rootNode, "foo");
|
||||
auto property2 = createProperty(rootNode, "bar");
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node}, {property, property2}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewPropertiesRemovedOnlyWithValidProperties)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = createProperty(rootNode, "foo");
|
||||
auto property2 = createProperty(rootNode, "bar");
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node}, {property, property2, {}}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesAboutToBeChanged)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
auto property2 = rootNode.bindingProperty("bar");
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}}}));
|
||||
|
||||
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property2))));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesChanged)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
auto property2 = rootNode.bindingProperty("bar");
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}}}));
|
||||
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property)), _));
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeDestroyIsCallingAbstractViewBindingPropertiesChangedOnlyWithValidProperties)
|
||||
{
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto property = rootNode.bindingProperty("foo");
|
||||
auto property2 = rootNode.bindingProperty("bar");
|
||||
ON_CALL(resourceManagementMock, removeNode(node))
|
||||
.WillByDefault(Return(
|
||||
ModelResourceSet{{node}, {}, {{property, "yi"}, {property2, "er"}, {{}, "san"}}}));
|
||||
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property)), _));
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeAboutToBeRemoved)
|
||||
{
|
||||
auto property = createProperty(rootNode, "foo");
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto node2 = createNodeWithParent(rootNode);
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(Return(ModelResourceSet{{node, node2}, {property}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
|
||||
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node2)));
|
||||
|
||||
rootNode.removeProperty("foo");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeRemoved)
|
||||
{
|
||||
auto property = createProperty(rootNode, "foo");
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto node2 = createNodeWithParent(rootNode);
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(Return(ModelResourceSet{{node, node2}, {property}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
|
||||
|
||||
rootNode.removeProperty("foo");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewNodeRemovedWithValidNodes)
|
||||
{
|
||||
auto property = createProperty(rootNode, "foo");
|
||||
auto node = createNodeWithParent(rootNode);
|
||||
auto node2 = createNodeWithParent(rootNode);
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(Return(ModelResourceSet{{node, node2, ModelNode{}}, {property}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node), _, _));
|
||||
EXPECT_CALL(viewMock, nodeRemoved(Eq(node2), _, _));
|
||||
|
||||
rootNode.removeProperty("foo");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesAboutToBeRemoved)
|
||||
{
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
auto property2 = createProperty(rootNode, "er");
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(Return(ModelResourceSet{{}, {property, property2}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property2))));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesRemoved)
|
||||
{
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
auto property2 = createProperty(rootNode, "er");
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(Return(ModelResourceSet{{}, {property, property2}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewPropertiesRemovedOnlyWithValidProperties)
|
||||
{
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
auto property2 = createProperty(rootNode, "er");
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(Return(ModelResourceSet{{}, {property, property2, {}}, {}}));
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property))));
|
||||
EXPECT_CALL(viewMock, propertiesRemoved(ElementsAre(Eq(property2))));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesAboutToBeChanged)
|
||||
{
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
auto property1 = rootNode.bindingProperty("foo");
|
||||
auto property2 = rootNode.bindingProperty("bar");
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(
|
||||
Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}}}));
|
||||
|
||||
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property1))));
|
||||
EXPECT_CALL(viewMock, bindingPropertiesAboutToBeChanged(ElementsAre(Eq(property2))));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model, ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesChanged)
|
||||
{
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
auto property1 = rootNode.bindingProperty("foo");
|
||||
auto property2 = rootNode.bindingProperty("bar");
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(
|
||||
Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}}}));
|
||||
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property1)), _));
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model,
|
||||
ModelNodeRemovePropertyIsCallingAbstractViewBindingPropertiesChangedOnlyWithValidProperties)
|
||||
{
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
auto property1 = rootNode.bindingProperty("foo");
|
||||
auto property2 = rootNode.bindingProperty("bar");
|
||||
ON_CALL(resourceManagementMock, removeProperty(property))
|
||||
.WillByDefault(
|
||||
Return(ModelResourceSet{{}, {property}, {{property1, "yi"}, {property2, "er"}, {}}}));
|
||||
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property1)), _));
|
||||
EXPECT_CALL(viewMock, bindingPropertiesChanged(ElementsAre(Eq(property2)), _));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model, ByDefaultRemoveModelNodeRemovesNode)
|
||||
{
|
||||
model.detachView(&viewMock);
|
||||
QmlDesigner::Model newModel{projectStorageMock, "QtQuick.Item"};
|
||||
newModel.attachView(&viewMock);
|
||||
auto node = createNodeWithParent(viewMock.rootModelNode());
|
||||
|
||||
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ByDefaultRemovePropertiesRemovesProperty)
|
||||
{
|
||||
model.detachView(&viewMock);
|
||||
QmlDesigner::Model newModel{projectStorageMock, "QtQuick.Item"};
|
||||
newModel.attachView(&viewMock);
|
||||
rootNode = viewMock.rootModelNode();
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
TEST_F(Model, ByDefaultRemoveModelNodeInFactoryMethodCallsRemovesNode)
|
||||
{
|
||||
model.detachView(&viewMock);
|
||||
auto newModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item");
|
||||
newModel->attachView(&viewMock);
|
||||
auto node = createNodeWithParent(viewMock.rootModelNode());
|
||||
|
||||
EXPECT_CALL(viewMock, nodeAboutToBeRemoved(Eq(node)));
|
||||
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
TEST_F(Model, ByDefaultRemovePropertiesInFactoryMethodCallsRemoveProperty)
|
||||
{
|
||||
model.detachView(&viewMock);
|
||||
auto newModel = QmlDesigner::Model::create(projectStorageMock, "QtQuick.Item");
|
||||
newModel->attachView(&viewMock);
|
||||
rootNode = viewMock.rootModelNode();
|
||||
auto property = createProperty(rootNode, "yi");
|
||||
|
||||
EXPECT_CALL(viewMock, propertiesAboutToBeRemoved(ElementsAre(Eq(property))));
|
||||
|
||||
rootNode.removeProperty("yi");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
42
tests/unit/unittest/modelresourcemanagementmock.h
Normal file
42
tests/unit/unittest/modelresourcemanagementmock.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "googletest.h"
|
||||
|
||||
#include <model/modelresourcemanagementinterface.h>
|
||||
#include <modelnode.h>
|
||||
|
||||
class ModelResourceManagementMock : public QmlDesigner::ModelResourceManagementInterface
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD(QmlDesigner::ModelResourceSet,
|
||||
removeNode,
|
||||
(const QmlDesigner::ModelNode &),
|
||||
(const, override));
|
||||
MOCK_METHOD(QmlDesigner::ModelResourceSet,
|
||||
removeProperty,
|
||||
(const QmlDesigner::AbstractProperty &),
|
||||
(const, override));
|
||||
};
|
||||
|
||||
class ModelResourceManagementMockWrapper : public QmlDesigner::ModelResourceManagementInterface
|
||||
{
|
||||
public:
|
||||
ModelResourceManagementMockWrapper(ModelResourceManagementMock &mock)
|
||||
: mock{mock}
|
||||
{}
|
||||
|
||||
QmlDesigner::ModelResourceSet removeNode(const QmlDesigner::ModelNode &node) const override
|
||||
{
|
||||
return mock.removeNode(node);
|
||||
}
|
||||
|
||||
QmlDesigner::ModelResourceSet removeProperty(const QmlDesigner::AbstractProperty &property) const override
|
||||
{
|
||||
return mock.removeProperty(property);
|
||||
}
|
||||
|
||||
ModelResourceManagementMock &mock;
|
||||
};
|
||||
@@ -25,11 +25,6 @@ protected:
|
||||
using iterator = QmlDesigner::NodeListProperty::iterator;
|
||||
NodeListProperty()
|
||||
{
|
||||
ModuleId modelId_QtQuick = ModuleId::create(1);
|
||||
setModuleId("QtQuick", modelId_QtQuick);
|
||||
setType(modelId_QtQuick, "Item", "data");
|
||||
model = std::make_unique<QmlDesigner::Model>(projectStorageMock, "QtQuick.Item");
|
||||
|
||||
model->attachView(&abstractViewMock);
|
||||
nodeListProperty = abstractViewMock.rootModelNode().nodeListProperty("foo");
|
||||
|
||||
@@ -82,8 +77,9 @@ protected:
|
||||
}
|
||||
|
||||
protected:
|
||||
NiceMock<ProjectStorageMock> projectStorageMock;
|
||||
std::unique_ptr<QmlDesigner::Model> model;
|
||||
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
|
||||
std::unique_ptr<QmlDesigner::Model> model{
|
||||
std::make_unique<QmlDesigner::Model>(projectStorageMock, "QtQuick.Item")};
|
||||
NiceMock<AbstractViewMock> abstractViewMock;
|
||||
QmlDesigner::NodeListProperty nodeListProperty;
|
||||
ModelNode node1;
|
||||
|
||||
89
tests/unit/unittest/projectstoragemock.cpp
Normal file
89
tests/unit/unittest/projectstoragemock.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "projectstoragemock.h"
|
||||
|
||||
namespace QmlDesigner {
|
||||
namespace {
|
||||
|
||||
template<typename BasicId>
|
||||
void incrementBasicId(BasicId &id)
|
||||
{
|
||||
id = BasicId::create(id.internalId() + 1);
|
||||
}
|
||||
|
||||
ModuleId createModule(ProjectStorageMock &mock, Utils::SmallStringView moduleName)
|
||||
{
|
||||
static ModuleId moduleId;
|
||||
incrementBasicId(moduleId);
|
||||
|
||||
ON_CALL(mock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
|
||||
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
TypeId createType(ProjectStorageMock &mock,
|
||||
ModuleId moduleId,
|
||||
Utils::SmallStringView typeName,
|
||||
Utils::SmallString defaultPropertyName,
|
||||
Storage::TypeTraits typeTraits,
|
||||
TypeId baseTypeId = TypeId{})
|
||||
{
|
||||
static TypeId typeId;
|
||||
incrementBasicId(typeId);
|
||||
|
||||
static PropertyDeclarationId defaultPropertyId;
|
||||
incrementBasicId(defaultPropertyId);
|
||||
|
||||
ON_CALL(mock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId));
|
||||
ON_CALL(mock, type(Eq(typeId)))
|
||||
.WillByDefault(Return(Storage::Info::Type{defaultPropertyId, typeTraits}));
|
||||
ON_CALL(mock, propertyName(Eq(defaultPropertyId))).WillByDefault(Return(defaultPropertyName));
|
||||
|
||||
if (baseTypeId)
|
||||
ON_CALL(mock, isBasedOn(Eq(typeId), Eq(baseTypeId))).WillByDefault(Return(true));
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
TypeId createObject(ProjectStorageMock &mock,
|
||||
ModuleId moduleId,
|
||||
Utils::SmallStringView typeName,
|
||||
Utils::SmallString defaultPropertyName,
|
||||
TypeId baseTypeId = TypeId{})
|
||||
{
|
||||
return createType(
|
||||
mock, moduleId, typeName, defaultPropertyName, Storage::TypeTraits::Reference, baseTypeId);
|
||||
}
|
||||
void setupIsBasedOn(ProjectStorageMock &mock)
|
||||
{
|
||||
auto call = [&](TypeId typeId, auto... ids) -> bool {
|
||||
return (mock.isBasedOn(typeId, ids) || ...);
|
||||
};
|
||||
ON_CALL(mock, isBasedOn(_, _, _)).WillByDefault(call);
|
||||
ON_CALL(mock, isBasedOn(_, _, _, _)).WillByDefault(call);
|
||||
ON_CALL(mock, isBasedOn(_, _, _, _, _)).WillByDefault(call);
|
||||
ON_CALL(mock, isBasedOn(_, _, _, _, _, _)).WillByDefault(call);
|
||||
ON_CALL(mock, isBasedOn(_, _, _, _, _, _, _)).WillByDefault(call);
|
||||
ON_CALL(mock, isBasedOn(_, _, _, _, _, _, _, _)).WillByDefault(call);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace QmlDesigner
|
||||
|
||||
void ProjectStorageMock::setupQtQtuick()
|
||||
{
|
||||
QmlDesigner::setupIsBasedOn(*this);
|
||||
|
||||
auto qmlModuleId = QmlDesigner::createModule(*this, "QML");
|
||||
auto qtQmlModelsModuleId = QmlDesigner::createModule(*this, "QtQml.Models");
|
||||
auto qtQuickModuleId = QmlDesigner::createModule(*this, "QtQuick");
|
||||
|
||||
auto qtObjectId = QmlDesigner::createObject(*this, qmlModuleId, "QtObject", "children");
|
||||
|
||||
QmlDesigner::createObject(*this, qtQmlModelsModuleId, "ListModel", "children", qtObjectId);
|
||||
QmlDesigner::createObject(*this, qtQmlModelsModuleId, "ListElement", "children", qtObjectId);
|
||||
|
||||
auto itemId = QmlDesigner::createObject(*this, qtQuickModuleId, "Item", "data", qtObjectId);
|
||||
QmlDesigner::createObject(*this, qtQuickModuleId, "ListView", "data", itemId);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
@@ -14,6 +14,8 @@
|
||||
class ProjectStorageMock : public QmlDesigner::ProjectStorageInterface
|
||||
{
|
||||
public:
|
||||
void setupQtQtuick();
|
||||
|
||||
MOCK_METHOD(void,
|
||||
synchronize,
|
||||
(QmlDesigner::Storage::Synchronization::SynchronizationPackage package),
|
||||
@@ -63,7 +65,6 @@ public:
|
||||
(const, override));
|
||||
MOCK_METHOD(QmlDesigner::TypeIds, prototypeAndSelfIds, (QmlDesigner::TypeId type), (const, override));
|
||||
MOCK_METHOD(QmlDesigner::TypeIds, prototypeIds, (QmlDesigner::TypeId type), (const, override));
|
||||
MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId), (const, override));
|
||||
MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override));
|
||||
MOCK_METHOD(bool,
|
||||
isBasedOn,
|
||||
@@ -158,3 +159,8 @@ public:
|
||||
MOCK_METHOD(std::vector<QmlDesigner::Cache::Source>, fetchAllSources, (), ());
|
||||
};
|
||||
|
||||
class ProjectStorageMockWithQtQtuick : public ProjectStorageMock
|
||||
{
|
||||
public:
|
||||
ProjectStorageMockWithQtQtuick() { setupQtQtuick(); }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user