QmlDesigner: Improve node creation with project storage

The file url was missing, so no valid source id could be created. That
lead to invalid imports.

The qualified path for the node instances is now created in the node
instance view.

Change-Id: I2685ab2fdbdb0fa8a5f89793be52c8fae2b8db8c
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
This commit is contained in:
Marco Bubke
2024-04-11 18:13:10 +02:00
parent c9f9804edf
commit db84bc43a9
15 changed files with 95 additions and 29 deletions

View File

@@ -64,13 +64,14 @@ namespace QmlDesigner {
DesignDocument acts as a facade to a model representing a qml document, DesignDocument acts as a facade to a model representing a qml document,
and the different views/widgets accessing it. and the different views/widgets accessing it.
*/ */
DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies, DesignDocument::DesignDocument([[maybe_unused]] const QUrl &filePath,
ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies) ExternalDependenciesInterface &externalDependencies)
#ifdef QDS_USE_PROJECTSTORAGE #ifdef QDS_USE_PROJECTSTORAGE
: m_documentModel(Model::create(projectStorageDependencies, : m_documentModel(Model::create(projectStorageDependencies,
"Item", "Item",
{Import::createLibraryImport("QtQuick")}, {Import::createLibraryImport("QtQuick")},
{}, filePath,
std::make_unique<ModelResourceManagement>())) std::make_unique<ModelResourceManagement>()))
#else #else
: m_documentModel( : m_documentModel(

View File

@@ -41,7 +41,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject
Q_OBJECT Q_OBJECT
public: public:
DesignDocument(ProjectStorageDependencies projectStorageDependencies, DesignDocument(const QUrl &filePath,
ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies); ExternalDependenciesInterface &externalDependencies);
~DesignDocument() override; ~DesignDocument() override;

View File

@@ -166,6 +166,7 @@ public:
NodeMetaInfo qtQmlConnectionsMetaInfo() const; NodeMetaInfo qtQmlConnectionsMetaInfo() const;
NodeMetaInfo qtQmlModelsListModelMetaInfo() const; NodeMetaInfo qtQmlModelsListModelMetaInfo() const;
NodeMetaInfo qtQmlModelsListElementMetaInfo() const; NodeMetaInfo qtQmlModelsListElementMetaInfo() const;
NodeMetaInfo qtQmlXmlListModelXmlListModelRoleMetaInfo() const;
NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const;
NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const;
NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const; NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const;

View File

@@ -167,6 +167,7 @@ public:
bool isQtMultimediaSoundEffect() const; bool isQtMultimediaSoundEffect() const;
bool isQtObject() const; bool isQtObject() const;
bool isQtQmlConnections() const; bool isQtQmlConnections() const;
bool isQtQmlModelsListElement() const;
bool isQtQuick3DBakedLightmap() const; bool isQtQuick3DBakedLightmap() const;
bool isQtQuick3DBuffer() const; bool isQtQuick3DBuffer() const;
bool isQtQuick3DCamera() const; bool isQtQuick3DCamera() const;
@@ -176,7 +177,6 @@ public:
bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceList() const;
bool isQtQuick3DInstanceListEntry() const; bool isQtQuick3DInstanceListEntry() const;
bool isQtQuick3DLight() const; bool isQtQuick3DLight() const;
bool isQtQuickListElement() const;
bool isQtQuickListModel() const; bool isQtQuickListModel() const;
bool isQtQuickListView() const; bool isQtQuickListView() const;
bool isQtQuick3DMaterial() const; bool isQtQuick3DMaterial() const;

View File

@@ -64,6 +64,8 @@
#include <qmlitemnode.h> #include <qmlitemnode.h>
#include <rewriterview.h> #include <rewriterview.h>
#include <projectstorage/projectstorage.h>
#include <utils/hdrimage.h> #include <utils/hdrimage.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
@@ -205,22 +207,17 @@ NodeInstanceView::~NodeInstanceView()
static bool isSkippedRootNode(const ModelNode &node) static bool isSkippedRootNode(const ModelNode &node)
{ {
static const PropertyNameList skipList({"Qt.ListModel", "QtQuick.ListModel", "Qt.ListModel", "QtQuick.ListModel"}); return node.metaInfo().isQtQuickListModel();
if (skipList.contains(node.type()))
return true;
return false;
} }
static bool isSkippedNode(const ModelNode &node) static bool isSkippedNode(const ModelNode &node)
{ {
static const PropertyNameList skipList({"QtQuick.XmlRole", "Qt.XmlRole", "QtQuick.ListElement", "Qt.ListElement"}); auto model = node.model();
if (skipList.contains(node.type())) auto listElement = model->qtQmlModelsListElementMetaInfo();
return true; auto xmlRole = model->qtQmlXmlListModelXmlListModelRoleMetaInfo();
return false; return node.metaInfo().isBasedOn(listElement, xmlRole);
} }
static bool parentTakesOverRendering(const ModelNode &modelNode) static bool parentTakesOverRendering(const ModelNode &modelNode)
@@ -644,7 +641,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
TypeName(), TypeName(),
key.type}; key.type};
m_nodeInstanceServer->changeAuxiliaryValues({{container}}); m_nodeInstanceServer->changeAuxiliaryValues({{container}});
}; }
break; break;
case AuxiliaryDataType::NodeInstanceAuxiliary: case AuxiliaryDataType::NodeInstanceAuxiliary:
@@ -656,7 +653,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
TypeName(), TypeName(),
key.type}; key.type};
m_nodeInstanceServer->changeAuxiliaryValues({{container}}); m_nodeInstanceServer->changeAuxiliaryValues({{container}});
}; }
break; break;
case AuxiliaryDataType::NodeInstancePropertyOverwrite: case AuxiliaryDataType::NodeInstancePropertyOverwrite:
@@ -991,6 +988,8 @@ QRectF NodeInstanceView::sceneRect() const
return {}; return {};
} }
namespace {
QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList) QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
{ {
QList<ModelNode> filteredNodeList; QList<ModelNode> filteredNodeList;
@@ -1003,14 +1002,12 @@ QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
return filteredNodeList; return filteredNodeList;
} }
namespace {
bool shouldSendAuxiliary(const AuxiliaryDataKey &key) bool shouldSendAuxiliary(const AuxiliaryDataKey &key)
{ {
return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite
|| key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty || key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty
|| key == lockedProperty; || key == lockedProperty;
} }
} // namespace
bool parentIsBehavior(ModelNode node) bool parentIsBehavior(ModelNode node)
{ {
@@ -1024,6 +1021,31 @@ bool parentIsBehavior(ModelNode node)
return false; return false;
} }
TypeName createQualifiedTypeName(const ModelNode &node)
{
if (!node)
return {};
#ifdef QDS_USE_PROJECTSTORAGE
auto model = node.model();
auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId());
if (exportedTypes.size()) {
const auto &exportedType = exportedTypes.front();
Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId);
typeName += '/';
typeName += exportedType.name;
return typeName.toQByteArray();
}
return {};
#else
return node.type();
#endif
}
} // namespace
CreateSceneCommand NodeInstanceView::createCreateSceneCommand() CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
{ {
QList<ModelNode> nodeList = allModelNodes(); QList<ModelNode> nodeList = allModelNodes();
@@ -1079,8 +1101,9 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
nodeFlags |= InstanceContainer::ParentTakesOverRendering; nodeFlags |= InstanceContainer::ParentTakesOverRendering;
const auto modelNode = instance.modelNode(); const auto modelNode = instance.modelNode();
InstanceContainer container(instance.instanceId(), InstanceContainer container(instance.instanceId(),
modelNode.type(), createQualifiedTypeName(modelNode),
modelNode.majorVersion(), modelNode.majorVersion(),
modelNode.minorVersion(), modelNode.minorVersion(),
ModelUtils::componentFilePath(modelNode), ModelUtils::componentFilePath(modelNode),
@@ -1243,7 +1266,7 @@ CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QLis
const auto modelNode = instance.modelNode(); const auto modelNode = instance.modelNode();
InstanceContainer container(instance.instanceId(), InstanceContainer container(instance.instanceId(),
modelNode.type(), createQualifiedTypeName(modelNode),
modelNode.majorVersion(), modelNode.majorVersion(),
modelNode.minorVersion(), modelNode.minorVersion(),
ModelUtils::componentFilePath(modelNode), ModelUtils::componentFilePath(modelNode),
@@ -1850,7 +1873,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
ModelNodePreviewImageData imageData; ModelNodePreviewImageData imageData;
imageData.id = modelNode.id(); imageData.id = modelNode.id();
imageData.type = QString::fromLatin1(modelNode.type()); imageData.type = QString::fromUtf8(createQualifiedTypeName(modelNode));
const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); const double ratio = m_externalDependencies.formEditorDevicePixelRatio();
if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) { if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) {
@@ -1958,7 +1981,7 @@ QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &model
if (m_imageDataMap.contains(id)) { if (m_imageDataMap.contains(id)) {
imageData = m_imageDataMap[id]; imageData = m_imageDataMap[id];
} else { } else {
imageData.type = QString::fromLatin1(modelNode.type()); imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode));
imageData.id = id; imageData.id = id;
m_imageDataMap.insert(id, imageData); m_imageDataMap.insert(id, imageData);
} }

View File

@@ -2766,7 +2766,7 @@ bool NodeMetaInfo::isQtQuick3DLight() const
} }
} }
bool NodeMetaInfo::isQtQuickListElement() const bool NodeMetaInfo::isQtQmlModelsListElement() const
{ {
if constexpr (useProjectStorage()) { if constexpr (useProjectStorage()) {
using namespace Storage::Info; using namespace Storage::Info;

View File

@@ -2210,6 +2210,16 @@ NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const
} }
} }
NodeMetaInfo Model::qtQmlXmlListModelXmlListModelRoleMetaInfo() const
{
if constexpr (useProjectStorage()) {
using namespace Storage::Info;
return createNodeMetaInfo<QtQml_XmlListModel, XmlListModelRole>();
} else {
return metaInfo("QtQml.XmlListModel.XmlListModelRole");
}
}
NodeMetaInfo Model::qmlQtObjectMetaInfo() const NodeMetaInfo Model::qmlQtObjectMetaInfo() const
{ {
if constexpr (useProjectStorage()) { if constexpr (useProjectStorage()) {

View File

@@ -501,7 +501,7 @@ public:
if (!propertyMetaInfo.isValid()) { if (!propertyMetaInfo.isValid()) {
const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper(); const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper();
// Only list elements might have unknown properties. // Only list elements might have unknown properties.
if (!node.metaInfo().isQtQuickListElement() && !isAttached) { if (!node.metaInfo().isQtQmlModelsListElement() && !isAttached) {
qCInfo(texttomodelMergerLog) qCInfo(texttomodelMergerLog)
<< Q_FUNC_INFO << "\nUnknown property" << Q_FUNC_INFO << "\nUnknown property"
<< propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line"

View File

@@ -91,6 +91,7 @@ inline constexpr char QtMultimedia[] = "QtMultimedia";
inline constexpr char QtObject[] = "QtObject"; inline constexpr char QtObject[] = "QtObject";
inline constexpr char QtQml[] = "QtQml"; inline constexpr char QtQml[] = "QtQml";
inline constexpr char QtQml_Models[] = "QtQml.Models"; inline constexpr char QtQml_Models[] = "QtQml.Models";
inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel";
inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D[] = "QtQuick3D";
inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D";
inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative";
@@ -131,6 +132,7 @@ inline constexpr char Transition[] = "Transition";
inline constexpr char UIntType[] = "uint"; inline constexpr char UIntType[] = "uint";
inline constexpr char View3D[] = "View3D"; inline constexpr char View3D[] = "View3D";
inline constexpr char Window[] = "Window"; inline constexpr char Window[] = "Window";
inline constexpr char XmlListModelRole[] = "XmlListModelRole";
inline constexpr char color[] = "color"; inline constexpr char color[] = "color";
inline constexpr char date[] = "date"; inline constexpr char date[] = "date";
inline constexpr char font[] = "font"; inline constexpr char font[] = "font";
@@ -176,6 +178,7 @@ class CommonTypeCache
CacheType<QtMultimedia, SoundEffect>, CacheType<QtMultimedia, SoundEffect>,
CacheType<QtQml_Models, ListElement>, CacheType<QtQml_Models, ListElement>,
CacheType<QtQml_Models, ListModel>, CacheType<QtQml_Models, ListModel>,
CacheType<QtQml_XmlListModel, XmlListModelRole>,
CacheType<QtQuick, BorderImage>, CacheType<QtQuick, BorderImage>,
CacheType<QtQuick, GridView>, CacheType<QtQuick, GridView>,
CacheType<QtQuick, Image>, CacheType<QtQuick, Image>,

View File

@@ -168,7 +168,7 @@ public:
return moduleId; return moduleId;
} }
Utils::SmallString moduleName(ModuleId moduleId) const Utils::SmallString moduleName(ModuleId moduleId) const override
{ {
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get module name"_t, NanotraceHR::Tracer tracer{"get module name"_t,
@@ -3278,8 +3278,9 @@ private:
auto update = [&](const TypeWithDefaultPropertyView &view, auto update = [&](const TypeWithDefaultPropertyView &view,
const Storage::Synchronization::Type &value) { const Storage::Synchronization::Type &value) {
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"reset default properties by update"_t, NanotraceHR::Tracer tracer{"synchronize default properties by update"_t,
projectStorageCategory(), projectStorageCategory(),
keyValue("type id", value.typeId),
keyValue("value", value), keyValue("value", value),
keyValue("view", view)}; keyValue("view", view)};
@@ -3294,7 +3295,8 @@ private:
updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId);
tracer.end(keyValue("updated", "yes")); tracer.end(keyValue("updated", "yes"),
keyValue("default property id", valueDefaultPropertyId));
return Sqlite::UpdateChange::Update; return Sqlite::UpdateChange::Update;
}; };
@@ -3324,6 +3326,7 @@ private:
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, NanotraceHR::Tracer tracer{"reset changed default properties by update"_t,
projectStorageCategory(), projectStorageCategory(),
keyValue("type id", value.typeId),
keyValue("value", value), keyValue("value", value),
keyValue("view", view)}; keyValue("view", view)};

View File

@@ -32,6 +32,7 @@ public:
virtual void removeObserver(ProjectStorageObserver *observer) = 0; virtual void removeObserver(ProjectStorageObserver *observer) = 0;
virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0;
virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0;
virtual std::optional<Storage::Info::PropertyDeclaration> virtual std::optional<Storage::Info::PropertyDeclaration>
propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0;
virtual TypeId typeId(ModuleId moduleId, virtual TypeId typeId(ModuleId moduleId,

View File

@@ -230,7 +230,9 @@ void DocumentManager::setCurrentDesignDocument(Core::IEditor *editor)
auto found = m_designDocuments.find(editor); auto found = m_designDocuments.find(editor);
if (found == m_designDocuments.end()) { if (found == m_designDocuments.end()) {
auto &inserted = m_designDocuments[editor] = std::make_unique<DesignDocument>( auto &inserted = m_designDocuments[editor] = std::make_unique<DesignDocument>(
m_projectManager.projectStorageDependencies(), m_externalDependencies); editor->document()->filePath().toString(),
m_projectManager.projectStorageDependencies(),
m_externalDependencies);
m_currentDesignDocument = inserted.get(); m_currentDesignDocument = inserted.get();
m_currentDesignDocument->setEditor(editor); m_currentDesignDocument->setEditor(editor);
} else { } else {

View File

@@ -51,6 +51,7 @@ ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName)
incrementBasicId(moduleId); incrementBasicId(moduleId);
ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId));
ON_CALL(*this, moduleName(Eq(moduleId))).WillByDefault(Return(moduleName));
ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId)); ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId));
return moduleId; return moduleId;
@@ -122,6 +123,14 @@ void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId,
exportedTypeName[typeId].emplace_back(moduleId, typeName); exportedTypeName[typeId].emplace_back(moduleId, typeName);
} }
void ProjectStorageMock::addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
QmlDesigner::SourceId sourceId)
{
exportedTypeNameBySourceId[{typeId, sourceId}].emplace_back(moduleId, typeName);
}
void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId, void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId, QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName) Utils::SmallStringView typeName)
@@ -364,6 +373,10 @@ ProjectStorageMock::ProjectStorageMock()
ON_CALL(*this, exportedTypeNames(_)).WillByDefault([&](TypeId id) { ON_CALL(*this, exportedTypeNames(_)).WillByDefault([&](TypeId id) {
return exportedTypeName[id]; return exportedTypeName[id];
}); });
ON_CALL(*this, exportedTypeNames(_, _)).WillByDefault([&](TypeId typeId, SourceId sourceId) {
return exportedTypeNameBySourceId[{typeId, sourceId}];
});
} }
void ProjectStorageMock::setupQtQuick() void ProjectStorageMock::setupQtQuick()

View File

@@ -46,6 +46,11 @@ public:
QmlDesigner::ModuleId moduleId, QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName); Utils::SmallStringView typeName);
void addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName,
QmlDesigner::SourceId sourceId);
void removeExportedTypeName(QmlDesigner::TypeId typeId, void removeExportedTypeName(QmlDesigner::TypeId typeId,
QmlDesigner::ModuleId moduleId, QmlDesigner::ModuleId moduleId,
Utils::SmallStringView typeName); Utils::SmallStringView typeName);
@@ -122,6 +127,7 @@ public:
MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override)); MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override));
MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override));
MOCK_METHOD(Utils::SmallString, moduleName, (QmlDesigner::ModuleId), (const, override));
MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>, MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>,
propertyDeclaration, propertyDeclaration,
@@ -331,6 +337,8 @@ public:
QmlDesigner::Storage::Info::CommonTypeCache<QmlDesigner::ProjectStorageInterface> typeCache{*this}; QmlDesigner::Storage::Info::CommonTypeCache<QmlDesigner::ProjectStorageInterface> typeCache{*this};
std::map<QmlDesigner::TypeId, QmlDesigner::Storage::Info::ExportedTypeNames> exportedTypeName; std::map<QmlDesigner::TypeId, QmlDesigner::Storage::Info::ExportedTypeNames> exportedTypeName;
std::map<std::pair<QmlDesigner::TypeId, QmlDesigner::SourceId>, QmlDesigner::Storage::Info::ExportedTypeNames>
exportedTypeNameBySourceId;
}; };
class ProjectStorageMockWithQtQtuick : public ProjectStorageMock class ProjectStorageMockWithQtQtuick : public ProjectStorageMock

View File

@@ -141,7 +141,7 @@ TEST_F(ModelUtils, find_lowest_common_ancestor_when_one_of_the_nodes_is_parent)
ASSERT_THAT(commonAncestor, parentNode); ASSERT_THAT(commonAncestor, parentNode);
} }
TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandFather) TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandfather)
{ {
auto grandFatherNode = model.createModelNode("Item"); auto grandFatherNode = model.createModelNode("Item");
auto fatherNode = model.createModelNode("Item"); auto fatherNode = model.createModelNode("Item");