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:
Marco Bubke
2024-09-04 18:58:12 +02:00
parent 18ca16cb25
commit 09e5e133bb
8 changed files with 107 additions and 12 deletions

View File

@@ -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} "")
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)
env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF)

View File

@@ -42,6 +42,8 @@ struct QmlTypeData
bool isCppType = false;
};
enum class InstantQmlTextUpdate { No, Yes };
class QMLDESIGNERCORE_EXPORT RewriterView : public AbstractView
{
Q_OBJECT
@@ -54,7 +56,8 @@ public:
public:
RewriterView(ExternalDependenciesInterface &externalDependencies,
DifferenceHandling differenceHandling = RewriterView::Amend);
DifferenceHandling differenceHandling = RewriterView::Amend,
InstantQmlTextUpdate instantQmlTextUpdate = InstantQmlTextUpdate::Yes);
~RewriterView() override;
void modelAttached(Model *model) override;
@@ -189,8 +192,10 @@ protected: // functions
private: //variables
ModelNode nodeAtTextCursorPositionHelper(const ModelNode &root, int cursorPosition) const;
void setupCanonicalHashes() const;
#ifndef QDS_USE_PROJECTSTORAGE
void handleLibraryInfoUpdate();
void handleProjectUpdate();
#endif
bool inErrorState() const { return !m_rewritingErrorMessage.isEmpty(); }
QPointer<TextModifier> m_textModifier;
@@ -209,7 +214,7 @@ private: //variables
QString m_rewritingErrorMessage;
QString m_lastCorrectQmlSource;
QTimer m_amendTimer;
bool m_instantQmlTextUpdate = false;
InstantQmlTextUpdate m_instantQmlTextUpdate = InstantQmlTextUpdate::No;
std::function<void(bool)> m_setWidgetStatusCallback;
bool m_hasIncompleteTypeInformation = false;
bool m_restoringAuxData = false;

View File

@@ -23,8 +23,8 @@ class QMLDESIGNERCORE_EXPORT TextModifier: public QObject
Q_OBJECT
private:
TextModifier(const TextModifier &);
TextModifier &operator=(const TextModifier &);
TextModifier(const TextModifier &) = delete;
TextModifier &operator=(const TextModifier &) = delete;
public:
struct MoveInfo {
@@ -42,7 +42,7 @@ public:
public:
TextModifier() = default;
~TextModifier() override = 0;
~TextModifier();
virtual void replace(int offset, int length, const QString& replacement) = 0;
virtual void move(const MoveInfo &moveInfo) = 0;

View File

@@ -53,20 +53,25 @@ bool debugQmlPuppet(const DesignerSettings &settings)
}
RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies,
DifferenceHandling differenceHandling)
DifferenceHandling differenceHandling,
InstantQmlTextUpdate instantQmlTextUpdate)
: AbstractView{externalDependencies}
, m_differenceHandling(differenceHandling)
, m_positionStorage(std::make_unique<ModelNodePositionStorage>())
, m_modelToTextMerger(std::make_unique<Internal::ModelToTextMerger>(this))
, m_textToModelMerger(std::make_unique<Internal::TextToModelMerger>(this))
, m_instantQmlTextUpdate(instantQmlTextUpdate)
{
setKind(Kind::Rewriter);
m_amendTimer.setSingleShot(true);
m_amendTimer.setInterval(800);
connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText);
if (m_instantQmlTextUpdate == InstantQmlTextUpdate::No) {
m_amendTimer.setInterval(800);
connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText);
}
#ifndef QDS_USE_PROJECTSTORAGE
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
connect(modelManager,
&QmlJS::ModelManagerInterface::libraryInfoUpdated,
@@ -83,6 +88,7 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies,
this,
&RewriterView::handleLibraryInfoUpdate,
Qt::QueuedConnection);
#endif
}
RewriterView::~RewriterView() = default;
@@ -112,7 +118,9 @@ void RewriterView::modelAttached(Model *model)
if (!(m_errors.isEmpty() && m_warnings.isEmpty()))
notifyErrorsAndWarnings(m_errors);
if (hasIncompleteTypeInformation()) {
if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes) {
restoreAuxiliaryData();
} else if (hasIncompleteTypeInformation()) {
m_modelAttachPending = true;
QTimer::singleShot(1000, this, [this, model]() {
modelAttached(model);
@@ -884,6 +892,7 @@ void RewriterView::setupCanonicalHashes() const
}
}
#ifndef QDS_USE_PROJECTSTORAGE
void RewriterView::handleLibraryInfoUpdate()
{
// Trigger dummy amend to reload document when library info changes
@@ -898,6 +907,7 @@ void RewriterView::handleProjectUpdate()
{
emit modelInterfaceProjectUpdated();
}
#endif
ModelNode RewriterView::nodeAtTextCursorPosition(int cursorPosition) const
{
@@ -913,8 +923,8 @@ bool RewriterView::renameId(const QString &oldId, const QString &newId)
&& rootModelNode().hasBindingProperty(propertyName)
&& rootModelNode().bindingProperty(propertyName).isAliasExport();
bool instant = m_instantQmlTextUpdate;
m_instantQmlTextUpdate = true;
auto instant = m_instantQmlTextUpdate;
m_instantQmlTextUpdate = InstantQmlTextUpdate::Yes;
bool refactoring = textModifier()->renameId(oldId, newId);
@@ -1158,7 +1168,8 @@ void RewriterView::qmlTextChanged()
}
case Amend: {
if (m_instantQmlTextUpdate || externalDependencies().instantQmlTextUpdate()) {
if (m_instantQmlTextUpdate == InstantQmlTextUpdate::Yes
|| externalDependencies().instantQmlTextUpdate()) {
amendQmlText();
} else {
if (externalDependencies().viewManagerUsesRewriterView(this)) {

View File

@@ -40,4 +40,5 @@ add_qtc_library(TestMocks OBJECT
sqlitetransactionbackendmock.h
sqlitewritestatementmock.cpp
sqlitewritestatementmock.h
textmodifiermock.h
)

View 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));
};

View File

@@ -7,4 +7,5 @@ extend_qtc_test(unittest
modelnode-test.cpp
modelresourcemanagement-test.cpp
nodelistproperty-test.cpp
rewriterview-test.cpp
)

View 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