forked from qt-creator/qt-creator
Add rewriter testing
Adding only the framework to write the rewriter tests. Task-number: QDS-13406 Change-Id: If4a7476d09595624c3a752411d516f4d9f3d601a Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -10,6 +10,7 @@ option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDI
|
|||||||
add_feature_info("Qml Designer Lite" ${QTC_USE_QML_DESIGNER_LITE} "")
|
add_feature_info("Qml Designer Lite" ${QTC_USE_QML_DESIGNER_LITE} "")
|
||||||
|
|
||||||
option(USE_PROJECTSTORAGE "Use ProjectStorage" ${QTC_USE_QML_DESIGNER_LITE})
|
option(USE_PROJECTSTORAGE "Use ProjectStorage" ${QTC_USE_QML_DESIGNER_LITE})
|
||||||
|
add_feature_info("Use project storage" ${USE_PROJECTSTORAGE} "")
|
||||||
option(DETACH_DISABLED_VIEWS "Detach disabled views" OFF)
|
option(DETACH_DISABLED_VIEWS "Detach disabled views" OFF)
|
||||||
|
|
||||||
env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF)
|
env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF)
|
||||||
|
@@ -42,6 +42,8 @@ struct QmlTypeData
|
|||||||
bool isCppType = false;
|
bool isCppType = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class InstantQmlTextUpdate { No, Yes };
|
||||||
|
|
||||||
class QMLDESIGNERCORE_EXPORT RewriterView : public AbstractView
|
class QMLDESIGNERCORE_EXPORT RewriterView : public AbstractView
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -54,7 +56,8 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
RewriterView(ExternalDependenciesInterface &externalDependencies,
|
RewriterView(ExternalDependenciesInterface &externalDependencies,
|
||||||
DifferenceHandling differenceHandling = RewriterView::Amend);
|
DifferenceHandling differenceHandling = RewriterView::Amend,
|
||||||
|
InstantQmlTextUpdate instantQmlTextUpdate = InstantQmlTextUpdate::Yes);
|
||||||
~RewriterView() override;
|
~RewriterView() override;
|
||||||
|
|
||||||
void modelAttached(Model *model) override;
|
void modelAttached(Model *model) override;
|
||||||
@@ -189,8 +192,10 @@ protected: // functions
|
|||||||
private: //variables
|
private: //variables
|
||||||
ModelNode nodeAtTextCursorPositionHelper(const ModelNode &root, int cursorPosition) const;
|
ModelNode nodeAtTextCursorPositionHelper(const ModelNode &root, int cursorPosition) const;
|
||||||
void setupCanonicalHashes() const;
|
void setupCanonicalHashes() const;
|
||||||
|
#ifndef QDS_USE_PROJECTSTORAGE
|
||||||
void handleLibraryInfoUpdate();
|
void handleLibraryInfoUpdate();
|
||||||
void handleProjectUpdate();
|
void handleProjectUpdate();
|
||||||
|
#endif
|
||||||
bool inErrorState() const { return !m_rewritingErrorMessage.isEmpty(); }
|
bool inErrorState() const { return !m_rewritingErrorMessage.isEmpty(); }
|
||||||
|
|
||||||
QPointer<TextModifier> m_textModifier;
|
QPointer<TextModifier> m_textModifier;
|
||||||
@@ -209,7 +214,7 @@ private: //variables
|
|||||||
QString m_rewritingErrorMessage;
|
QString m_rewritingErrorMessage;
|
||||||
QString m_lastCorrectQmlSource;
|
QString m_lastCorrectQmlSource;
|
||||||
QTimer m_amendTimer;
|
QTimer m_amendTimer;
|
||||||
bool m_instantQmlTextUpdate = false;
|
InstantQmlTextUpdate m_instantQmlTextUpdate = InstantQmlTextUpdate::No;
|
||||||
std::function<void(bool)> m_setWidgetStatusCallback;
|
std::function<void(bool)> m_setWidgetStatusCallback;
|
||||||
bool m_hasIncompleteTypeInformation = false;
|
bool m_hasIncompleteTypeInformation = false;
|
||||||
bool m_restoringAuxData = false;
|
bool m_restoringAuxData = false;
|
||||||
|
@@ -23,8 +23,8 @@ class QMLDESIGNERCORE_EXPORT TextModifier: public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextModifier(const TextModifier &);
|
TextModifier(const TextModifier &) = delete;
|
||||||
TextModifier &operator=(const TextModifier &);
|
TextModifier &operator=(const TextModifier &) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct MoveInfo {
|
struct MoveInfo {
|
||||||
@@ -42,7 +42,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
TextModifier() = default;
|
TextModifier() = default;
|
||||||
~TextModifier() override = 0;
|
~TextModifier();
|
||||||
|
|
||||||
virtual void replace(int offset, int length, const QString& replacement) = 0;
|
virtual void replace(int offset, int length, const QString& replacement) = 0;
|
||||||
virtual void move(const MoveInfo &moveInfo) = 0;
|
virtual void move(const MoveInfo &moveInfo) = 0;
|
||||||
|
@@ -53,20 +53,25 @@ bool debugQmlPuppet(const DesignerSettings &settings)
|
|||||||
}
|
}
|
||||||
|
|
||||||
RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies,
|
RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies,
|
||||||
DifferenceHandling differenceHandling)
|
DifferenceHandling differenceHandling,
|
||||||
|
InstantQmlTextUpdate instantQmlTextUpdate)
|
||||||
: AbstractView{externalDependencies}
|
: AbstractView{externalDependencies}
|
||||||
, m_differenceHandling(differenceHandling)
|
, m_differenceHandling(differenceHandling)
|
||||||
, m_positionStorage(std::make_unique<ModelNodePositionStorage>())
|
, m_positionStorage(std::make_unique<ModelNodePositionStorage>())
|
||||||
, m_modelToTextMerger(std::make_unique<Internal::ModelToTextMerger>(this))
|
, m_modelToTextMerger(std::make_unique<Internal::ModelToTextMerger>(this))
|
||||||
, m_textToModelMerger(std::make_unique<Internal::TextToModelMerger>(this))
|
, m_textToModelMerger(std::make_unique<Internal::TextToModelMerger>(this))
|
||||||
|
, m_instantQmlTextUpdate(instantQmlTextUpdate)
|
||||||
{
|
{
|
||||||
setKind(Kind::Rewriter);
|
setKind(Kind::Rewriter);
|
||||||
|
|
||||||
m_amendTimer.setSingleShot(true);
|
m_amendTimer.setSingleShot(true);
|
||||||
|
|
||||||
|
if (m_instantQmlTextUpdate == InstantQmlTextUpdate::No) {
|
||||||
m_amendTimer.setInterval(800);
|
m_amendTimer.setInterval(800);
|
||||||
connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText);
|
connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QDS_USE_PROJECTSTORAGE
|
||||||
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
|
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
|
||||||
connect(modelManager,
|
connect(modelManager,
|
||||||
&QmlJS::ModelManagerInterface::libraryInfoUpdated,
|
&QmlJS::ModelManagerInterface::libraryInfoUpdated,
|
||||||
@@ -83,6 +88,7 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies,
|
|||||||
this,
|
this,
|
||||||
&RewriterView::handleLibraryInfoUpdate,
|
&RewriterView::handleLibraryInfoUpdate,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
RewriterView::~RewriterView() = default;
|
RewriterView::~RewriterView() = default;
|
||||||
@@ -112,7 +118,9 @@ void RewriterView::modelAttached(Model *model)
|
|||||||
if (!(m_errors.isEmpty() && m_warnings.isEmpty()))
|
if (!(m_errors.isEmpty() && m_warnings.isEmpty()))
|
||||||
notifyErrorsAndWarnings(m_errors);
|
notifyErrorsAndWarnings(m_errors);
|
||||||
|
|
||||||
if (hasIncompleteTypeInformation()) {
|
if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes) {
|
||||||
|
restoreAuxiliaryData();
|
||||||
|
} else if (hasIncompleteTypeInformation()) {
|
||||||
m_modelAttachPending = true;
|
m_modelAttachPending = true;
|
||||||
QTimer::singleShot(1000, this, [this, model]() {
|
QTimer::singleShot(1000, this, [this, model]() {
|
||||||
modelAttached(model);
|
modelAttached(model);
|
||||||
@@ -884,6 +892,7 @@ void RewriterView::setupCanonicalHashes() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef QDS_USE_PROJECTSTORAGE
|
||||||
void RewriterView::handleLibraryInfoUpdate()
|
void RewriterView::handleLibraryInfoUpdate()
|
||||||
{
|
{
|
||||||
// Trigger dummy amend to reload document when library info changes
|
// Trigger dummy amend to reload document when library info changes
|
||||||
@@ -898,6 +907,7 @@ void RewriterView::handleProjectUpdate()
|
|||||||
{
|
{
|
||||||
emit modelInterfaceProjectUpdated();
|
emit modelInterfaceProjectUpdated();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ModelNode RewriterView::nodeAtTextCursorPosition(int cursorPosition) const
|
ModelNode RewriterView::nodeAtTextCursorPosition(int cursorPosition) const
|
||||||
{
|
{
|
||||||
@@ -913,8 +923,8 @@ bool RewriterView::renameId(const QString &oldId, const QString &newId)
|
|||||||
&& rootModelNode().hasBindingProperty(propertyName)
|
&& rootModelNode().hasBindingProperty(propertyName)
|
||||||
&& rootModelNode().bindingProperty(propertyName).isAliasExport();
|
&& rootModelNode().bindingProperty(propertyName).isAliasExport();
|
||||||
|
|
||||||
bool instant = m_instantQmlTextUpdate;
|
auto instant = m_instantQmlTextUpdate;
|
||||||
m_instantQmlTextUpdate = true;
|
m_instantQmlTextUpdate = InstantQmlTextUpdate::Yes;
|
||||||
|
|
||||||
bool refactoring = textModifier()->renameId(oldId, newId);
|
bool refactoring = textModifier()->renameId(oldId, newId);
|
||||||
|
|
||||||
@@ -1158,7 +1168,8 @@ void RewriterView::qmlTextChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Amend: {
|
case Amend: {
|
||||||
if (m_instantQmlTextUpdate || externalDependencies().instantQmlTextUpdate()) {
|
if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes
|
||||||
|
|| externalDependencies().instantQmlTextUpdate()) {
|
||||||
amendQmlText();
|
amendQmlText();
|
||||||
} else {
|
} else {
|
||||||
if (externalDependencies().viewManagerUsesRewriterView(this)) {
|
if (externalDependencies().viewManagerUsesRewriterView(this)) {
|
||||||
|
@@ -40,4 +40,5 @@ add_qtc_library(TestMocks OBJECT
|
|||||||
sqlitetransactionbackendmock.h
|
sqlitetransactionbackendmock.h
|
||||||
sqlitewritestatementmock.cpp
|
sqlitewritestatementmock.cpp
|
||||||
sqlitewritestatementmock.h
|
sqlitewritestatementmock.h
|
||||||
|
textmodifiermock.h
|
||||||
)
|
)
|
||||||
|
31
tests/unit/tests/mocks/textmodifiermock.h
Normal file
31
tests/unit/tests/mocks/textmodifiermock.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// 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 "../utils/googletest.h"
|
||||||
|
|
||||||
|
#include <textmodifier.h>
|
||||||
|
|
||||||
|
class TextModifierMock : public QmlDesigner::TextModifier
|
||||||
|
{
|
||||||
|
MOCK_METHOD(void, replace, (int offset, int length, const QString &replacement), (override));
|
||||||
|
MOCK_METHOD(void, move, (const MoveInfo &moveInfo), (override));
|
||||||
|
MOCK_METHOD(void, indent, (int offset, int length), (override));
|
||||||
|
MOCK_METHOD(void, indentLines, (int startLine, int endLine), (override));
|
||||||
|
MOCK_METHOD(TextEditor::TabSettings, tabSettings, (), (const, override));
|
||||||
|
MOCK_METHOD(void, startGroup, (), (override));
|
||||||
|
MOCK_METHOD(void, flushGroup, (), (override));
|
||||||
|
MOCK_METHOD(void, commitGroup, (), (override));
|
||||||
|
MOCK_METHOD(QTextDocument *, textDocument, (), (const, override));
|
||||||
|
MOCK_METHOD(QString, text, (), (const, override));
|
||||||
|
MOCK_METHOD(QTextCursor, textCursor, (), (const, override));
|
||||||
|
MOCK_METHOD(void, deactivateChangeSignals, (), (override));
|
||||||
|
MOCK_METHOD(void, reactivateChangeSignals, (), (override));
|
||||||
|
MOCK_METHOD(bool, renameId, (const QString &oldId, const QString &newId), (override));
|
||||||
|
MOCK_METHOD(QStringList,
|
||||||
|
autoComplete,
|
||||||
|
(QTextDocument * textDocument, int positio, bool explicitComplete),
|
||||||
|
(override));
|
||||||
|
MOCK_METHOD(bool, moveToComponent, (int nodeOffset, const QString &importData), (override));
|
||||||
|
};
|
@@ -7,4 +7,5 @@ extend_qtc_test(unittest
|
|||||||
modelnode-test.cpp
|
modelnode-test.cpp
|
||||||
modelresourcemanagement-test.cpp
|
modelresourcemanagement-test.cpp
|
||||||
nodelistproperty-test.cpp
|
nodelistproperty-test.cpp
|
||||||
|
rewriterview-test.cpp
|
||||||
)
|
)
|
||||||
|
45
tests/unit/tests/unittests/model/rewriterview-test.cpp
Normal file
45
tests/unit/tests/unittests/model/rewriterview-test.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "../utils/googletest.h"
|
||||||
|
|
||||||
|
#include <externaldependenciesmock.h>
|
||||||
|
#include <mocks/abstractviewmock.h>
|
||||||
|
#include <mocks/modelresourcemanagementmock.h>
|
||||||
|
#include <mocks/projectstoragemock.h>
|
||||||
|
#include <mocks/sourcepathcachemock.h>
|
||||||
|
#include <rewriterview.h>
|
||||||
|
#include <textmodifiermock.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using QmlDesigner::AbstractView;
|
||||||
|
|
||||||
|
class RewriterView : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
RewriterView()
|
||||||
|
{
|
||||||
|
rewriter.setTextModifier(&textModifierMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RewriterView() { model.setRewriterView(nullptr); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
NiceMock<ExternalDependenciesMock> externalDependenciesMock;
|
||||||
|
NiceMock<TextModifierMock> textModifierMock;
|
||||||
|
NiceMock<SourcePathCacheMockWithPaths> pathCacheMock{"/path/foo.qml"};
|
||||||
|
NiceMock<ProjectStorageMockWithQtQuick> projectStorageMock{pathCacheMock.sourceId, "/path"};
|
||||||
|
NiceMock<ModelResourceManagementMock> resourceManagementMock;
|
||||||
|
QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")};
|
||||||
|
QmlDesigner::Model model{{projectStorageMock, pathCacheMock},
|
||||||
|
"Item",
|
||||||
|
imports,
|
||||||
|
QUrl::fromLocalFile(pathCacheMock.path.toQString()),
|
||||||
|
std::make_unique<ModelResourceManagementMockWrapper>(
|
||||||
|
resourceManagementMock)};
|
||||||
|
QmlDesigner::RewriterView rewriter{externalDependenciesMock};
|
||||||
|
NiceMock<AbstractViewMock> view;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
Reference in New Issue
Block a user