2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2019-12-13 15:10:46 +00:00
|
|
|
|
|
|
|
#include <QtTest/QtTest>
|
2020-03-18 08:19:31 +00:00
|
|
|
#include <QDir>
|
|
|
|
#include <QFile>
|
2019-12-13 15:10:46 +00:00
|
|
|
#include <QProcess>
|
|
|
|
#include <QString>
|
2020-03-18 08:19:31 +00:00
|
|
|
#include <QTemporaryDir>
|
2021-04-19 14:43:21 +00:00
|
|
|
#include <QtTest/private/qemulationdetector_p.h>
|
2021-08-06 10:27:35 +00:00
|
|
|
#include <QtQuickTestUtils/private/qmlutils_p.h>
|
2021-09-15 07:38:49 +00:00
|
|
|
#include <QtQmlDom/private/qqmldomitem_p.h>
|
|
|
|
#include <QtQmlDom/private/qqmldomlinewriter_p.h>
|
|
|
|
#include <QtQmlDom/private/qqmldomoutwriter_p.h>
|
|
|
|
#include <QtQmlDom/private/qqmldomtop_p.h>
|
|
|
|
|
|
|
|
using namespace QQmlJS::Dom;
|
2019-12-13 15:10:46 +00:00
|
|
|
|
|
|
|
class TestQmlformat: public QQmlDataTest
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
public:
|
2021-11-03 14:39:00 +00:00
|
|
|
enum class RunOption { OnCopy, OrigToCopy };
|
2021-08-06 10:27:35 +00:00
|
|
|
TestQmlformat();
|
|
|
|
|
2019-12-13 15:10:46 +00:00
|
|
|
private Q_SLOTS:
|
|
|
|
void initTestCase() override;
|
|
|
|
|
|
|
|
void testFormat();
|
2020-09-02 07:03:24 +00:00
|
|
|
void testFormat_data();
|
2020-06-17 12:06:11 +00:00
|
|
|
|
2020-09-02 07:03:24 +00:00
|
|
|
void testLineEndings();
|
2020-01-15 10:25:18 +00:00
|
|
|
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
|
|
|
|
void testExample();
|
|
|
|
void testExample_data();
|
2021-09-15 08:35:16 +00:00
|
|
|
void normalizeExample();
|
|
|
|
void normalizeExample_data();
|
2020-01-15 10:25:18 +00:00
|
|
|
#endif
|
|
|
|
|
2022-10-19 10:50:31 +00:00
|
|
|
void testBackupFileLimit();
|
|
|
|
|
2023-10-13 11:30:27 +00:00
|
|
|
void testFilesOption_data();
|
|
|
|
void testFilesOption();
|
2019-12-13 15:10:46 +00:00
|
|
|
private:
|
|
|
|
QString readTestFile(const QString &path);
|
2021-11-03 14:39:00 +00:00
|
|
|
QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true,
|
|
|
|
RunOption rOption = RunOption::OnCopy);
|
2021-09-15 07:38:49 +00:00
|
|
|
QString formatInMemory(const QString &fileToFormat, bool *didSucceed = nullptr,
|
|
|
|
LineWriterOptions options = LineWriterOptions(),
|
|
|
|
WriteOutChecks extraChecks = WriteOutCheck::ReparseCompare,
|
|
|
|
WriteOutChecks largeChecks = WriteOutCheck::None);
|
2019-12-13 15:10:46 +00:00
|
|
|
|
|
|
|
QString m_qmlformatPath;
|
2020-01-15 10:25:18 +00:00
|
|
|
QStringList m_excludedDirs;
|
|
|
|
QStringList m_invalidFiles;
|
2023-03-07 14:49:30 +00:00
|
|
|
QStringList m_ignoreFiles;
|
2020-01-15 10:25:18 +00:00
|
|
|
|
|
|
|
QStringList findFiles(const QDir &);
|
|
|
|
bool isInvalidFile(const QFileInfo &fileName) const;
|
2023-03-07 14:49:30 +00:00
|
|
|
bool isIgnoredFile(const QFileInfo &fileName) const;
|
2019-12-13 15:10:46 +00:00
|
|
|
};
|
|
|
|
|
2022-06-07 02:23:31 +00:00
|
|
|
// Don't fail on warnings because we read a lot of QML files that might intentionally be malformed.
|
2021-08-06 10:27:35 +00:00
|
|
|
TestQmlformat::TestQmlformat()
|
2022-06-07 02:23:31 +00:00
|
|
|
: QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
|
2021-08-06 10:27:35 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-12-13 15:10:46 +00:00
|
|
|
void TestQmlformat::initTestCase()
|
|
|
|
{
|
|
|
|
QQmlDataTest::initTestCase();
|
2020-09-16 08:34:53 +00:00
|
|
|
m_qmlformatPath = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QLatin1String("/qmlformat");
|
2019-12-13 15:10:46 +00:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
m_qmlformatPath += QLatin1String(".exe");
|
|
|
|
#endif
|
|
|
|
if (!QFileInfo(m_qmlformatPath).exists()) {
|
|
|
|
QString message = QStringLiteral("qmlformat executable not found (looked for %0)").arg(m_qmlformatPath);
|
|
|
|
QFAIL(qPrintable(message));
|
|
|
|
}
|
2020-01-15 10:25:18 +00:00
|
|
|
|
|
|
|
// Add directories you want excluded here
|
|
|
|
|
|
|
|
// These snippets are not expected to run on their own.
|
|
|
|
m_excludedDirs << "doc/src/snippets/qml/visualdatamodel_rootindex";
|
|
|
|
m_excludedDirs << "doc/src/snippets/qml/qtbinding";
|
|
|
|
m_excludedDirs << "doc/src/snippets/qml/imports";
|
|
|
|
m_excludedDirs << "doc/src/snippets/qtquick1/visualdatamodel_rootindex";
|
|
|
|
m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding";
|
|
|
|
m_excludedDirs << "doc/src/snippets/qtquick1/imports";
|
|
|
|
m_excludedDirs << "tests/manual/v4";
|
2023-05-23 13:02:50 +00:00
|
|
|
m_excludedDirs << "tests/manual/qmllsformatter";
|
2020-01-15 10:25:18 +00:00
|
|
|
m_excludedDirs << "tests/auto/qml/ecmascripttests";
|
|
|
|
m_excludedDirs << "tests/auto/qml/qmllint";
|
|
|
|
|
|
|
|
// Add invalid files (i.e. files with syntax errors)
|
|
|
|
m_invalidFiles << "tests/auto/quick/qquickloader/data/InvalidSourceComponent.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.2.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.3.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.5.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/property.4.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/empty.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/missingObject.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/insertedSemicolon.1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nonexistantProperty.5.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidRoot.1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml";
|
2021-09-15 07:38:49 +00:00
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidID.4.qml";
|
2021-04-09 15:42:06 +00:00
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/questionDotEOF.qml";
|
2020-01-15 10:25:18 +00:00
|
|
|
m_invalidFiles << "tests/auto/qml/qquickfolderlistmodel/data/dummy.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
|
2020-03-19 12:49:17 +00:00
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
|
2020-01-15 10:25:18 +00:00
|
|
|
m_invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml";
|
qmlls: autocomplete scriptexpressions
Add some support for autocompletion in script expressions.
Add missing getter in QQmlJSScope to be able to list js identifiers of a
semantic scope in qmlls.
Add static helper methods XXXCompletion that collect some information
JavaScript identifiers, methods and properties made available from a
QQmlJSScope. Another helper method collectFromAllJavaScriptParents
can collect the results of a XXXCompletion method through all
JavaScript scopes of a DomItem.
Avoid code duplication and remove the implementation searching for
methods in the DOM: instead, use the already existing implementation in
QQmlJSScope::methods.
Finally, add the method scriptIdentifierCompletion() that computes all
autocompletions for a scriptIdentifier, that is called in
QQmlLSUtils::completions and in the tests.
Fix some tests to flag property as properties, inside of "Fields", and
add extra tests to see if the newly implemented completions work.
Cleanup QQmlLSUtils::completions() by extracting its code into static
helper methods reachableTypes() and reachableMethods(), replace two
enums by one QFlag as both enums were always used together.
Extend reachableTypes() to find also non-object types, like the value
types int, date, for example. This is later used for property type or
function parameter type completion, for example.
Make QQmlLSUtils::completions() more readable by returning early and
by separating the completion by QML language constructs, instead of
grouping multiple unrelated constructs together. This became possible
thanks to the new static helpers mentioned above.
Suppress completion inside of function parameter definitions and inside
of id definitions.
Add some tests for property type completion and pragma completion with
QEXPECT_FAIL, those features will be implemented in a later commit.
Add 'import' completion for empty files + a test that test completions
on an empty file.
Fix tests for colon-checking: some completions insert
'<propertyName>: ' for properties, e.g. for bindings, and some do not,
e.g. for property usage in JS expressions.
Also fix the test in tst_qmlls_modules to expect methods instead of
functions.
Add exception in tst_qmlformat for the empty qml file used to test if
completions work in a completely empty file.
Task-number: QTBUG-116899
Change-Id: I63de62c71d63aa4ab62ca6d83c6be157f4e6f96c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2023-09-11 13:50:26 +00:00
|
|
|
m_invalidFiles << "tests/auto/qmlls/utils/data/emptyFile.qml";
|
2020-01-17 13:28:09 +00:00
|
|
|
|
2021-09-15 07:38:49 +00:00
|
|
|
// Files that get changed:
|
|
|
|
// rewrite of import "bla/bla/.." to import "bla"
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.4.qml";
|
|
|
|
// block -> object in internal update
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml";
|
|
|
|
// removal of unsupported indexing of Object declaration
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/hangOnWarning.qml";
|
|
|
|
// removal of duplicated id
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/component.3.qml";
|
|
|
|
// Optional chains are not permitted on the left-hand-side in assignments
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmllanguage/data/optionalChaining.LHS.qml";
|
|
|
|
// object literal with = assignements
|
2022-11-18 07:15:16 +00:00
|
|
|
m_invalidFiles << "tests/auto/quickcontrols/controls/data/tst_scrollbar.qml";
|
2021-09-15 07:38:49 +00:00
|
|
|
|
2020-01-17 13:28:09 +00:00
|
|
|
// These files rely on exact formatting
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml";
|
2023-03-07 14:49:30 +00:00
|
|
|
|
|
|
|
// These files are too big
|
2023-05-25 09:54:52 +00:00
|
|
|
m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/longQmlFile.qml";
|
|
|
|
m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/deeplyNested.qml";
|
2020-01-15 10:25:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QStringList TestQmlformat::findFiles(const QDir &d)
|
|
|
|
{
|
2022-10-05 05:29:16 +00:00
|
|
|
for (int ii = 0; ii < m_excludedDirs.size(); ++ii) {
|
2020-01-15 10:25:18 +00:00
|
|
|
QString s = m_excludedDirs.at(ii);
|
|
|
|
if (d.absolutePath().endsWith(s))
|
|
|
|
return QStringList();
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList rv;
|
|
|
|
|
2023-03-02 13:20:12 +00:00
|
|
|
const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
|
|
|
|
QDir::Files);
|
|
|
|
for (const QString &file: files) {
|
2023-03-07 14:49:30 +00:00
|
|
|
QString absoluteFilePath = d.absoluteFilePath(file);
|
|
|
|
if (!isIgnoredFile(QFileInfo(absoluteFilePath)))
|
|
|
|
rv << absoluteFilePath;
|
2020-01-15 10:25:18 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 13:20:12 +00:00
|
|
|
const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
|
|
|
|
QDir::NoSymLinks);
|
|
|
|
for (const QString &dir: dirs) {
|
2020-01-15 10:25:18 +00:00
|
|
|
QDir sub = d;
|
|
|
|
sub.cd(dir);
|
|
|
|
rv << findFiles(sub);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TestQmlformat::isInvalidFile(const QFileInfo &fileName) const
|
|
|
|
{
|
|
|
|
for (const QString &invalidFile : m_invalidFiles) {
|
|
|
|
if (fileName.absoluteFilePath().endsWith(invalidFile))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2019-12-13 15:10:46 +00:00
|
|
|
}
|
|
|
|
|
2023-03-07 14:49:30 +00:00
|
|
|
bool TestQmlformat::isIgnoredFile(const QFileInfo &fileName) const
|
|
|
|
{
|
|
|
|
for (const QString &file : m_ignoreFiles) {
|
|
|
|
if (fileName.absoluteFilePath().endsWith(file))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-13 15:10:46 +00:00
|
|
|
QString TestQmlformat::readTestFile(const QString &path)
|
|
|
|
{
|
|
|
|
QFile file(testFile(path));
|
|
|
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
|
|
return "";
|
|
|
|
|
|
|
|
return QString::fromUtf8(file.readAll());
|
|
|
|
}
|
|
|
|
|
2020-03-18 08:19:31 +00:00
|
|
|
void TestQmlformat::testLineEndings()
|
|
|
|
{
|
|
|
|
// macos
|
2020-09-02 10:34:29 +00:00
|
|
|
const QString macosContents =
|
|
|
|
runQmlformat(testFile("Example1.formatted.qml"), { "-l", "macos" });
|
2020-03-18 08:19:31 +00:00
|
|
|
QVERIFY(!macosContents.contains("\n"));
|
|
|
|
QVERIFY(macosContents.contains("\r"));
|
|
|
|
|
|
|
|
// windows
|
2020-09-02 10:34:29 +00:00
|
|
|
const QString windowsContents =
|
|
|
|
runQmlformat(testFile("Example1.formatted.qml"), { "-l", "windows" });
|
2020-03-18 08:19:31 +00:00
|
|
|
QVERIFY(windowsContents.contains("\r\n"));
|
|
|
|
|
|
|
|
// unix
|
2020-09-02 10:34:29 +00:00
|
|
|
const QString unixContents = runQmlformat(testFile("Example1.formatted.qml"), { "-l", "unix" });
|
2020-03-18 08:19:31 +00:00
|
|
|
QVERIFY(unixContents.contains("\n"));
|
|
|
|
QVERIFY(!unixContents.contains("\r"));
|
|
|
|
}
|
|
|
|
|
2020-09-02 07:03:24 +00:00
|
|
|
void TestQmlformat::testFormat_data()
|
2020-06-12 13:24:25 +00:00
|
|
|
{
|
2020-09-02 07:03:24 +00:00
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
QTest::addColumn<QString>("fileFormatted");
|
2020-09-02 10:34:29 +00:00
|
|
|
QTest::addColumn<QStringList>("args");
|
2021-11-03 14:39:00 +00:00
|
|
|
QTest::addColumn<RunOption>("runOption");
|
2020-09-02 07:03:24 +00:00
|
|
|
|
2021-11-03 14:39:00 +00:00
|
|
|
QTest::newRow("example1") << "Example1.qml"
|
|
|
|
<< "Example1.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("example1 (tabs)")
|
2020-09-02 10:34:29 +00:00
|
|
|
<< "Example1.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "Example1.formatted.tabs.qml" << QStringList { "-t" } << RunOption::OnCopy;
|
2020-09-02 10:34:29 +00:00
|
|
|
QTest::newRow("example1 (two spaces)")
|
|
|
|
<< "Example1.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "Example1.formatted.2spaces.qml" << QStringList { "-w", "2" } << RunOption::OnCopy;
|
|
|
|
QTest::newRow("annotation") << "Annotations.qml"
|
|
|
|
<< "Annotations.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
2020-09-02 07:03:24 +00:00
|
|
|
QTest::newRow("front inline") << "FrontInline.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "FrontInline.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
2020-09-02 07:03:24 +00:00
|
|
|
QTest::newRow("if blocks") << "IfBlocks.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "IfBlocks.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("read-only properties")
|
|
|
|
<< "readOnlyProps.qml"
|
|
|
|
<< "readOnlyProps.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2020-09-02 07:03:24 +00:00
|
|
|
QTest::newRow("states and transitions")
|
|
|
|
<< "statesAndTransitions.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "statesAndTransitions.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("large bindings")
|
|
|
|
<< "largeBindings.qml"
|
|
|
|
<< "largeBindings.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("verbatim strings")
|
|
|
|
<< "verbatimString.qml"
|
|
|
|
<< "verbatimString.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("inline components")
|
|
|
|
<< "inlineComponents.qml"
|
|
|
|
<< "inlineComponents.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2020-09-02 07:03:24 +00:00
|
|
|
QTest::newRow("nested ifs") << "nestedIf.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "nestedIf.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2020-09-02 07:03:24 +00:00
|
|
|
QTest::newRow("QTBUG-85003") << "QtBug85003.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "QtBug85003.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
|
|
|
QTest::newRow("nested functions")
|
|
|
|
<< "nestedFunctions.qml"
|
|
|
|
<< "nestedFunctions.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("multiline comments")
|
|
|
|
<< "multilineComment.qml"
|
|
|
|
<< "multilineComment.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2020-10-01 07:41:16 +00:00
|
|
|
QTest::newRow("for of") << "forOf.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "forOf.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("property names")
|
|
|
|
<< "propertyNames.qml"
|
|
|
|
<< "propertyNames.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2020-10-07 09:23:33 +00:00
|
|
|
QTest::newRow("empty object") << "emptyObject.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "emptyObject.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
|
|
|
QTest::newRow("arrow functions")
|
|
|
|
<< "arrowFunctions.qml"
|
|
|
|
<< "arrowFunctions.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2021-10-25 16:30:26 +00:00
|
|
|
QTest::newRow("settings") << "settings/Example1.qml"
|
2021-11-03 14:39:00 +00:00
|
|
|
<< "settings/Example1.formatted_mac_cr.qml" << QStringList {}
|
|
|
|
<< RunOption::OrigToCopy;
|
2022-08-05 08:37:24 +00:00
|
|
|
QTest::newRow("forWithLet")
|
|
|
|
<< "forWithLet.qml"
|
|
|
|
<< "forWithLet.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2022-11-06 20:40:50 +00:00
|
|
|
|
|
|
|
QTest::newRow("objects spacing (no changes)")
|
|
|
|
<< "objectsSpacing.qml"
|
|
|
|
<< "objectsSpacing.formatted.qml" << QStringList { "--objects-spacing" } << RunOption::OnCopy;
|
|
|
|
|
|
|
|
QTest::newRow("normalize + objects spacing")
|
|
|
|
<< "normalizedObjectsSpacing.qml"
|
|
|
|
<< "normalizedObjectsSpacing.formatted.qml" << QStringList { "-n", "--objects-spacing" } << RunOption::OnCopy;
|
2022-11-06 19:42:07 +00:00
|
|
|
|
|
|
|
QTest::newRow("ids new lines")
|
|
|
|
<< "checkIdsNewline.qml"
|
|
|
|
<< "checkIdsNewline.formatted.qml" << QStringList { "-n" } << RunOption::OnCopy;
|
2022-11-23 18:33:40 +00:00
|
|
|
|
|
|
|
QTest::newRow("functions spacing (no changes)")
|
|
|
|
<< "functionsSpacing.qml"
|
|
|
|
<< "functionsSpacing.formatted.qml" << QStringList { "--functions-spacing" } << RunOption::OnCopy;
|
|
|
|
|
|
|
|
QTest::newRow("normalize + functions spacing")
|
|
|
|
<< "normalizedFunctionsSpacing.qml"
|
|
|
|
<< "normalizedFunctionsSpacing.formatted.qml" << QStringList { "-n", "--functions-spacing" } << RunOption::OnCopy;
|
2023-01-18 14:36:23 +00:00
|
|
|
QTest::newRow("dontRemoveComments")
|
|
|
|
<< "dontRemoveComments.qml"
|
|
|
|
<< "dontRemoveComments.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
2023-01-25 13:11:18 +00:00
|
|
|
QTest::newRow("ecmaScriptClassInQml")
|
|
|
|
<< "ecmaScriptClassInQml.qml"
|
|
|
|
<< "ecmaScriptClassInQml.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2023-03-02 12:46:54 +00:00
|
|
|
QTest::newRow("arrowFunctionWithBinding")
|
|
|
|
<< "arrowFunctionWithBinding.qml"
|
|
|
|
<< "arrowFunctionWithBinding.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2023-04-02 21:44:50 +00:00
|
|
|
QTest::newRow("blanklinesAfterComment")
|
|
|
|
<< "blanklinesAfterComment.qml"
|
|
|
|
<< "blanklinesAfterComment.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2023-06-14 21:20:37 +00:00
|
|
|
QTest::newRow("pragmaValueList")
|
|
|
|
<< "pragma.qml"
|
|
|
|
<< "pragma.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2023-07-31 13:12:01 +00:00
|
|
|
QTest::newRow("objectDestructuring")
|
|
|
|
<< "objectDestructuring.qml"
|
|
|
|
<< "objectDestructuring.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2023-08-30 12:50:11 +00:00
|
|
|
QTest::newRow("destructuringFunctionParameter")
|
|
|
|
<< "destructuringFunctionParameter.qml"
|
|
|
|
<< "destructuringFunctionParameter.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2023-08-31 09:38:00 +00:00
|
|
|
QTest::newRow("ellipsisFunctionArgument")
|
|
|
|
<< "ellipsisFunctionArgument.qml"
|
|
|
|
<< "ellipsisFunctionArgument.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
2020-06-12 13:24:25 +00:00
|
|
|
}
|
|
|
|
|
2020-09-02 07:03:24 +00:00
|
|
|
void TestQmlformat::testFormat()
|
2020-06-18 12:36:37 +00:00
|
|
|
{
|
2020-09-02 07:03:24 +00:00
|
|
|
QFETCH(QString, file);
|
|
|
|
QFETCH(QString, fileFormatted);
|
2020-09-02 10:34:29 +00:00
|
|
|
QFETCH(QStringList, args);
|
2021-11-03 14:39:00 +00:00
|
|
|
QFETCH(RunOption, runOption);
|
2020-09-02 07:03:24 +00:00
|
|
|
|
2021-11-03 14:39:00 +00:00
|
|
|
QCOMPARE(runQmlformat(testFile(file), args, true, runOption), readTestFile(fileFormatted));
|
2020-06-18 12:36:37 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 10:25:18 +00:00
|
|
|
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
|
|
|
|
void TestQmlformat::testExample_data()
|
|
|
|
{
|
2021-04-19 14:43:21 +00:00
|
|
|
if (QTestPrivate::isRunningArmOnX86())
|
|
|
|
QSKIP("Crashes in QEMU. (timeout)");
|
2020-01-15 10:25:18 +00:00
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
|
|
|
|
QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
|
|
|
|
QString tests = QLatin1String(SRCDIR) + "/../../../../tests/";
|
|
|
|
|
|
|
|
QStringList files;
|
|
|
|
files << findFiles(QDir(examples));
|
|
|
|
files << findFiles(QDir(tests));
|
|
|
|
|
|
|
|
for (const QString &file : files)
|
|
|
|
QTest::newRow(qPrintable(file)) << file;
|
|
|
|
}
|
2021-09-15 08:35:16 +00:00
|
|
|
|
|
|
|
void TestQmlformat::normalizeExample_data()
|
|
|
|
{
|
|
|
|
if (QTestPrivate::isRunningArmOnX86())
|
|
|
|
QSKIP("Crashes in QEMU. (timeout)");
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
|
|
|
|
QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
|
|
|
|
QString tests = QLatin1String(SRCDIR) + "/../../../../tests/";
|
|
|
|
|
|
|
|
// normalizeExample is similar to testExample, so we test it only on nExamples + nTests
|
|
|
|
// files to avoid making too many
|
|
|
|
QStringList files;
|
|
|
|
const int nExamples = 10;
|
|
|
|
int i = 0;
|
|
|
|
for (const auto &f : findFiles(QDir(examples))) {
|
|
|
|
files << f;
|
|
|
|
if (++i == nExamples)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const int nTests = 10;
|
|
|
|
i = 0;
|
|
|
|
for (const auto &f : findFiles(QDir(tests))) {
|
|
|
|
files << f;
|
|
|
|
if (++i == nTests)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const QString &file : files)
|
|
|
|
QTest::newRow(qPrintable(file)) << file;
|
|
|
|
}
|
2020-01-15 10:25:18 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
|
|
|
|
void TestQmlformat::testExample()
|
|
|
|
{
|
|
|
|
QFETCH(QString, file);
|
2020-11-14 13:30:42 +00:00
|
|
|
const bool isInvalid = isInvalidFile(QFileInfo(file));
|
2021-09-15 07:38:49 +00:00
|
|
|
bool wasSuccessful;
|
|
|
|
LineWriterOptions opts;
|
|
|
|
opts.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
|
|
|
|
QString output = formatInMemory(file, &wasSuccessful, opts);
|
2020-01-15 10:25:18 +00:00
|
|
|
|
2020-11-14 13:30:42 +00:00
|
|
|
if (!isInvalid)
|
2021-09-15 07:38:49 +00:00
|
|
|
QVERIFY(wasSuccessful && !output.isEmpty());
|
2020-01-15 10:25:18 +00:00
|
|
|
}
|
2021-09-15 08:35:16 +00:00
|
|
|
|
|
|
|
void TestQmlformat::normalizeExample()
|
|
|
|
{
|
|
|
|
QFETCH(QString, file);
|
|
|
|
const bool isInvalid = isInvalidFile(QFileInfo(file));
|
|
|
|
bool wasSuccessful;
|
|
|
|
LineWriterOptions opts;
|
|
|
|
opts.attributesSequence = LineWriterOptions::AttributesSequence::Normalize;
|
|
|
|
QString output = formatInMemory(file, &wasSuccessful, opts);
|
|
|
|
|
|
|
|
if (!isInvalid)
|
|
|
|
QVERIFY(wasSuccessful && !output.isEmpty());
|
|
|
|
}
|
2020-01-15 10:25:18 +00:00
|
|
|
#endif
|
|
|
|
|
2022-10-19 10:50:31 +00:00
|
|
|
void TestQmlformat::testBackupFileLimit()
|
|
|
|
{
|
|
|
|
// Create a temporary directory
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
|
|
|
|
// Unformatted file to format
|
|
|
|
const QString fileToFormat{ testFile("Annotations.qml") };
|
|
|
|
|
|
|
|
{
|
|
|
|
const QString tempFile = tempDir.path() + QDir::separator() + "test_0.qml";
|
|
|
|
const QString backupFile = tempFile + QStringLiteral("~");
|
|
|
|
QFile::copy(fileToFormat, tempFile);
|
|
|
|
|
|
|
|
QProcess process;
|
|
|
|
process.start(m_qmlformatPath, QStringList{ "--verbose", "--inplace", tempFile });
|
|
|
|
QVERIFY(process.waitForFinished());
|
|
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
|
|
|
QCOMPARE(process.exitCode(), 0);
|
|
|
|
QVERIFY(QFileInfo::exists(tempFile));
|
|
|
|
QVERIFY(!QFileInfo::exists(backupFile));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-13 11:30:27 +00:00
|
|
|
void TestQmlformat::testFilesOption_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("containerFile");
|
|
|
|
QTest::addColumn<QStringList>("individualFiles");
|
|
|
|
|
|
|
|
QTest::newRow("initial") << "fileListToFormat"
|
|
|
|
<< QStringList{"valid1.qml", "invalidEntry:cannot be parsed", "valid2.qml"};
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestQmlformat::testFilesOption()
|
|
|
|
{
|
|
|
|
QFETCH(QString, containerFile);
|
|
|
|
QFETCH(QStringList, individualFiles);
|
|
|
|
|
|
|
|
// Create a temporary directory
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
tempDir.setAutoRemove(false);
|
|
|
|
QStringList actualFormattedFilesPath;
|
|
|
|
|
|
|
|
// Iterate through files in the source directory and copy them to the temporary directory
|
|
|
|
const auto sourceDir = dataDirectory() + QDir::separator() + "filesOption";
|
|
|
|
|
|
|
|
// Create a file that contains the list of files to be formatted
|
|
|
|
const QString tempFilePath = tempDir.path() + QDir::separator() + containerFile;
|
|
|
|
QFile container(tempFilePath);
|
|
|
|
if (container.open(QIODevice::Text | QIODevice::WriteOnly)) {
|
|
|
|
QTextStream out(&container);
|
|
|
|
|
|
|
|
for (const auto &file : individualFiles) {
|
|
|
|
QString destinationFilePath = tempDir.path() + QDir::separator() + file;
|
|
|
|
if (QFile::copy(sourceDir + QDir::separator() + file, destinationFilePath))
|
|
|
|
actualFormattedFilesPath << destinationFilePath;
|
|
|
|
out << destinationFilePath << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
container.close();
|
|
|
|
} else {
|
|
|
|
QFAIL("Cannot create temp test file\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
QProcess process;
|
|
|
|
process.start(m_qmlformatPath, QStringList{"-F", tempFilePath});
|
|
|
|
QVERIFY(process.waitForFinished());
|
|
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto readFile = [](const QString &filePath){
|
|
|
|
QFile file(filePath);
|
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
|
|
qWarning() << "Error on opening the file " << filePath;
|
|
|
|
return QByteArray{};
|
|
|
|
}
|
|
|
|
|
|
|
|
return file.readAll();
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const auto &filePath : actualFormattedFilesPath) {
|
|
|
|
auto expectedFormattedFile = QFileInfo(filePath).fileName();
|
|
|
|
const auto expectedFormattedFilePath = sourceDir + QDir::separator() +
|
|
|
|
expectedFormattedFile.replace(".qml", ".formatted.qml");
|
|
|
|
|
|
|
|
QCOMPARE(readFile(filePath), readFile(expectedFormattedFilePath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-02 10:34:29 +00:00
|
|
|
QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList args,
|
2021-11-03 14:39:00 +00:00
|
|
|
bool shouldSucceed, RunOption rOptions)
|
2019-12-13 15:10:46 +00:00
|
|
|
{
|
2020-03-18 08:19:31 +00:00
|
|
|
// Copy test file to temporary location
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml";
|
|
|
|
|
2021-11-03 14:39:00 +00:00
|
|
|
if (rOptions == RunOption::OnCopy) {
|
|
|
|
QFile::copy(fileToFormat, tempFile);
|
|
|
|
args << QLatin1String("-i");
|
|
|
|
args << tempFile;
|
|
|
|
} else {
|
|
|
|
args << fileToFormat;
|
|
|
|
}
|
2019-12-13 15:10:46 +00:00
|
|
|
|
|
|
|
auto verify = [&]() {
|
|
|
|
QProcess process;
|
2021-11-03 14:39:00 +00:00
|
|
|
if (rOptions == RunOption::OrigToCopy)
|
|
|
|
process.setStandardOutputFile(tempFile);
|
2019-12-13 15:10:46 +00:00
|
|
|
process.start(m_qmlformatPath, args);
|
|
|
|
QVERIFY(process.waitForFinished());
|
|
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
|
|
|
if (shouldSucceed)
|
|
|
|
QCOMPARE(process.exitCode(), 0);
|
|
|
|
};
|
|
|
|
verify();
|
|
|
|
|
2020-03-18 08:19:31 +00:00
|
|
|
QFile temp(tempFile);
|
|
|
|
|
|
|
|
temp.open(QIODevice::ReadOnly);
|
|
|
|
QString formatted = QString::fromUtf8(temp.readAll());
|
|
|
|
|
|
|
|
return formatted;
|
2019-12-13 15:10:46 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 07:38:49 +00:00
|
|
|
QString TestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucceed,
|
|
|
|
LineWriterOptions options, WriteOutChecks extraChecks,
|
|
|
|
WriteOutChecks largeChecks)
|
|
|
|
{
|
|
|
|
DomItem env = DomEnvironment::create(
|
|
|
|
QStringList(), // as we load no dependencies we do not need any paths
|
|
|
|
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
|
|
|
|
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
|
|
|
|
DomItem tFile;
|
|
|
|
env.loadFile(
|
2023-03-10 11:07:27 +00:00
|
|
|
FileToLoad::fromFileSystem(env.ownerAs<DomEnvironment>(), fileToFormat),
|
2021-09-15 07:38:49 +00:00
|
|
|
[&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
|
|
|
|
LoadOption::DefaultLoad);
|
|
|
|
env.loadPendingDependencies();
|
|
|
|
MutableDomItem myFile = tFile.field(Fields::currentItem);
|
|
|
|
|
|
|
|
DomItem writtenOut;
|
|
|
|
QString resultStr;
|
|
|
|
if (myFile.field(Fields::isValid).value().toBool()) {
|
|
|
|
WriteOutChecks checks = extraChecks;
|
|
|
|
const qsizetype largeFileSize = 32000;
|
|
|
|
if (tFile.field(Fields::code).value().toString().size() > largeFileSize)
|
|
|
|
checks = largeChecks;
|
|
|
|
|
|
|
|
QTextStream res(&resultStr);
|
|
|
|
LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"), options);
|
|
|
|
OutWriter ow(lw);
|
|
|
|
ow.indentNextlines = true;
|
|
|
|
DomItem qmlFile = tFile.field(Fields::currentItem);
|
|
|
|
writtenOut = qmlFile.writeOutForFile(ow, checks);
|
|
|
|
lw.eof();
|
|
|
|
res.flush();
|
|
|
|
}
|
|
|
|
if (didSucceed)
|
|
|
|
*didSucceed = bool(writtenOut);
|
|
|
|
return resultStr;
|
|
|
|
}
|
|
|
|
|
2019-12-13 15:10:46 +00:00
|
|
|
QTEST_MAIN(TestQmlformat)
|
|
|
|
#include "tst_qmlformat.moc"
|