aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qmlformat/tst_qmlformat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qmlformat/tst_qmlformat.cpp')
-rw-r--r--tests/auto/qml/qmlformat/tst_qmlformat.cpp601
1 files changed, 521 insertions, 80 deletions
diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
index 165be7e973..da3ebc69a2 100644
--- a/tests/auto/qml/qmlformat/tst_qmlformat.cpp
+++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDir>
@@ -32,16 +7,46 @@
#include <QProcess>
#include <QString>
#include <QTemporaryDir>
+#include <QtTest/private/qemulationdetector_p.h>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomlinewriter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
-#include <util.h>
+using namespace QQmlJS::Dom;
+
+// TODO refactor extension helpers
+const QString QML_EXT = ".qml";
+const QString JS_EXT = ".js";
+const QString MJS_EXT = ".mjs";
+
+static QStringView fileExt(QStringView filename)
+{
+ if (filename.endsWith(QML_EXT)) {
+ return QML_EXT;
+ }
+ if (filename.endsWith(JS_EXT)) {
+ return JS_EXT;
+ }
+ if (filename.endsWith(MJS_EXT)) {
+ return MJS_EXT;
+ }
+ Q_UNREACHABLE();
+};
class TestQmlformat: public QQmlDataTest
{
Q_OBJECT
+public:
+ enum class RunOption { OnCopy, OrigToCopy };
+ TestQmlformat();
+
private Q_SLOTS:
void initTestCase() override;
+ //actually testFormat tests CLI of qmlformat
void testFormat();
void testFormat_data();
@@ -49,20 +54,46 @@ private Q_SLOTS:
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
void testExample();
void testExample_data();
+ void normalizeExample();
+ void normalizeExample_data();
#endif
+ void testBackupFileLimit();
+
+ void testFilesOption_data();
+ void testFilesOption();
+
+ void plainJS_data();
+ void plainJS();
+
+ void ecmascriptModule();
+
private:
QString readTestFile(const QString &path);
- QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true);
+ //TODO(QTBUG-117849) refactor this helper function
+ QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true,
+ RunOption rOption = RunOption::OnCopy, QStringView ext = QML_EXT);
+ QString formatInMemory(const QString &fileToFormat, bool *didSucceed = nullptr,
+ LineWriterOptions options = LineWriterOptions(),
+ WriteOutChecks extraChecks = WriteOutCheck::ReparseCompare,
+ WriteOutChecks largeChecks = WriteOutCheck::None);
QString m_qmlformatPath;
QStringList m_excludedDirs;
QStringList m_invalidFiles;
+ QStringList m_ignoreFiles;
QStringList findFiles(const QDir &);
bool isInvalidFile(const QFileInfo &fileName) const;
+ bool isIgnoredFile(const QFileInfo &fileName) const;
};
+// Don't fail on warnings because we read a lot of QML files that might intentionally be malformed.
+TestQmlformat::TestQmlformat()
+ : QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
+{
+}
+
void TestQmlformat::initTestCase()
{
QQmlDataTest::initTestCase();
@@ -85,6 +116,7 @@ void TestQmlformat::initTestCase()
m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding";
m_excludedDirs << "doc/src/snippets/qtquick1/imports";
m_excludedDirs << "tests/manual/v4";
+ m_excludedDirs << "tests/manual/qmllsformatter";
m_excludedDirs << "tests/auto/qml/ecmascripttests";
m_excludedDirs << "tests/auto/qml/qmllint";
@@ -101,6 +133,9 @@ void TestQmlformat::initTestCase()
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";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidID.4.qml";
+ m_invalidFiles << "tests/auto/qml/qqmllanguage/data/questionDotEOF.qml";
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";
@@ -123,16 +158,43 @@ void TestQmlformat::initTestCase()
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";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/emptyFile.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/completions/afterDots.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/bindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/modules/data/completions/defaultBindingAfterDot.qml";
+ m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.qml";
+
+ // 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
+ m_invalidFiles << "tests/auto/quickcontrols/controls/data/tst_scrollbar.qml";
// 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";
+
+ // These files are too big
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/longQmlFile.qml";
+ m_ignoreFiles << "tests/benchmarks/qml/qmldom/data/deeplyNested.qml";
}
QStringList TestQmlformat::findFiles(const QDir &d)
{
- for (int ii = 0; ii < m_excludedDirs.count(); ++ii) {
+ for (int ii = 0; ii < m_excludedDirs.size(); ++ii) {
QString s = m_excludedDirs.at(ii);
if (d.absolutePath().endsWith(s))
return QStringList();
@@ -140,15 +202,17 @@ QStringList TestQmlformat::findFiles(const QDir &d)
QStringList rv;
- QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
- QDir::Files);
- foreach (const QString &file, files) {
- rv << d.absoluteFilePath(file);
+ const QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"),
+ QDir::Files);
+ for (const QString &file: files) {
+ QString absoluteFilePath = d.absoluteFilePath(file);
+ if (!isIgnoredFile(QFileInfo(absoluteFilePath)))
+ rv << absoluteFilePath;
}
- QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
- QDir::NoSymLinks);
- foreach (const QString &dir, dirs) {
+ const QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot |
+ QDir::NoSymLinks);
+ for (const QString &dir: dirs) {
QDir sub = d;
sub.cd(dir);
rv << findFiles(sub);
@@ -166,6 +230,15 @@ bool TestQmlformat::isInvalidFile(const QFileInfo &fileName) const
return false;
}
+bool TestQmlformat::isIgnoredFile(const QFileInfo &fileName) const
+{
+ for (const QString &file : m_ignoreFiles) {
+ if (fileName.absoluteFilePath().endsWith(file))
+ return true;
+ }
+ return false;
+}
+
QString TestQmlformat::readTestFile(const QString &path)
{
QFile file(testFile(path));
@@ -200,49 +273,145 @@ void TestQmlformat::testFormat_data()
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("fileFormatted");
QTest::addColumn<QStringList>("args");
+ QTest::addColumn<RunOption>("runOption");
- QTest::newRow("example1")
+ QTest::newRow("example1") << "Example1.qml"
+ << "Example1.formatted.qml" << QStringList {} << RunOption::OnCopy;
+ QTest::newRow("example1 (tabs)")
<< "Example1.qml"
- << "Example1.formatted.qml" << QStringList {};
- QTest::newRow("example1 (tabs)") << "Example1.qml"
- << "Example1.formatted.tabs.qml" << QStringList { "-t" };
+ << "Example1.formatted.tabs.qml" << QStringList { "-t" } << RunOption::OnCopy;
QTest::newRow("example1 (two spaces)")
<< "Example1.qml"
- << "Example1.formatted.2spaces.qml" << QStringList { "-w", "2" };
- QTest::newRow("annotation")
- << "Annotations.qml"
- << "Annotations.formatted.qml" << QStringList {};
+ << "Example1.formatted.2spaces.qml" << QStringList { "-w", "2" } << RunOption::OnCopy;
+ QTest::newRow("annotation") << "Annotations.qml"
+ << "Annotations.formatted.qml" << QStringList {}
+ << RunOption::OnCopy;
QTest::newRow("front inline") << "FrontInline.qml"
- << "FrontInline.formatted.qml" << QStringList {};
+ << "FrontInline.formatted.qml" << QStringList {}
+ << RunOption::OnCopy;
QTest::newRow("if blocks") << "IfBlocks.qml"
- << "IfBlocks.formatted.qml" << QStringList {};
- QTest::newRow("read-only properties") << "readOnlyProps.qml"
- << "readOnlyProps.formatted.qml" << QStringList {};
+ << "IfBlocks.formatted.qml" << QStringList {} << RunOption::OnCopy;
+ QTest::newRow("read-only properties")
+ << "readOnlyProps.qml"
+ << "readOnlyProps.formatted.qml" << QStringList {} << RunOption::OnCopy;
QTest::newRow("states and transitions")
<< "statesAndTransitions.qml"
- << "statesAndTransitions.formatted.qml" << QStringList {};
- QTest::newRow("large bindings") << "largeBindings.qml"
- << "largeBindings.formatted.qml" << QStringList {};
- QTest::newRow("verbatim strings") << "verbatimString.qml"
- << "verbatimString.formatted.qml" << QStringList {};
- QTest::newRow("inline components") << "inlineComponents.qml"
- << "inlineComponents.formatted.qml" << QStringList {};
+ << "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;
QTest::newRow("nested ifs") << "nestedIf.qml"
- << "nestedIf.formatted.qml" << QStringList {};
+ << "nestedIf.formatted.qml" << QStringList {} << RunOption::OnCopy;
QTest::newRow("QTBUG-85003") << "QtBug85003.qml"
- << "QtBug85003.formatted.qml" << QStringList {};
- QTest::newRow("nested functions") << "nestedFunctions.qml"
- << "nestedFunctions.formatted.qml" << QStringList {};
- QTest::newRow("multiline comments") << "multilineComment.qml"
- << "multilineComment.formatted.qml" << QStringList {};
+ << "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;
QTest::newRow("for of") << "forOf.qml"
- << "forOf.formatted.qml" << QStringList {};
- QTest::newRow("property names") << "propertyNames.qml"
- << "propertyNames.formatted.qml" << QStringList {};
+ << "forOf.formatted.qml" << QStringList {} << RunOption::OnCopy;
+ QTest::newRow("property names")
+ << "propertyNames.qml"
+ << "propertyNames.formatted.qml" << QStringList {} << RunOption::OnCopy;
QTest::newRow("empty object") << "emptyObject.qml"
- << "emptyObject.formatted.qml" << QStringList {};
- QTest::newRow("arrow functions") << "arrowFunctions.qml"
- << "arrowFunctions.formatted.qml" << QStringList {};
+ << "emptyObject.formatted.qml" << QStringList {}
+ << RunOption::OnCopy;
+ QTest::newRow("arrow functions")
+ << "arrowFunctions.qml"
+ << "arrowFunctions.formatted.qml" << QStringList {} << RunOption::OnCopy;
+ QTest::newRow("settings") << "settings/Example1.qml"
+ << "settings/Example1.formatted_mac_cr.qml" << QStringList {}
+ << RunOption::OrigToCopy;
+ QTest::newRow("forWithLet")
+ << "forWithLet.qml"
+ << "forWithLet.formatted.qml" << QStringList {} << RunOption::OnCopy;
+
+ 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;
+
+ QTest::newRow("ids new lines")
+ << "checkIdsNewline.qml"
+ << "checkIdsNewline.formatted.qml" << QStringList { "-n" } << RunOption::OnCopy;
+
+ 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;
+ QTest::newRow("dontRemoveComments")
+ << "dontRemoveComments.qml"
+ << "dontRemoveComments.formatted.qml" << QStringList {} << RunOption::OnCopy;
+ QTest::newRow("ecmaScriptClassInQml")
+ << "ecmaScriptClassInQml.qml"
+ << "ecmaScriptClassInQml.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("arrowFunctionWithBinding")
+ << "arrowFunctionWithBinding.qml"
+ << "arrowFunctionWithBinding.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("blanklinesAfterComment")
+ << "blanklinesAfterComment.qml"
+ << "blanklinesAfterComment.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("pragmaValueList")
+ << "pragma.qml"
+ << "pragma.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("objectDestructuring")
+ << "objectDestructuring.qml"
+ << "objectDestructuring.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("destructuringFunctionParameter")
+ << "destructuringFunctionParameter.qml"
+ << "destructuringFunctionParameter.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("ellipsisFunctionArgument")
+ << "ellipsisFunctionArgument.qml"
+ << "ellipsisFunctionArgument.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("importStatements")
+ << "importStatements.qml"
+ << "importStatements.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("arrayEndComma")
+ << "arrayEndComma.qml"
+ << "arrayEndComma.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("escapeChars")
+ << "escapeChars.qml"
+ << "escapeChars.formatted.qml" << QStringList{} << RunOption::OnCopy;
+ QTest::newRow("javascriptBlock")
+ << "javascriptBlock.qml"
+ << "javascriptBlock.formatted.qml" << QStringList{} << RunOption::OnCopy;
+
+ //plainJS
+ QTest::newRow("nestedLambdaWithIfElse")
+ << "lambdaWithIfElseInsideLambda.js"
+ << "lambdaWithIfElseInsideLambda.formatted.js" << QStringList{} << RunOption::OnCopy;
+
+ QTest::newRow("indentEquals2")
+ << "threeFunctionsOneLine.js"
+ << "threeFunctions.formattedW2.js" << QStringList{"-w=2"} << RunOption::OnCopy;
+
+ QTest::newRow("tabIndents")
+ << "threeFunctionsOneLine.js"
+ << "threeFunctions.formattedTabs.js" << QStringList{"-t"} << RunOption::OnCopy;
+
+ QTest::newRow("normalizedFunctionSpacing")
+ << "threeFunctionsOneLine.js"
+ << "threeFunctions.formattedFuncSpacing.js"
+ << QStringList{ "-n", "--functions-spacing" } << RunOption::OnCopy;
+
+ QTest::newRow("esm_tabIndents")
+ << "mini_esm.mjs"
+ << "mini_esm.formattedTabs.mjs" << QStringList{ "-t" } << RunOption::OnCopy;
}
void TestQmlformat::testFormat()
@@ -250,21 +419,144 @@ void TestQmlformat::testFormat()
QFETCH(QString, file);
QFETCH(QString, fileFormatted);
QFETCH(QStringList, args);
+ QFETCH(RunOption, runOption);
- QCOMPARE(runQmlformat(testFile(file), args), readTestFile(fileFormatted));
+ auto formatted = runQmlformat(testFile(file), args, true, runOption, fileExt(file));
+ QEXPECT_FAIL("normalizedFunctionSpacing",
+ "Normalize && function spacing are not yet supported for JS", Abort);
+ auto exp = readTestFile(fileFormatted);
+ QCOMPARE(formatted, exp);
+}
+
+void TestQmlformat::plainJS_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QString>("fileFormatted");
+
+ QTest::newRow("simpleStatement") << "simpleJSStatement.js"
+ << "simpleJSStatement.formatted.js";
+ QTest::newRow("simpleFunction") << "simpleOnelinerJSFunc.js"
+ << "simpleOnelinerJSFunc.formatted.js";
+ QTest::newRow("simpleLoop") << "simpleLoop.js"
+ << "simpleLoop.formatted.js";
+ QTest::newRow("messyIfStatement") << "messyIfStatement.js"
+ << "messyIfStatement.formatted.js";
+ QTest::newRow("lambdaFunctionWithLoop") << "lambdaFunctionWithLoop.js"
+ << "lambdaFunctionWithLoop.formatted.js";
+ QTest::newRow("lambdaWithIfElse") << "lambdaWithIfElse.js"
+ << "lambdaWithIfElse.formatted.js";
+ QTest::newRow("nestedLambdaWithIfElse") << "lambdaWithIfElseInsideLambda.js"
+ << "lambdaWithIfElseInsideLambda.formatted.js";
+ QTest::newRow("twoFunctions") << "twoFunctions.js"
+ << "twoFunctions.formatted.js";
+ QTest::newRow("pragma") << "pragma.js"
+ << "pragma.formatted.js";
+ QTest::newRow("classConstructor") << "class.js"
+ << "class.formatted.js";
+ QTest::newRow("legacyDirectives") << "directives.js"
+ << "directives.formatted.js";
+ QTest::newRow("legacyDirectivesWithComments") << "directivesWithComments.js"
+ << "directivesWithComments.formatted.js";
+}
+
+void TestQmlformat::plainJS()
+{
+ QFETCH(QString, file);
+ QFETCH(QString, fileFormatted);
+
+ bool wasSuccessful;
+ LineWriterOptions opts;
+#ifdef Q_OS_WIN
+ opts.lineEndings = QQmlJS::Dom::LineWriterOptions::LineEndings::Windows;
+#endif
+ QString output = formatInMemory(testFile(file), &wasSuccessful, opts, WriteOutCheck::None);
+
+ QVERIFY(wasSuccessful && !output.isEmpty());
+
+ // TODO(QTBUG-119404)
+ QEXPECT_FAIL("classConstructor", "see QTBUG-119404", Abort);
+ // TODO(QTBUG-119770)
+ QEXPECT_FAIL("legacyDirectivesWithComments", "see QTBUG-119770", Abort);
+ auto exp = readTestFile(fileFormatted);
+ QCOMPARE(output, exp);
+}
+
+void TestQmlformat::ecmascriptModule()
+{
+ QString file("esm.mjs");
+ QString formattedFile("esm.formatted.mjs");
+
+ bool wasSuccessful;
+ LineWriterOptions opts;
+#ifdef Q_OS_WIN
+ opts.lineEndings = QQmlJS::Dom::LineWriterOptions::LineEndings::Windows;
+#endif
+ QString output = formatInMemory(testFile(file), &wasSuccessful, opts, WriteOutCheck::None);
+
+ QVERIFY(wasSuccessful && !output.isEmpty());
+
+ auto exp = readTestFile(formattedFile);
+ QCOMPARE(output, readTestFile(formattedFile));
}
#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
void TestQmlformat::testExample_data()
{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Crashes in QEMU. (timeout)");
QTest::addColumn<QString>("file");
QString examples = QLatin1String(SRCDIR) + "/../../../../examples/";
QString tests = QLatin1String(SRCDIR) + "/../../../../tests/";
+ QStringList exampleFiles;
+ QStringList testFiles;
QStringList files;
- files << findFiles(QDir(examples));
- files << findFiles(QDir(tests));
+ exampleFiles << findFiles(QDir(examples));
+ testFiles << findFiles(QDir(tests));
+
+ // Actually this test is an e2e test and not the unit test.
+ // At the moment of writing, CI lacks providing instruments for the automated tests
+ // which might be time-consuming, as for example this one.
+ // Therefore as part of QTBUG-122990 this test was copied to the /manual/e2e/qml/qmlformat
+ // however very small fraction of the test data is still preserved here for the sake of
+ // testing automatically at least a small part of the examples
+ const int nBatch = 10;
+ files << exampleFiles.mid(0, nBatch) << exampleFiles.mid(exampleFiles.size() / 2, nBatch)
+ << exampleFiles.mid(exampleFiles.size() - nBatch, nBatch);
+ files << testFiles.mid(0, nBatch) << testFiles.mid(exampleFiles.size() / 2, nBatch)
+ << testFiles.mid(exampleFiles.size() - nBatch, nBatch);
+
+ for (const QString &file : files)
+ QTest::newRow(qPrintable(file)) << file;
+}
+
+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;
@@ -276,26 +568,138 @@ void TestQmlformat::testExample()
{
QFETCH(QString, file);
const bool isInvalid = isInvalidFile(QFileInfo(file));
- QString output = runQmlformat(file, {}, !isInvalid);
+ bool wasSuccessful;
+ LineWriterOptions opts;
+ opts.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+ QString output = formatInMemory(file, &wasSuccessful, opts);
+
+ if (!isInvalid)
+ QVERIFY(wasSuccessful && !output.isEmpty());
+}
+
+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(!output.isEmpty());
+ QVERIFY(wasSuccessful && !output.isEmpty());
}
#endif
+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));
+ };
+}
+
+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));
+ }
+}
+
QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList args,
- bool shouldSucceed)
+ bool shouldSucceed, RunOption rOptions, QStringView ext)
{
// Copy test file to temporary location
QTemporaryDir tempDir;
- const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml";
- QFile::copy(fileToFormat, tempFile);
-
- args << QLatin1String("-i");
- args << tempFile;
+ const QString tempFile = (tempDir.path() + QDir::separator() + "to_format") % ext;
+
+ if (rOptions == RunOption::OnCopy) {
+ QFile::copy(fileToFormat, tempFile);
+ args << QLatin1String("-i");
+ args << tempFile;
+ } else {
+ args << fileToFormat;
+ }
auto verify = [&]() {
QProcess process;
+ if (rOptions == RunOption::OrigToCopy)
+ process.setStandardOutputFile(tempFile);
process.start(m_qmlformatPath, args);
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
@@ -306,11 +710,48 @@ QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList arg
QFile temp(tempFile);
- temp.open(QIODevice::ReadOnly);
+ if (!temp.open(QIODevice::ReadOnly))
+ qFatal("Could not open %s", qPrintable(tempFile));
QString formatted = QString::fromUtf8(temp.readAll());
return formatted;
}
+QString TestQmlformat::formatInMemory(const QString &fileToFormat, bool *didSucceed,
+ LineWriterOptions options, WriteOutChecks extraChecks,
+ WriteOutChecks largeChecks)
+{
+ auto 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(FileToLoad::fromFileSystem(env, fileToFormat),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ env->loadPendingDependencies();
+ MutableDomItem myFile = tFile.field(Fields::currentItem);
+
+ bool 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 = writtenOut;
+ return resultStr;
+}
+
QTEST_MAIN(TestQmlformat)
#include "tst_qmlformat.moc"