summaryrefslogtreecommitdiff
path: root/tests/auto
diff options
context:
space:
mode:
authorOlivier De Cannière <olivier.decanniere@qt.io>2023-05-04 10:31:12 +0200
committerOlivier De Cannière <olivier.decanniere@qt.io>2023-05-04 14:01:25 +0200
commitb153740606956da52ace34a43214b8c70dff5f9e (patch)
treee7fef08838cfebe090034ccfae7e1898fd092353 /tests/auto
parent757f50166b5186a1fcfb5e7e5737801f094fda1b (diff)
downloadqtdeclarative-b153740606956da52ace34a43214b8c70dff5f9e.tar.gz
QmlDom: Remove the standalone version of QmlDom
Change-Id: I2582f3ca0217ec9791ead71393cfa506c28086b8 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'tests/auto')
-rw-r--r--tests/auto/qmldom/CMakeLists.txt1
-rw-r--r--tests/auto/qmldom/combined/CMakeLists.txt1
-rw-r--r--tests/auto/qmldom/combined/tst_dom_all.cpp5
-rw-r--r--tests/auto/qmldom/standalone/CMakeLists.txt39
-rw-r--r--tests/auto/qmldom/standalone/tst_standalone.cpp5
-rw-r--r--tests/auto/qmldom/standalone/tst_standalone.h634
6 files changed, 0 insertions, 685 deletions
diff --git a/tests/auto/qmldom/CMakeLists.txt b/tests/auto/qmldom/CMakeLists.txt
index 7e2a0b885f..f3186e2263 100644
--- a/tests/auto/qmldom/CMakeLists.txt
+++ b/tests/auto/qmldom/CMakeLists.txt
@@ -10,4 +10,3 @@ add_subdirectory(stringdumper)
add_subdirectory(merging)
add_subdirectory(reformatter)
add_subdirectory(combined)
-add_subdirectory(standalone)
diff --git a/tests/auto/qmldom/combined/CMakeLists.txt b/tests/auto/qmldom/combined/CMakeLists.txt
index 6903f89eff..203184273e 100644
--- a/tests/auto/qmldom/combined/CMakeLists.txt
+++ b/tests/auto/qmldom/combined/CMakeLists.txt
@@ -20,7 +20,6 @@ qt_internal_add_test(tst_dom_all
../domitem/tst_qmldomitem.h
../merging/tst_dommerging.h
../reformatter/tst_reformatter.h
- ../standalone/tst_standalone.h
INCLUDE_DIRECTORIES
..
DEFINES
diff --git a/tests/auto/qmldom/combined/tst_dom_all.cpp b/tests/auto/qmldom/combined/tst_dom_all.cpp
index b3920f8e41..25706de12f 100644
--- a/tests/auto/qmldom/combined/tst_dom_all.cpp
+++ b/tests/auto/qmldom/combined/tst_dom_all.cpp
@@ -7,7 +7,6 @@
#include "merging/tst_dommerging.h"
#include "path/tst_qmldompath.h"
#include "reformatter/tst_reformatter.h"
-#include "standalone/tst_standalone.h"
#include <QtCore/qdebug.h>
@@ -38,10 +37,6 @@ int main(int argc, char *argv[])
QQmlJS::Dom::TestReformatter test;
status |= QTest::qExec(&test, argc, argv);
}
- {
- QQmlJS::Dom::TestStandalone test;
- status |= QTest::qExec(&test, argc, argv);
- }
if (status)
qWarning() << "Combined test failed!";
return status;
diff --git a/tests/auto/qmldom/standalone/CMakeLists.txt b/tests/auto/qmldom/standalone/CMakeLists.txt
deleted file mode 100644
index e141ec0835..0000000000
--- a/tests/auto/qmldom/standalone/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-#####################################################################
-## tst_dom_all Binary executing all tests together
-## (simpler to verify coverage)
-#####################################################################
-# Collect test data
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
- domdata/*)
-list(APPEND test_data ${test_data_glob})
-
-set(QMLDOM_EXTERNAL_BUILD OFF CACHE BOOL "If the build is against an external Qt, and not tested inside a build of this Qt" FORCE)
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/qmldom/standalone qmldomlib)
-
-if(MSVC)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
-elseif (MINGW)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
-endif()
-
-qt_internal_add_test(tst_standalone
- SOURCES
- tst_standalone.cpp
- DEFINES
- QT_DEPRECATED_WARNINGS
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
- LIBRARIES
- Qt::Core
- Qt::QmlPrivate
- qmldomlib
- TESTDATA ${test_data}
-)
-
-qt_internal_extend_target(tst_standalone CONDITION ANDROID OR IOS
- DEFINES
- QT_QMLTEST_DATADIR=":/domdata"
-)
diff --git a/tests/auto/qmldom/standalone/tst_standalone.cpp b/tests/auto/qmldom/standalone/tst_standalone.cpp
deleted file mode 100644
index 14b6424525..0000000000
--- a/tests/auto/qmldom/standalone/tst_standalone.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "tst_standalone.h"
-QTEST_MAIN(QQmlJS::Dom::TestStandalone)
diff --git a/tests/auto/qmldom/standalone/tst_standalone.h b/tests/auto/qmldom/standalone/tst_standalone.h
deleted file mode 100644
index 8bea75dda6..0000000000
--- a/tests/auto/qmldom/standalone/tst_standalone.h
+++ /dev/null
@@ -1,634 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <QtTest/QtTest>
-#ifdef QMLDOM_STANDALONE
-# include "qmldom/qqmldom_global.h"
-// common declarations
-# include "qmldom/qqmldomitem_p.h"
-// comparisons of two DomItems
-# include "qmldom/qqmldomcompare_p.h"
-// field filters to compare only selected fields (ignore for example location changes)
-# include "qmldom/qqmldomfieldfilter_p.h"
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-# include "qmldom/qqmldomelements_p.h"
-// cast of the top level items (DomEnvironments,...)
-# include "qmldom/qqmldomtop_p.h"
-#else
-# include <QtQmlDom/qqmldom_global.h>
-// common declarations
-# include <QtQmlDom/private/qqmldomitem_p.h>
-// comparisons of two DomItems
-# include <QtQmlDom/private/qqmldomcompare_p.h>
-// field filters to compare only selected fields (ignore for example location changes)
-# include <QtQmlDom/private/qqmldomfieldfilter_p.h>
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-# include <QtQmlDom/private/qqmldomelements_p.h>
-// cast of the top level items (DomEnvironments,...)
-# include <QtQmlDom/private/qqmldomtop_p.h>
-#endif
-
-#include <QDebug>
-#include <QLatin1String>
-#include <QLatin1Char>
-#include <QLibraryInfo>
-#include <QDir>
-
-QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace Dom {
-
-class TestStandalone : public QObject
-{
- Q_OBJECT
-private slots:
- void testLoadEditSave()
- {
- QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
- QStringList qmltypeDirs =
- QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
-
- // qDebug() << "Creating an environment loading qml from the directories" << qmltypeDirs;
- // qDebug() << "single threaded, no dependencies";
- DomItem env = DomEnvironment::create(
- qmltypeDirs,
- QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
- | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
-
- QString testFilePath = baseDir + QLatin1String("/file1.qml");
- DomItem tFile; // place where to store the loaded file
-
- // qDebug() << "loading the file" << testFilePath;
- env.loadFile(
- FileToLoad::fromFileSystem(env.ownerAs<DomEnvironment>(), testFilePath),
- [&tFile](Path, const DomItem &, const DomItem &newIt) {
- tFile = newIt; // callback called when everything is loaded that receives the
- // loaded external file pair (path, oldValue, newValue)
- },
- LoadOption::DefaultLoad);
-
- // trigger the load
- env.loadPendingDependencies();
-
- // # Read only API: DomItem is a generic pointer for read only access to Dom Itmes :)
- {
- // ## declarative json like API
- DomItem qmlFile = tFile.field(Fields::currentItem);
- DomItem imports = qmlFile.field(Fields::imports);
- DomItem qmlObj = qmlFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
-
- // ### Dump
- // any DomItem can be dumped
- // qDebug() << "writing to QDebug dumps that element:" << imports;
- // often the dump is too verbose, and one might want it to a separate file
- QString dumpFilePath = QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- qmlFile.dump(dumpFilePath, FieldFilter::defaultFilter());
- // qDebug() << "dumped file to" << dumpFilePath;
-
- // ### Paths
- // qDebug() << "To identify a DomItem a canonical path can be used:"
- // << imports.canonicalPath();
- // a path can be converted to/from strings
- QString pString = imports.canonicalPath().toString();
- Path importsPath = Path::fromString(pString);
- // and loaded again using the .path(somePath) method
- DomItem imports2 = env.path(importsPath);
- QCOMPARE(imports, imports2);
- // the canonical path is absolute, but you can have relative paths
- Path first = Path::Index(0);
- DomItem firstImport = imports.path(first);
- // an existing path can also be extended
- Path firstImportPath = importsPath.index(0);
- QCOMPARE(firstImportPath, firstImport.canonicalPath());
- // the normal elements of a path are index, key, field
- // Uppercase static method creates one, lowercase appends to an existing path.
- Path mainComponentPath = Path::Field(Fields::components).key("").index(0);
- DomItem mainComponent = qmlFile.path(mainComponentPath);
- // DomItems have the same methods to access their elements
- DomItem mainComponent2 = qmlFile.field(Fields::components).key("").index(0);
- // two other special ements are root (root element for absolute paths)
- Path topPath = Path::Root(PathRoot::Top);
- QCOMPARE(topPath, importsPath[0]);
- // the current element performs an operation (tipically a lookup or iteration) at the
- // current path location (not handled here)
- Path lookupPath = Path::Current(PathCurrent::Lookup);
-
- // there are various visit methods to iterate/visit DomItems in particular visitTree
- // which is quite flexible.
- // They normally use callbacks that can return false to stop the iteration.
- // Still often the DomKind specific for loop presentated later are clearer and more
- // convenient
- {
- QString s;
- QTextStream dbg(&s);
- imports.visitTree(
- Path(),
- [&dbg](Path p, const DomItem &el, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "*"
- << p.last().toString() << " " << domKindToString(el.domKind())
- << "(" << el.internalKindStr() << ")\n";
- // returning false here stops the whole iteration
- return true;
- },
- VisitOption::Default, // we want a recursive visit visiting also the top and
- // adopted
- [&dbg](Path p, const DomItem &, bool canonicalChild) {
- // returning false here skips that branch
- if (!canonicalChild) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+"
- << p.last().toString() << " (adopted, will not recurse)\n";
- } else if (p && p.headIndex(0) % 2 == 1) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "-"
- << p.last().toString() << " *recursive visit skipped*\n";
- return false; // we skip odd entries in lists;
- } else {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+"
- << p.last().toString() << "\n";
- }
- return true;
- },
- [&dbg](Path p, const DomItem &, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "="
- << p.last().toString() << "\n";
- return true;
- });
- dbg.flush();
- QCOMPARE(s,
- QStringLiteral("* List(List)\n"
- "+\n"
- " *[0] Object(Import)\n"
- " +[0]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.implicit Value(ConstantData)\n"
- " +.implicit\n"
- " =.implicit\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[0]\n"
- " *[1] Object(Import)\n"
- " -[1] *recursive visit skipped*\n"
- " *[2] Object(Import)\n"
- " +[2]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.implicit Value(ConstantData)\n"
- " +.implicit\n"
- " =.implicit\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[2]\n"
- " *[3] Object(Import)\n"
- " -[3] *recursive visit skipped*\n"
- " *[4] Object(Import)\n"
- " +[4]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[4]\n"
- "=\n"));
- }
-
- // ### DomKind
- // any DomItem belongs to 5 basic types
-
- // 1. Object (a C++ object)
- QCOMPARE(qmlFile.domKind(), DomKind::Object);
- // The underlying type of the c++ object can be found with .internalKind()
- QCOMPARE(qmlFile.internalKind(), DomType::QmlFile);
- // .initernalKindStr() is a convenience string version of it
- QCOMPARE(qmlFile.internalKindStr(), u"QmlFile");
- // the object attributes (fields) can be reached using .field(u"filedName")
- // normally one should not use a string, but the Fields:: constant
- DomItem qmlFile2 = tFile.field(Fields::currentItem);
- // all the available fields can be listed via fields()
- // qDebug() << "The" << qmlObj.internalKindStr() << "at" << qmlObj.canonicalPath()
- // << "has the following fields:" << qmlObj.fields();
- // we can access the underlying C++ object with as<>
- // if (const QmlFile *qmlFilePtr = qmlFile.as<QmlFile>())
- // qDebug() << "The QmlFile lives at the address" << qmlFilePtr;
- //// We can get the shared pointer of the owner type (which for the file is the QmlFile
- /// itself
- // if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
- // qDebug() << "QmlFile uses shared pointers as ownership method, the underlying
- // address "
- // "is the same"
- // << qmlFilePtr.get();
-
- // 2. a (Cbor-) Value, i.e a string, number,...
- DomItem fPath = qmlFile.field(Fields::canonicalFilePath);
- QCOMPARE(fPath.domKind(), DomKind::Value);
- // the Cbor representation of a value can be extracted with .value(), and in this case
- // we can then call toString
- // qDebug() << "The filePath DomItem is " << fPath << " and it still 'knows' its path "
- // << fPath.canonicalPath() << " but can have it also as value:" <<
- // fPath.value()
- // << "or even better as string." <<
- // fPath.value().toString(QLatin1String("*none*"));
- // a DomItem might have a valid value() even if it is not of type DomKind::Value, indeed
- // CBor maps and lists are mapped to DomKind::Map and DomKind::List, and can be
- // traversed thought that but also have a valid value().
-
- // 3. a list
- QCOMPARE(imports.domKind(), DomKind::List);
- // the number of elements can be sound with .indexes() and with .index(n) we access each
- // element
- // qDebug() << "We have " << imports.indexes() << " imports, and the first is "
- // << imports.index(0);
- // If we want to just loop on the elements .values() is the most convenient way
- // technically values *always* works even for objects and maps, iterating on the values
- // for (DomItem import : imports.values()) {
- // if (const Import *importPtr = import.as<Import>()) {
- // if (importPtr->implicit)
- // qDebug() << importPtr->uri << importPtr->version.stringValue();
- // }
- //}
-
- // 4. a map
- DomItem bindings = qmlObj.field(Fields::bindings);
- QCOMPARE(bindings.domKind(), DomKind::Map);
- // The keys of the map can be reached either with .keys() or .sortedKeys(), each element
- // with .key(k)
- // qDebug() << "bindings";
- // for (QString k : bindings.sortedKeys()) {
- // for (DomItem b : bindings.key(k).values()) {
- // qDebug() << k << ":" << b;
- // }
- //}
-
- // 5 The empty element
- DomItem empty;
- QCOMPARE(empty.domKind(), DomKind::Empty);
- // The empty element is the only DomItem that casted to bool returns false, so checking
- // for it can be just an implicit cast to bool
- QVERIFY(bindings && !empty);
- // the empty element supports all the previus operations so that one can traverse a non
- // existing path without checking at every element, but only check the result
- DomItem nonExisting = qmlFile.field(u"no-existing").key(u"a").index(0);
- QVERIFY(!nonExisting);
-
- // the index operator [] can be used instead of .index/.key/.field, it might be slightly
- // less efficient but works
-
- // find type
- // access type
-
- // ### write out
- // it is possible to write out a qmlFile (actually also parts of it), which will
- // automatically reformat it
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".qml"));
- DomItem newFile = qmlFile.writeOut(reformattedFilePath);
- // qDebug() << "reformatted written at " << reformattedFilePath;
-
- // ## Jumping around
- // ### Generic Methods
- // from a DomItem you do no have just deeper in the tree, you can also go up the
- // hierarch toward the root .container() just goes up one step in the canonicalPath of
- // the object
- QCOMPARE(imports, firstImport.container());
- // .containingObject() goes up to the containing DomKind::Object, skipping over all Maps
- // and Lists
- QCOMPARE(qmlFile, firstImport.containingObject());
- // .owner() returns the shared pointer based "owner" object, qmlFile and
- // ScriptExpression are owningItems
- QCOMPARE(qmlFile, bindings.owner());
- // .top() goes to the top of the tree, i.e the environment (or the universe)
- QCOMPARE(env, bindings.top());
- // environment is normally the same as top, but making sure it is a actually a
- // DomEnvironment
- QCOMPARE(env, bindings.environment());
- // the universe is a cache of loaded files which for each file keeps two versions: the
- // latest and the latest valid it can be reached with .universe(), from the universe you
- // cannot get back to the environment.
- QCOMPARE(env.universe().internalKind(), DomType::DomUniverse);
-
- // ## QML Oriented Methods
- // The Dom model is not for generic json-like structures, so there are methods tailored
- // for Qml and its structure The methods can succeed if there is a clearly defined
- // unique result. sometime there is an obivious, but not necessarily unique choice
- // (tipically going up the hierarchy), for example given a qml file the obvious choice
- // for a component is the root component, but the file might contain other inline
- // components, and for an object with different version exposed (C++ property
- // versioning) the latest version is the natural choice, but other might be available.
- // In these case passing GoTo::MostLikely as argument makes the method to this obivious
- // choice (or possibly even only choice if no other versions/components are actually
- // defined), instead of refusing any potentially ambiguous situation and returning the
- // empty element.
-
- // .fileObject() goes to the object representing the whole file
- // (from either the external object returned by load or from inside the file)
- DomItem fileObject = tFile.fileObject();
- DomItem fileObject2 = imports.fileObject();
- QCOMPARE(fileObject, fileObject2);
- QCOMPARE(fileObject.internalKind(), DomType::QmlFile);
- // .component() goes to the component object.
- QCOMPARE(qmlObj.component(), qmlFile.component(GoTo::MostLikely));
- // .pragmas gives access to the pragmas of the current component
- QCOMPARE(qmlFile.pragmas(), qmlFile.field(Fields::pragmas));
-
- // QmlObject
- // QmlObject if the main to represent the type information (methods, bindings,
- // properties,...) of qml. Please note that QmlObject -> component operation is
- // potentially lossy, when multiple version are exposed, so we represent a type through
- // its root object, not through a component.
-
- // .qmlObject() goes to the current QmlObject
- QCOMPARE(qmlObj, bindings.qmlObject());
-
- // Given the centrality of QmlObject several of its attributes have convenience methods
- // to access them:
-
- // .children() makes subObjects contained inside a QmlObject accessible
- // note that it is possible to add objects also by directly binding the children or data
- // attribute, those children are not listed here, this accesses only those listed inside
- // the QmlObject
- QCOMPARE(qmlObj.children(), qmlObj.field(Fields::children));
- DomItem subObj0 = qmlObj.children().index(0);
- // .child(<i>) is a shortcut for .children.index(<i>)
- QCOMPARE(subObj0, qmlObj.child(0));
- // rootQmlObject goes to the root qmlObject (unless one reaches an empty element)
- QVERIFY(bool(subObj0));
- QCOMPARE(subObj0.rootQmlObject(), qmlObj);
- // .bindings() returns the bindings defined in the current object
- QCOMPARE(bindings, qmlObj.bindings());
- DomItem mCompObj = qmlObj.child(0)
- .child(0)
- .bindings()
- .key(u"delegate")
- .index(0)
- .field(Fields::value)
- .child(1);
- // .methods() gives methods definitions and signals
- DomItem methods = mCompObj.methods();
- // qDebug() << "mCompObj methods:";
- for (QString methodName : methods.sortedKeys()) {
- for (DomItem method : methods.key(methodName).values()) {
- if (const MethodInfo *methodPtr = method.as<MethodInfo>()) {
- QCOMPARE(methodName, methodPtr->name);
- // qDebug() << " " << methodPtr->name << methodPtr->methodType;
- }
- }
- }
- // qDebug() << "mCompObj propertyDefs:";
- // .propertyDefs() returns the properties defined in the current object
- DomItem pDefs = mCompObj.propertyDefs();
- for (QString pDefName : pDefs.sortedKeys()) {
- for (DomItem pDef : pDefs.key(pDefName).values()) {
- if (const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>()) {
- QCOMPARE(pDefName, pDefPtr->name);
- // qDebug() << " " << pDefPtr->name << pDefPtr->typeName;
- }
- }
- }
- // binding and property definitions are about the ones defined in the current object
- // often one is interested also to the inherited properties.
- // Here PropertyInfo helps, it list all the definitions and bindings for a given
- // property in the inheritance order (local definitions, parent definitions, parent
- // parent definitions,...) .propertyInfos() gives access in the usual way (through a
- // DomItem)
- DomItem propertyInfos = mCompObj.propertyInfos();
- // .propertyInfoWithName(<name>) directly accesses one
- PropertyInfo pInfo = mCompObj.propertyInfoWithName(QStringLiteral(u"a"));
- // qDebug() << "bindings" << pInfo.bindings;
- // .propertyInfoNames() gives the names of the properties
- QCOMPARE(propertyInfos.keys(), mCompObj.propertyInfoNames());
-
- // .globalScope() goes to the globa scope object
- QCOMPARE(qmlObj.globalScope().internalKind(), DomType::GlobalScope);
- // and scope to the containing scope
- QCOMPARE(bindings.scope(), qmlObj);
- }
- // mutate & edit
- {
- // DomItem handles read-only access, but if one wants to change something it cannot be
- // used. MutableDomItem can be initialized with a DomItem, and provides also the methods
- // to modify the item. It keeps the OwningItem and the path to the current item.
- // Mutability can invalidate pointers to non owning items (and thus DomItem).
- // For this reason one should not modify something that other code can have a DomItem
- // pointer to, the best practice is to make shared object immutable and never change
- // them. One should modify only a copy that is used only by a single thread, and do not
- // shared untils all modifications are done. A MutableItem stays valid (or becomes
- // Empty), but stays safe to use
- //
- // Assuming one guarantees that editing is ok, doing it in practice is just about using
- // MutableDomItem instead of DomItem
- // It is possible to simply initialize a mutable item with a DomItem
- DomItem origFile = tFile.fileObject();
- MutableDomItem myFile0(origFile);
- // Normally it is better to have a separate environment. Is possible to avoid re-reading
- // the files already read by sharing the Universe between two environments.
- // But normally it is better and just as safe to work on a copy, so that one can be sure
- // that no DomItem is kept by other code gets invalidated. The .makeCopy creates a deep
- // copy, and by default (DomItem::CopyOption::EnvConnected) creates an environment which
- // to takes all non local elements from the current environment (its parent environment)
- // but replaces the file object with the copy. When finished one can replace the file
- // object of the parent with the new one using .commitToBase().
- MutableDomItem myFile = origFile.makeCopy();
- QVERIFY(myFile.ownerAs<QmlFile>()
- && myFile.ownerAs<QmlFile>() != myFile0.ownerAs<QmlFile>());
- QVERIFY(myFile.environment().ownerAs<DomEnvironment>()
- && myFile.environment().ownerAs<DomEnvironment>()
- != myFile0.environment().ownerAs<DomEnvironment>());
- // we can check that the two files are really identical (.item() give back the DomItem
- // of a MutableDomItem
- Q_ASSERT(domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // MutableDomItem has the same methods as DomItem
- MutableDomItem qmlObj = myFile.qmlObject(GoTo::MostLikely);
- MutableDomItem qmlObj2 = myFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
- QVERIFY(bool(qmlObj));
- QCOMPARE(qmlObj, qmlObj2);
- // qDebug() << "mutable qmlObj has canonicalPath " << qmlObj.canonicalPath();
- // but it adds methods to add
- // * new PropertyDefinitions
- PropertyDefinition b;
- b.name = QLatin1String("xx");
- b.typeName = QLatin1String("int");
- // if we make t true we also have to give a value...
- MutableDomItem addedPDef = qmlObj.addPropertyDef(b);
- // qDebug() << "added property definition at:" << addedPDef.pathFromOwner();
- // * new bindings
- MutableDomItem addedBinding0 = qmlObj.addBinding(
- Binding("height",
- std::shared_ptr<ScriptExpression>(new ScriptExpression(
- QStringLiteral(u"243"),
- ScriptExpression::ExpressionType::BindingExpression))));
- // by default addBinding, addPropertyDef and addMethod have the AddOption::Override
- // to make it more difficult to create invalid documents, so that only the
- // following binding remains (where we use the convenience constructor that constucts
- // the ScriptExpression internally
- MutableDomItem addedBinding =
- qmlObj.addBinding(Binding("height", QStringLiteral(u"242")));
- // qDebug() << "added binding at:" << addedBinding.pathFromOwner();
- // * new methods
- MethodInfo mInfo;
- mInfo.name = QLatin1String("foo2");
- MethodParameter param;
- param.name = QLatin1String("x");
- mInfo.parameters.append(param);
- mInfo.setCode(QLatin1String("return 4*10+2 - x"));
- // we can change the added binding
- addedBinding.setCode(QLatin1String("245"));
- MutableDomItem addedMethod = qmlObj.addMethod(mInfo);
- // qDebug() << "added method at:" << addedMethod.pathFromOwner();
- // * new QmlObjects
- QmlObject subObj;
- subObj.setName(QLatin1String("Item"));
- MutableDomItem addedSubObj = qmlObj.addChild(subObj);
- // qDebug() << "added subObject at:" << addedMethod.pathFromOwner();
- // It is possible to modify the content of objects, using the mutableAs method
- if (PropertyDefinition *addedPDefPtr = addedPDef.mutableAs<PropertyDefinition>()) {
- addedPDefPtr->isRequired = true;
- }
- MutableDomItem firstChild = qmlObj.child(0);
- // qDebug() << "firstChild:" << firstChild;
- // It is possible remove objects
- if (QmlObject *qmlObjPtr = qmlObj.mutableAs<QmlObject>()) {
- QList<QmlObject> children = qmlObjPtr->children();
- children.removeAt(0);
- qmlObjPtr->setChildren(children);
- }
- // But as MutableDomItem does not keep the identity, just the same position, the
- // addedSubObj becomes invalid (and firstChild changes)
- // qDebug() << "after removal firstChild:" << firstChild;
- // qDebug() << "addedSubObj becomes invalid:" << addedSubObj;
- // qDebug() << "But the last object is the added one:"
- // << qmlObj.child(qmlObj.children().indexes() - 1);
-
- // now origFile are different
- Q_ASSERT(!domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // and we can look at the places where they differ
- // qDebug().noquote().nospace()
- // << "Edits introduced the following diffs (ignoring file locations"
- // << " and thus whitespace/reformatting changes):\n"
- // << domCompareStrList(origFile, myFile, FieldFilter::noLocationFilter(),
- // DomCompareStrList::AllDiffs)
- // .join(QString());
-
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".qml"));
- Q_ASSERT(myFile.as<QmlFile>()
- && myFile.as<QmlFile>() == myFile.fileObject().as<QmlFile>());
- MutableDomItem reformattedEditedFile = myFile.writeOut(reformattedFilePath);
- // the reformatted edited file might be different from the edited file
- // but the differences are just in file location/formatting
- Q_ASSERT(domCompareStrList(myFile, reformattedEditedFile,
- FieldFilter::noLocationFilter())
- .isEmpty());
-
- // qDebug() << "The edited file was written at " << reformattedFilePath;
- QString dumpFilePath = QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited0")
- + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- myFile.dump(dumpFilePath);
- // qDebug() << "The non reformatted edited file was dumped at " << dumpFilePath;
- QString reformattedDumpFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- reformattedEditedFile.dump(reformattedDumpFilePath);
- // qDebug() << "The edited file was dumped at " << reformattedDumpFilePath;
- // The top environment still contains the original loaded file
- Q_ASSERT(origFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>());
- QCOMPARE(tFile.fileObject().refreshed().ownerAs<QmlFile>(),
- origFile.ownerAs<QmlFile>());
- QCOMPARE(tFile.fileObject().ownerAs<QmlFile>(), origFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- != reformattedEditedFile.ownerAs<QmlFile>());
- // we can commit the reformatted file
- if (!reformattedEditedFile.commitToBase()) {
- qWarning() << "No reformatted file to commit";
- }
- // myFile might not be the same (If and updated check is requested, not the case here)
- if (myFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>()
- && !myFile.commitToBase()) {
- qWarning() << "Could not commit edited file";
- }
- // but refreshing it (looking up its canonical path) we always find the updated file
- QCOMPARE(myFile.refreshed().ownerAs<QmlFile>(),
- reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- == reformattedEditedFile.ownerAs<QmlFile>());
- }
- }
-};
-
-} // Dom
-} // QQmlJS
-QT_END_NAMESPACE