forked from qt-creator/qt-creator
Task-number: QDS-9766 Change-Id: I41c5d6769400772aa4b0d8b395b6a7ed79270cbd Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
468 lines
18 KiB
C++
468 lines
18 KiB
C++
// 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 <include/bindingproperty.h>
|
|
#include <include/model.h>
|
|
#include <include/modelnode.h>
|
|
#include <include/nodeabstractproperty.h>
|
|
#include <include/nodelistproperty.h>
|
|
#include <include/nodeproperty.h>
|
|
#include <include/signalhandlerproperty.h>
|
|
#include <include/variantproperty.h>
|
|
#include <model/modelresourcemanagement.h>
|
|
|
|
namespace {
|
|
using QmlDesigner::AbstractProperty;
|
|
using QmlDesigner::ModelNode;
|
|
using QmlDesigner::ModelNodes;
|
|
using QmlDesigner::ModelResourceSet;
|
|
|
|
template<typename Matcher>
|
|
auto SetExpressionProperty(const Matcher &matcher)
|
|
{
|
|
return Field("property", &QmlDesigner::ModelResourceSet::SetExpression::property, matcher);
|
|
}
|
|
|
|
template<typename Matcher>
|
|
auto SetExpressionExpression(const Matcher &matcher)
|
|
{
|
|
return Field("expression", &QmlDesigner::ModelResourceSet::SetExpression::expression, matcher);
|
|
}
|
|
|
|
template<typename PropertyMatcher, typename ExpressionMatcher>
|
|
auto SetExpression(const PropertyMatcher &propertyMatcher, const ExpressionMatcher &expressionMatcher)
|
|
{
|
|
return AllOf(SetExpressionProperty(propertyMatcher), SetExpressionExpression(expressionMatcher));
|
|
}
|
|
|
|
class ModelResourceManagement : public testing::Test
|
|
{
|
|
protected:
|
|
ModelResourceManagement()
|
|
{
|
|
model.attachView(&viewMock);
|
|
rootNode = model.rootModelNode();
|
|
}
|
|
auto createNodeWithParent(const QmlDesigner::TypeName &typeName,
|
|
QmlDesigner::NodeAbstractProperty parentProperty,
|
|
const QString &id = {})
|
|
{
|
|
auto node = model.createModelNode(typeName);
|
|
parentProperty.reparentHere(node);
|
|
if (id.size())
|
|
node.setIdWithoutRefactoring(id);
|
|
|
|
return node;
|
|
}
|
|
|
|
protected:
|
|
NiceMock<MockListModelEditorView> viewMock;
|
|
NiceMock<ProjectStorageMockWithQtQtuick> projectStorageMock;
|
|
QmlDesigner::ModelResourceManagement management;
|
|
QmlDesigner::Model model{projectStorageMock, "QtQuick.Item"};
|
|
ModelNode rootNode;
|
|
};
|
|
|
|
TEST_F(ModelResourceManagement, RemoveProperty)
|
|
{
|
|
auto layerEnabledProperty = rootNode.variantProperty("layer.enabled");
|
|
layerEnabledProperty.setValue(true);
|
|
|
|
auto resources = management.removeProperties({layerEnabledProperty}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Contains(layerEnabledProperty));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemoveMultipleProperties)
|
|
{
|
|
auto layerEnabledProperty = rootNode.variantProperty("layer.enabled");
|
|
layerEnabledProperty.setValue(true);
|
|
auto layerHiddenProperty = rootNode.variantProperty("layer.hidden");
|
|
layerHiddenProperty.setValue(true);
|
|
|
|
auto resources = management.removeProperties({layerEnabledProperty, layerHiddenProperty}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, IsSupersetOf({layerEnabledProperty, layerHiddenProperty}));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemoveNode)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty());
|
|
|
|
auto resources = management.removeNodes({node}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Contains(node));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemoveMultipleNodes)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty());
|
|
auto node2 = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty());
|
|
|
|
auto resources = management.removeNodes({node, node2}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({node, node2}));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, DontRemoveChildNodes)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty());
|
|
auto childNode = createNodeWithParent("QtQuick.Item", node.defaultNodeListProperty());
|
|
|
|
auto resources = management.removeNodes({node}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Not(Contains(childNode)));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemovePropertyLayerEnabled)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.nodeProperty("layer.effect"));
|
|
auto layerEnabledProperty = rootNode.variantProperty("layer.enabled");
|
|
layerEnabledProperty.setValue(true);
|
|
|
|
auto resources = management.removeNodes({node}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Contains(layerEnabledProperty));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemovePropertyLayerEnabledIfLayerEffectPropertyIsRemoved)
|
|
{
|
|
auto layerEffectProperty = rootNode.nodeProperty("layer.effect");
|
|
auto node = createNodeWithParent("QtQuick.Item", layerEffectProperty);
|
|
auto layerEnabledProperty = rootNode.variantProperty("layer.enabled");
|
|
layerEnabledProperty.setValue(true);
|
|
|
|
auto resources = management.removeProperties({layerEffectProperty}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Contains(layerEnabledProperty));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, DontRemovePropertyLayerEnabledIfNotExists)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.nodeProperty("layer.effect"));
|
|
auto layerEnabledProperty = rootNode.variantProperty("layer.enabled");
|
|
|
|
auto resources = management.removeNodes({node}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Not(Contains(layerEnabledProperty)));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemoveAliasExportProperty)
|
|
{
|
|
auto fooNode = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty(), "foo");
|
|
auto aliasExportProperty = rootNode.bindingProperty("foo");
|
|
aliasExportProperty.setDynamicTypeNameAndExpression("alias", "foo");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Contains(aliasExportProperty));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemoveAliasForChildExportProperty)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty());
|
|
auto fooNode = createNodeWithParent("QtQuick.Item", node.defaultNodeListProperty(), "foo");
|
|
auto aliasExportProperty = rootNode.bindingProperty("foo");
|
|
aliasExportProperty.setDynamicTypeNameAndExpression("alias", "foo");
|
|
|
|
auto resources = management.removeNodes({node}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Contains(aliasExportProperty));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, RemoveAliasForGrandChildExportProperty)
|
|
{
|
|
auto node = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty());
|
|
auto childNode = createNodeWithParent("QtQuick.Item", node.defaultNodeListProperty());
|
|
auto fooNode = createNodeWithParent("QtQuick.Item", childNode.defaultNodeListProperty(), "foo");
|
|
auto aliasExportProperty = rootNode.bindingProperty("foo");
|
|
aliasExportProperty.setDynamicTypeNameAndExpression("alias", "foo");
|
|
|
|
auto resources = management.removeNodes({node}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Contains(aliasExportProperty));
|
|
}
|
|
|
|
TEST_F(ModelResourceManagement, DontRemoveNonAliasExportProperty)
|
|
{
|
|
auto fooNode = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty(), "foo");
|
|
auto aliasExportProperty = rootNode.bindingProperty("foo");
|
|
aliasExportProperty.setDynamicTypeNameAndExpression("int", "foo");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeProperties, Not(Contains(aliasExportProperty)));
|
|
}
|
|
|
|
struct TargetData
|
|
{
|
|
QmlDesigner::TypeName targetType;
|
|
QmlDesigner::TypeName type;
|
|
QmlDesigner::PropertyName propertyName;
|
|
|
|
// printer function for TargetData - don't remove
|
|
friend std::ostream &operator<<(std::ostream &os, const TargetData &data)
|
|
{
|
|
return os << "(" << data.targetType << ", " << data.type << ")";
|
|
}
|
|
};
|
|
|
|
class ForTarget : public ModelResourceManagement, public testing::WithParamInterface<TargetData>
|
|
{
|
|
protected:
|
|
TargetData parameters = GetParam();
|
|
ModelNode fooNode = createNodeWithParent(parameters.targetType,
|
|
rootNode.defaultNodeListProperty(),
|
|
"foo");
|
|
ModelNode barNode = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty(), "bar");
|
|
ModelNode source = createNodeWithParent(parameters.type,
|
|
rootNode.defaultNodeListProperty(),
|
|
"source1");
|
|
ModelNode source2 = createNodeWithParent(parameters.type,
|
|
rootNode.defaultNodeListProperty(),
|
|
"source2");
|
|
QmlDesigner::BindingProperty sourceTargetProperty = source.bindingProperty(parameters.propertyName);
|
|
QmlDesigner::BindingProperty source2TargetProperty = source2.bindingProperty(
|
|
parameters.propertyName);
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
ModelResourceManagement,
|
|
ForTarget,
|
|
testing::Values(TargetData{"QtQuick.Item", "QtQuick.PropertyChanges", "target"},
|
|
TargetData{"QtQuick.Item", "QtQuick.Timeline.KeyframeGroup", "target"},
|
|
TargetData{"FlowView.FlowTransition", "FlowView.FlowActionArea", "target"},
|
|
TargetData{"QtQuick.Item", "QtQuick.PropertyAnimation", "target"},
|
|
TargetData{"FlowView.FlowItem", "FlowView.FlowTransition", "to"},
|
|
TargetData{"FlowView.FlowItem", "FlowView.FlowTransition", "from"}));
|
|
|
|
TEST_P(ForTarget, Remove)
|
|
{
|
|
sourceTargetProperty.setExpression("foo");
|
|
source2TargetProperty.setExpression("foo");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({source, source2}));
|
|
}
|
|
|
|
TEST_P(ForTarget, DontRemoveForDifferentTarget)
|
|
{
|
|
sourceTargetProperty.setExpression("bar");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Not(Contains(source)));
|
|
}
|
|
|
|
TEST_P(ForTarget, DontRemoveKeyIfTargetIsNotSet)
|
|
{
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Not(Contains(source)));
|
|
}
|
|
|
|
TEST_P(ForTarget, DontRemoveIfTargetCannotBeResolved)
|
|
{
|
|
sourceTargetProperty.setExpression("not_exists");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Not(Contains(source)));
|
|
}
|
|
|
|
class ForTargets : public ModelResourceManagement, public testing::WithParamInterface<TargetData>
|
|
{
|
|
protected:
|
|
TargetData parameters = GetParam();
|
|
ModelNode fooNode = createNodeWithParent(parameters.targetType,
|
|
rootNode.defaultNodeListProperty(),
|
|
"foo");
|
|
ModelNode barNode = createNodeWithParent(parameters.targetType,
|
|
rootNode.defaultNodeListProperty(),
|
|
"bar");
|
|
ModelNode yiNode = createNodeWithParent(parameters.targetType,
|
|
rootNode.defaultNodeListProperty(),
|
|
"yi");
|
|
ModelNode erNode = createNodeWithParent(parameters.targetType,
|
|
rootNode.defaultNodeListProperty(),
|
|
"er");
|
|
ModelNode sanNode = createNodeWithParent(parameters.targetType,
|
|
rootNode.defaultNodeListProperty(),
|
|
"san");
|
|
ModelNode source = createNodeWithParent(parameters.type,
|
|
rootNode.defaultNodeListProperty(),
|
|
"source1");
|
|
ModelNode source2 = createNodeWithParent(parameters.type,
|
|
rootNode.defaultNodeListProperty(),
|
|
"source2");
|
|
QmlDesigner::BindingProperty sourceTargetsProperty = source.bindingProperty("targets");
|
|
QmlDesigner::BindingProperty source2TargetsProperty = source2.bindingProperty("targets");
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(ModelResourceManagement,
|
|
ForTargets,
|
|
testing::Values(TargetData{"FlowView.FlowTransition", "FlowView.FlowDecision"},
|
|
TargetData{"FlowView.FlowTransition", "FlowView.FlowWildcard"},
|
|
TargetData{"QtQuick.Item", "QtQuick.PropertyAnimation"}));
|
|
|
|
TEST_P(ForTargets, Remove)
|
|
{
|
|
sourceTargetsProperty.setExpression("[foo]");
|
|
source2TargetsProperty.setExpression("[foo]");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({source, source2}));
|
|
}
|
|
|
|
TEST_P(ForTargets, HandleInvalidBinding)
|
|
{
|
|
sourceTargetsProperty.setExpression("[foo, broken]");
|
|
source2TargetsProperty.setExpression("[foo, fail]");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({source, source2}));
|
|
}
|
|
|
|
TEST_P(ForTargets, RemoveIndirectly)
|
|
{
|
|
auto parenNode = createNodeWithParent("QtQuick.Item", rootNode.defaultNodeListProperty(), "hoo");
|
|
parenNode.defaultNodeListProperty().reparentHere(fooNode);
|
|
parenNode.defaultNodeListProperty().reparentHere(barNode);
|
|
sourceTargetsProperty.setExpression("[foo, bar]");
|
|
source2TargetsProperty.setExpression("[bar, foo]");
|
|
|
|
auto resources = management.removeNodes({parenNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({source, source2}));
|
|
}
|
|
|
|
TEST_P(ForTargets, DontRemoveTargetIfThereAreStillAnOtherTargets)
|
|
{
|
|
sourceTargetsProperty.setExpression("[foo, bar]");
|
|
source2TargetsProperty.setExpression("[foo, bar]");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, AllOf(Not(Contains(source)), Not(Contains(source2))));
|
|
}
|
|
|
|
TEST_P(ForTargets, ChangeExpressionIfThereAreStillAnOtherTargets)
|
|
{
|
|
sourceTargetsProperty.setExpression("[foo, bar]");
|
|
source2TargetsProperty.setExpression("[foo, bar]");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.setExpressions,
|
|
UnorderedElementsAre(SetExpression(sourceTargetsProperty, "[bar]"),
|
|
SetExpression(source2TargetsProperty, "[bar]")));
|
|
}
|
|
|
|
TEST_P(ForTargets, DontChangeOrderInExpression)
|
|
{
|
|
sourceTargetsProperty.setExpression("[yi, foo, er, san]");
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.setExpressions,
|
|
UnorderedElementsAre(SetExpression(sourceTargetsProperty, "[yi, er, san]")));
|
|
}
|
|
|
|
struct StateData
|
|
{
|
|
QmlDesigner::TypeName type;
|
|
QmlDesigner::PropertyName propertyName;
|
|
|
|
// printer function for TargetData - don't remove
|
|
friend std::ostream &operator<<(std::ostream &os, const StateData &data)
|
|
{
|
|
return os << "(" << data.type << ", " << data.propertyName << ")";
|
|
}
|
|
};
|
|
|
|
class ForState : public ModelResourceManagement, public testing::WithParamInterface<StateData>
|
|
{
|
|
protected:
|
|
ModelNode createStateWithParent(QmlDesigner::NodeAbstractProperty parentProperty,
|
|
const QString &name)
|
|
{
|
|
ModelNode stateNode = createNodeWithParent("QtQuick.State", parentProperty, name);
|
|
stateNode.variantProperty("name").setValue(name);
|
|
|
|
return stateNode;
|
|
}
|
|
|
|
protected:
|
|
StateData parameters = GetParam();
|
|
ModelNode fooNode = createStateWithParent(rootNode.defaultNodeListProperty(), "foo");
|
|
ModelNode barNode = createStateWithParent(rootNode.defaultNodeListProperty(), "bar");
|
|
ModelNode yiNode = createStateWithParent(rootNode.defaultNodeListProperty(), "yi");
|
|
ModelNode erNode = createStateWithParent(rootNode.defaultNodeListProperty(), "er");
|
|
ModelNode sanNode = createStateWithParent(rootNode.defaultNodeListProperty(), "san");
|
|
ModelNode source = createNodeWithParent(parameters.type,
|
|
rootNode.defaultNodeListProperty(),
|
|
"source1");
|
|
ModelNode source2 = createNodeWithParent(parameters.type,
|
|
rootNode.defaultNodeListProperty(),
|
|
"source2");
|
|
QmlDesigner::VariantProperty sourceStateProperty = source.variantProperty(parameters.propertyName);
|
|
QmlDesigner::VariantProperty source2StateProperty = source2.variantProperty(
|
|
parameters.propertyName);
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(ModelResourceManagement,
|
|
ForState,
|
|
testing::Values(StateData{"QtQuick.Transition", "from"},
|
|
StateData{"QtQuick.Transition", "to"}));
|
|
|
|
TEST_P(ForState, Remove)
|
|
{
|
|
sourceStateProperty.setValue(QString{"foo"});
|
|
source2StateProperty.setValue(QString{"foo"});
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({source, source2}));
|
|
}
|
|
|
|
TEST_P(ForState, DontRemoveForStarState)
|
|
{
|
|
fooNode.variantProperty("name").setValue("*");
|
|
sourceStateProperty.setValue(QString{"*"});
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Not(Contains(source)));
|
|
}
|
|
|
|
TEST_P(ForState, DontRemoveForEmptyState)
|
|
{
|
|
fooNode.variantProperty("name").setValue("");
|
|
sourceStateProperty.setValue(QString{""});
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, Not(Contains(source)));
|
|
}
|
|
|
|
TEST_P(ForState, DontRemoveForDifferentState)
|
|
{
|
|
sourceStateProperty.setValue(QString{"foo"});
|
|
source2StateProperty.setValue(QString{"bar"});
|
|
|
|
auto resources = management.removeNodes({fooNode}, &model);
|
|
|
|
ASSERT_THAT(resources.removeModelNodes, IsSupersetOf({source}));
|
|
}
|
|
|
|
} // namespace
|