2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2019 The Qt Company Ltd.
|
2024-02-22 14:51:16 +00:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
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>
|
2025-01-21 16:08:42 +00:00
|
|
|
#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
|
2025-01-27 11:06:07 +00:00
|
|
|
#include <QtQmlFormat/private/qqmlformatoptions_p.h>
|
2021-09-15 07:38:49 +00:00
|
|
|
|
|
|
|
using namespace QQmlJS::Dom;
|
2019-12-13 15:10:46 +00:00
|
|
|
|
2024-03-14 11:48:38 +00:00
|
|
|
// 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();
|
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
2023-11-20 13:19:41 +00:00
|
|
|
//actually testFormat tests CLI of qmlformat
|
2019-12-13 15:10:46 +00:00
|
|
|
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();
|
2023-11-20 11:57:54 +00:00
|
|
|
|
|
|
|
void plainJS_data();
|
|
|
|
void plainJS();
|
2024-03-14 11:48:38 +00:00
|
|
|
|
|
|
|
void ecmascriptModule();
|
|
|
|
|
2024-12-04 10:20:09 +00:00
|
|
|
void commandLineOptions_data();
|
|
|
|
void commandLineOptions();
|
|
|
|
|
2025-01-21 16:08:42 +00:00
|
|
|
void writeDefaults();
|
|
|
|
|
2025-01-27 11:06:07 +00:00
|
|
|
void settingsFromFileOrCommandLine_data();
|
|
|
|
void settingsFromFileOrCommandLine();
|
|
|
|
|
2025-01-24 12:51:04 +00:00
|
|
|
void multipleSettingsFiles();
|
2025-03-17 08:48:53 +00:00
|
|
|
void qml_data();
|
|
|
|
void qml();
|
2025-01-24 12:51:04 +00:00
|
|
|
|
2019-12-13 15:10:46 +00:00
|
|
|
private:
|
|
|
|
QString readTestFile(const QString &path);
|
2023-11-20 13:19:41 +00:00
|
|
|
//TODO(QTBUG-117849) refactor this helper function
|
2021-11-03 14:39:00 +00:00
|
|
|
QString runQmlformat(const QString &fileToFormat, QStringList args, bool shouldSucceed = true,
|
2024-03-14 11:48:38 +00:00
|
|
|
RunOption rOption = RunOption::OnCopy, QStringView ext = QML_EXT);
|
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";
|
qqmljs.g: insert empty identifiers when missing
For the completion in qmlls, users usually expect to see a list of
completions after typing in a T_DOT ("."). Sadly, this T_DOT usually
makes the QML code invalid or ambiguous, such that the parser aborts
parsing.
For qmlls, this is quite bad: no completions can be proposed if the AST
cannot be computed. Therefore, this commit tries to make the parser a
little bit more resistant to missing T_IDENTIFIER behind T_DOT.
Add a yyprevtoken field in the parser to keep track of what was the
last successfully parsed token, and update it with yytoken before yytoken
changes.
Extract the pushTokenWithEmptyLocation() logic from the automatic
semicolon inserting code, and reuse it for the automatic insertion of
identifiers after dots.
Add some tests in tst_qmlls_utils and adapt the qmlls completion code to
work with missing right hand sides (RHS) of dotted expression
`a.b`. Create a new file missingRHS.qml because yyy.qml does not seem to
stop growing.
The fix of this commit does not take care of all possible cases: when
T_DOT is followed by an T_IDENTIFIER, then no T_IDENTIFIER is inserted
even if the parsing fails afterwards. This happens when a JS
statement is behind a T_DOT without identifier, for example. Add tests
for that too, QEXPECT_FAIL them and put them in a separate file
missingRHS.parserfail.qml. They need to be in a separate file because no
completions can be obtained when the parser fails, and that affects all
the completions of the entire file. This special file
missingRHS.parserfail.qml also needs to be ignored by tst_qmlformat.
Task-number: QTBUG-115836
Change-Id: If307430131a7df25ae9bd4ea0393d47c0641c8d3
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2023-11-17 13:23:20 +00:00
|
|
|
m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qmlls/utils/data/completions/missingRHS.parserfail.qml";
|
2023-12-14 10:45:51 +00:00
|
|
|
m_invalidFiles << "tests/auto/qmlls/utils/data/completions/attachedPropertyMissingRHS.qml";
|
|
|
|
m_invalidFiles << "tests/auto/qmlls/utils/data/completions/groupedPropertyMissingRHS.qml";
|
qmlls: insert semicolons behind dots as completion workaround
IDE's usually ask completion requests whenever users starts typing a
'.'. For this patch, we assume that the user is currently writing code,
that the '.' he just entered is followed by a newline and that the line
below the cursor is unrelated, which should be a pretty realistic
scenario of writing code.
For example, imagine a user typing in following code:
```
x: root.<current cursor>
SomeQualifiedModule.Item {}
```
The LSP client will request completion suggestions when the user types
in `root.` and qmlls will be confused about the completion, as it will
see a QmlObject binding in the Dom representation.
Another example are JS statements, where the parser stops working when
he sees
```
root.<current cursor>
for (;;) {}
```
because it tries to construct the function call `root.for` and has
troubles with the semicolons inside the function call argument.
To go around this problem, insert semicolons when the symbol that
requested the completion is a dot and when this dot is followed by a
newline.
In qqmlcompletionsupport, create a new Dom representation with the
patched code, and keep it out the QQmlCodeModel to avoid bothering other
modules, like the linting module, with the inserted "invisible"
semicolon (invisible in the eyes of the user).
Now, when the parser is run again on the patched code, it will see
```
root.;
```
and will happily create the correct field member expression using its
recovery mode. One could also have inserted any identifier like
'_dummyIdentifier' followed by a semicolon to get:
```
root._dummyIdentifier;
```
which would have worked too I believe.
Add two tests, one in tst_qmlls_modules to test if the patched Dom can
be used to provide the completions, and on in tst_qmlls_utils to test if
the "normal" dom construction works on (hand)patched versions of the
files. tst_qmlls_utils cannot test the patching itself as that happens
in the qqmlcompletionsupport module.
The afterDots.qml files are invalid and as such should be ignored by
tst_qmlformat.
Pick-to: 6.7
Fixes: QTBUG-119839
Change-Id: Ib914b78512894c423792588842d635d3d1467922
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2023-12-21 10:04:19 +00:00
|
|
|
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";
|
2024-01-17 15:08:12 +00:00
|
|
|
m_invalidFiles << "tests/auto/qmlls/utils/data/qualifiedModule.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 (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;
|
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-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
|
|
|
|
2024-12-18 14:15:50 +00:00
|
|
|
QTest::newRow("sorting imports")
|
|
|
|
<< "sortingImports.qml"
|
|
|
|
<< "sortingImports.formatted.qml" << QStringList { "-S" } << 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-11-20 13:19:41 +00:00
|
|
|
|
|
|
|
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;
|
2024-03-14 11:48:38 +00:00
|
|
|
|
|
|
|
QTest::newRow("esm_tabIndents")
|
|
|
|
<< "mini_esm.mjs"
|
|
|
|
<< "mini_esm.formattedTabs.mjs" << QStringList{ "-t" } << 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
|
|
|
|
2024-03-14 11:48:38 +00:00
|
|
|
auto formatted = runQmlformat(testFile(file), args, true, runOption, fileExt(file));
|
2025-03-17 08:48:53 +00:00
|
|
|
auto exp = readTestFile(fileFormatted);
|
2023-11-20 13:19:41 +00:00
|
|
|
QEXPECT_FAIL("normalizedFunctionSpacing",
|
|
|
|
"Normalize && function spacing are not yet supported for JS", Abort);
|
2024-03-14 11:48:38 +00:00
|
|
|
QCOMPARE(formatted, exp);
|
2020-06-18 12:36:37 +00:00
|
|
|
}
|
|
|
|
|
2023-11-20 11:57:54 +00:00
|
|
|
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";
|
2025-01-28 09:33:45 +00:00
|
|
|
QTest::newRow("preserveOptionalTokens") << "preserveOptionalTokens.js"
|
|
|
|
<< "preserveOptionalTokens.formatted.js";
|
2025-01-28 15:45:28 +00:00
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions.fail_pragma")
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_pragma.js"
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_pragma.formatted.js";
|
2025-01-29 14:51:09 +00:00
|
|
|
QTest::newRow("fromAsIdentifier") << "fromAsIdentifier.js"
|
|
|
|
<< "fromAsIdentifier.formatted.js";
|
2025-01-20 13:29:58 +00:00
|
|
|
QTest::newRow("caseWithComment") << "caseWithComment.js"
|
|
|
|
<< "caseWithComment.formatted.js";
|
2023-11-20 11:57:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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-119770)
|
|
|
|
QEXPECT_FAIL("legacyDirectivesWithComments", "see QTBUG-119770", Abort);
|
2025-01-28 15:45:28 +00:00
|
|
|
QEXPECT_FAIL("noSuperfluousSpaceInsertions.fail_pragma",
|
|
|
|
"Not all cases have been covered yet (QTBUG-133315)", Abort);
|
2023-11-20 11:57:54 +00:00
|
|
|
auto exp = readTestFile(fileFormatted);
|
2024-03-14 11:48:38 +00:00
|
|
|
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));
|
2023-11-20 11:57:54 +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/";
|
|
|
|
|
2024-04-22 14:49:00 +00:00
|
|
|
QStringList exampleFiles;
|
|
|
|
QStringList testFiles;
|
2020-01-15 10:25:18 +00:00
|
|
|
QStringList files;
|
2024-04-22 14:49:00 +00:00
|
|
|
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);
|
2020-01-15 10:25:18 +00:00
|
|
|
|
|
|
|
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");
|
|
|
|
|
2025-02-10 09:57:15 +00:00
|
|
|
QTest::newRow("initial") << "fileListToFormat" << QStringList{ "valid1.qml", "valid2.qml" };
|
2023-10-13 11:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestQmlformat::testFilesOption()
|
|
|
|
{
|
|
|
|
QFETCH(QString, containerFile);
|
|
|
|
QFETCH(QStringList, individualFiles);
|
|
|
|
|
|
|
|
// Create a temporary directory
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
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);
|
2025-02-11 10:03:55 +00:00
|
|
|
QVERIFY2(container.open(QIODevice::Text | QIODevice::WriteOnly),
|
|
|
|
"Cannot create temp test file");
|
|
|
|
|
|
|
|
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";
|
2023-10-13 11:30:27 +00:00
|
|
|
}
|
2025-02-11 10:03:55 +00:00
|
|
|
container.close();
|
2023-10-13 11:30:27 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
QProcess process;
|
|
|
|
process.start(m_qmlformatPath, QStringList{"-F", tempFilePath});
|
|
|
|
QVERIFY(process.waitForFinished());
|
|
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
2025-02-11 10:03:55 +00:00
|
|
|
QCOMPARE(process.exitCode(), 0);
|
2023-10-13 11:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
};
|
|
|
|
|
2025-02-11 10:03:55 +00:00
|
|
|
for (const auto &filePath : std::as_const(actualFormattedFilesPath)) {
|
2023-10-13 11:30:27 +00:00
|
|
|
auto expectedFormattedFile = QFileInfo(filePath).fileName();
|
|
|
|
const auto expectedFormattedFilePath = sourceDir + QDir::separator() +
|
|
|
|
expectedFormattedFile.replace(".qml", ".formatted.qml");
|
|
|
|
|
|
|
|
QCOMPARE(readFile(filePath), readFile(expectedFormattedFilePath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 10:20:09 +00:00
|
|
|
void TestQmlformat::commandLineOptions_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QStringList>("args");
|
|
|
|
QTest::addColumn<QString>("expectedErrorMessage");
|
|
|
|
|
2025-02-10 09:57:15 +00:00
|
|
|
const QString dummy = testFile("dummy.qml");
|
|
|
|
const QString empty = testFile("empty");
|
2024-12-04 10:20:09 +00:00
|
|
|
QTest::newRow("columnWidthError")
|
2025-02-10 09:57:15 +00:00
|
|
|
<< QStringList{ dummy, "-W", "-11111" }
|
2024-12-04 10:20:09 +00:00
|
|
|
<< "Error: Invalid value passed to -W. Must be an integer >= -1\n";
|
|
|
|
QTest::newRow("columnWidthNoError")
|
2025-02-10 09:57:15 +00:00
|
|
|
<< QStringList{ dummy, "-W", "80" } << "";
|
2024-12-04 10:20:09 +00:00
|
|
|
QTest::newRow("indentWidthError")
|
2025-02-10 09:57:15 +00:00
|
|
|
<< QStringList{ dummy, "--indent-width", "expect integer" }
|
2024-12-04 10:20:09 +00:00
|
|
|
<< "Error: Invalid value passed to -w\n";
|
|
|
|
QTest::newRow("indentWidthNoError")
|
2025-02-10 09:57:15 +00:00
|
|
|
<< QStringList{ dummy, "--indent-width", "4" } << "";
|
|
|
|
QTest::newRow("noInputFiles.qml")
|
|
|
|
<< QStringList{} << "Error: Expected at least one input file.\n";
|
|
|
|
QTest::newRow("fOptionFileDoesNotExist")
|
|
|
|
<< QStringList{ "-F", "nope" }
|
|
|
|
<< "Error: Could not open file \"nope\" for option -F.\n";
|
|
|
|
QTest::newRow("fOptionFileIsEmpty")
|
|
|
|
<< QStringList{ "-F", empty }
|
|
|
|
<< "Error: File \"" + empty + "\" for option -F is empty.\n";
|
|
|
|
QTest::newRow("fOptionFileContainsNope")
|
|
|
|
<< QStringList{ "-F", testFile("filesToFormatNope") }
|
|
|
|
<< "Error: Entry \"nope\" of file \"" + testFile("filesToFormatNope")
|
|
|
|
+ "\" passed to option -F could not be found.\n";
|
|
|
|
QTest::newRow("positionalArgumentDoesNotExist")
|
|
|
|
<< QStringList{ "nope" }
|
|
|
|
<< "Error: Could not find file \"nope\".\n";
|
2024-12-04 10:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestQmlformat::commandLineOptions()
|
|
|
|
{
|
|
|
|
QFETCH(QStringList, args);
|
|
|
|
QFETCH(QString, expectedErrorMessage);
|
|
|
|
|
|
|
|
auto verify = [&]() {
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
const QString tempFile = tempDir.path() + QDir::separator() + "test_0.qml";
|
|
|
|
|
|
|
|
QProcess process;
|
|
|
|
process.setStandardOutputFile(tempFile);
|
|
|
|
process.start(m_qmlformatPath, args);
|
|
|
|
QVERIFY(process.waitForFinished());
|
|
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
|
|
|
// normalized error message
|
|
|
|
auto rawError = process.readAllStandardError();
|
|
|
|
QTextStream stream(&rawError, QIODeviceBase::ReadOnly | QIODeviceBase::Text);
|
|
|
|
QCOMPARE(stream.readAll(), expectedErrorMessage.toUtf8());
|
|
|
|
if (expectedErrorMessage.isEmpty())
|
|
|
|
QCOMPARE(process.exitCode(), 0);
|
|
|
|
else
|
|
|
|
QCOMPARE_NE(process.exitCode(), 0);
|
|
|
|
};
|
|
|
|
|
|
|
|
verify();
|
|
|
|
}
|
|
|
|
|
2025-01-21 16:08:42 +00:00
|
|
|
void TestQmlformat::writeDefaults()
|
|
|
|
{
|
|
|
|
auto verify = [&]() {
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
const QString qmlformatIni = tempDir.path() + QDir::separator() + ".qmlformat.ini";
|
|
|
|
|
|
|
|
QProcess process;
|
|
|
|
process.setWorkingDirectory(tempDir.path());
|
|
|
|
process.start(m_qmlformatPath, QStringList{ "--write-defaults" });
|
|
|
|
QVERIFY(process.waitForFinished());
|
|
|
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
|
|
|
|
|
|
|
QQmlToolingSettings settings("qmlformat");
|
|
|
|
QVERIFY(settings.search(qmlformatIni));
|
|
|
|
|
|
|
|
QCOMPARE(settings.value("UseTabs").toBool(), false);
|
|
|
|
QCOMPARE(settings.value("IndentWidth").toInt(), 4);
|
|
|
|
QCOMPARE(settings.value("MaxColumnWidth").toInt(), -1);
|
|
|
|
QCOMPARE(settings.value("NormalizeOrder").toBool(), false);
|
|
|
|
QCOMPARE(settings.value("NewlineType").toString(), "native");
|
|
|
|
QCOMPARE(settings.value("ObjectSpacing").toBool(), false);
|
|
|
|
QCOMPARE(settings.value("FunctionsSpacing").toBool(), false);
|
|
|
|
QCOMPARE(settings.value("SortImports").toBool(), false);
|
|
|
|
};
|
|
|
|
|
|
|
|
verify();
|
|
|
|
}
|
|
|
|
|
2025-01-27 11:06:07 +00:00
|
|
|
void TestQmlformat::settingsFromFileOrCommandLine_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("qmlformatIniPath");
|
|
|
|
QTest::addColumn<QStringList>("qmlformatInitOptions");
|
|
|
|
QTest::addColumn<QQmlFormatOptions>("expectedOptions");
|
|
|
|
|
|
|
|
{
|
|
|
|
QQmlFormatOptions options;
|
|
|
|
options.setIndentWidth(20);
|
|
|
|
// In settings file, indentwidth is set to 4000, while cli overrides it to 20
|
|
|
|
// 20 should be the final value
|
|
|
|
QTest::newRow("clOverridesIndentWidth")
|
|
|
|
<< testFile("iniFiles/dummySettingsFile.ini")
|
|
|
|
<< QStringList{ m_qmlformatPath, "--indent-width", "20" } << options;
|
2025-01-24 12:51:04 +00:00
|
|
|
options.setIndentWidth(4000);
|
|
|
|
// In settings file, indentwidth is set to 4000, and nothing overrides it.
|
|
|
|
// 4000 should be the final value
|
|
|
|
QTest::newRow("iniFileIndentWidth") << testFile("iniFiles/dummySettingsFile.ini")
|
|
|
|
<< QStringList{ m_qmlformatPath } << options;
|
|
|
|
options.setMaxColumnWidth(100);
|
|
|
|
// In settings file, maxcolumnwidth is set to -1, but cli overrides it 100.
|
|
|
|
// 100 should be the final value
|
|
|
|
QTest::newRow("clOverridesColumnWidth")
|
|
|
|
<< testFile("iniFiles/dummySettingsFile.ini")
|
|
|
|
<< QStringList{ m_qmlformatPath, "-W", "100" } << options;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
QQmlFormatOptions options;
|
|
|
|
// settings file sets all bools excepts Tabs to true.
|
|
|
|
options.setTabsEnabled(false);
|
|
|
|
options.setNormalizeEnabled(true);
|
|
|
|
options.setObjectsSpacing(true);
|
|
|
|
options.setFunctionsSpacing(true);
|
|
|
|
QTest::newRow("iniFileSetsBools") << testFile("iniFiles/toggledBools.ini")
|
|
|
|
<< QStringList{ m_qmlformatPath } << options;
|
|
|
|
|
|
|
|
// cli overrides the Tabs option to true
|
|
|
|
options.setTabsEnabled(true);
|
|
|
|
QTest::newRow("cliOverridesTabs") << testFile("iniFiles/toggledBools.ini")
|
|
|
|
<< QStringList{ m_qmlformatPath, "--tabs" } << options;
|
2025-01-27 11:06:07 +00:00
|
|
|
}
|
2025-01-27 11:01:56 +00:00
|
|
|
{
|
|
|
|
// settings should apply when -F is passed
|
|
|
|
QQmlFormatOptions options;
|
|
|
|
options.setIndentWidth(4000);
|
|
|
|
QTest::newRow("settingOnFilesOption")
|
|
|
|
<< testFile("iniFiles/dummySettingsFile.ini")
|
|
|
|
<< QStringList{ m_qmlformatPath, "-F", "dummyFilesPath" } << options;
|
|
|
|
}
|
2025-01-27 11:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestQmlformat::settingsFromFileOrCommandLine()
|
|
|
|
{
|
|
|
|
QFETCH(QString, qmlformatIniPath);
|
|
|
|
QFETCH(QStringList, qmlformatInitOptions);
|
|
|
|
QFETCH(QQmlFormatOptions, expectedOptions);
|
|
|
|
|
|
|
|
auto verify = [&]() {
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
const QString qmlformatIni = tempDir.path() + QDir::separator() + ".qmlformat.ini";
|
|
|
|
const QString dummyQmlFile = tempDir.path() + QDir::separator() + "test.qml";
|
|
|
|
|
|
|
|
QFile::copy(qmlformatIniPath, qmlformatIni);
|
|
|
|
QQmlFormatSettings settings("qmlformat");
|
2025-02-10 09:57:15 +00:00
|
|
|
QStringList cmdlineOptions;
|
2025-01-27 11:01:56 +00:00
|
|
|
if ((qstrcmp(QTest::currentDataTag(), "settingOnFilesOption") == 0))
|
2025-02-10 09:57:15 +00:00
|
|
|
cmdlineOptions = qmlformatInitOptions << "-F" << dummyQmlFile;
|
|
|
|
else
|
|
|
|
cmdlineOptions = QStringList(dummyQmlFile) << qmlformatInitOptions;
|
|
|
|
|
|
|
|
QQmlFormatOptions options = QQmlFormatOptions::buildCommandLineOptions(cmdlineOptions);
|
2025-01-27 11:06:07 +00:00
|
|
|
auto overridenOptions = options.optionsForFile(dummyQmlFile, &settings);
|
|
|
|
|
|
|
|
QCOMPARE(overridenOptions.tabsEnabled(), expectedOptions.tabsEnabled());
|
|
|
|
QCOMPARE(overridenOptions.indentWidth(), expectedOptions.indentWidth());
|
|
|
|
QCOMPARE(overridenOptions.maxColumnWidth(), expectedOptions.maxColumnWidth());
|
|
|
|
QCOMPARE(overridenOptions.normalizeEnabled(), expectedOptions.normalizeEnabled());
|
|
|
|
QCOMPARE(overridenOptions.newline(), expectedOptions.newline());
|
|
|
|
QCOMPARE(overridenOptions.objectsSpacing(), expectedOptions.objectsSpacing());
|
|
|
|
QCOMPARE(overridenOptions.functionsSpacing(), expectedOptions.functionsSpacing());
|
|
|
|
QCOMPARE(overridenOptions.sortImports(), expectedOptions.sortImports());
|
|
|
|
};
|
|
|
|
|
|
|
|
verify();
|
|
|
|
}
|
|
|
|
|
2025-01-24 12:51:04 +00:00
|
|
|
/*
|
|
|
|
* Create a temporary directory with the following structure
|
|
|
|
|--dir1
|
|
|
|
| |--.qmlformat.ini
|
|
|
|
| |-- test1.qml
|
|
|
|
|--dir2
|
|
|
|
| |-- test2.qml
|
|
|
|
|
|
|
|
* test2.qml should differ from the test2.qml options on indentwidth, because test1 gets it from
|
|
|
|
* its settings file.
|
|
|
|
*/
|
|
|
|
void TestQmlformat::multipleSettingsFiles()
|
|
|
|
{
|
|
|
|
QTemporaryDir tempDir;
|
|
|
|
QTemporaryDir dir1(tempDir.path() + "/dir1");
|
|
|
|
QTemporaryDir dir2(tempDir.path() + "/dir2");
|
|
|
|
const QString qmlformat1Ini = dir1.path() + "/.qmlformat.ini";
|
|
|
|
const QString test1Qml = dir1.path() + "/test.qml";
|
|
|
|
const QString test2Qml = dir2.path() + "/test.qml";
|
|
|
|
|
|
|
|
QFile::copy(testFile("iniFiles/dummySettingsFile.ini"), qmlformat1Ini);
|
|
|
|
QQmlFormatSettings settings("qmlformat");
|
|
|
|
QQmlFormatOptions options =
|
|
|
|
QQmlFormatOptions::buildCommandLineOptions(QStringList{ m_qmlformatPath });
|
|
|
|
auto test1Options = options.optionsForFile(test1Qml, &settings);
|
|
|
|
auto test2Options = options.optionsForFile(test2Qml, &settings);
|
|
|
|
|
|
|
|
QCOMPARE(test1Options.tabsEnabled(), test2Options.tabsEnabled());
|
|
|
|
QCOMPARE_NE(test1Options.indentWidth(), test2Options.indentWidth());
|
|
|
|
QCOMPARE(test1Options.maxColumnWidth(), test2Options.maxColumnWidth());
|
|
|
|
QCOMPARE(test1Options.normalizeEnabled(), test2Options.normalizeEnabled());
|
|
|
|
QCOMPARE(test1Options.newline(), test2Options.newline());
|
|
|
|
QCOMPARE(test1Options.objectsSpacing(), test2Options.objectsSpacing());
|
|
|
|
QCOMPARE(test1Options.functionsSpacing(), test2Options.functionsSpacing());
|
|
|
|
QCOMPARE(test1Options.sortImports(), test2Options.sortImports());
|
|
|
|
}
|
|
|
|
|
2020-09-02 10:34:29 +00:00
|
|
|
QString TestQmlformat::runQmlformat(const QString &fileToFormat, QStringList args,
|
2024-03-14 11:48:38 +00:00
|
|
|
bool shouldSucceed, RunOption rOptions, QStringView ext)
|
2019-12-13 15:10:46 +00:00
|
|
|
{
|
2020-03-18 08:19:31 +00:00
|
|
|
// Copy test file to temporary location
|
|
|
|
QTemporaryDir tempDir;
|
2024-03-14 11:48:38 +00:00
|
|
|
const QString tempFile = (tempDir.path() + QDir::separator() + "to_format") % ext;
|
2020-03-18 08:19:31 +00:00
|
|
|
|
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);
|
|
|
|
|
2024-03-23 19:41:57 +00:00
|
|
|
if (!temp.open(QIODevice::ReadOnly))
|
|
|
|
qFatal("Could not open %s", qPrintable(tempFile));
|
2020-03-18 08:19:31 +00:00
|
|
|
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)
|
|
|
|
{
|
2023-12-28 20:31:13 +00:00
|
|
|
auto env = DomEnvironment::create(
|
2021-09-15 07:38:49 +00:00
|
|
|
QStringList(), // as we load no dependencies we do not need any paths
|
|
|
|
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
|
|
|
|
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
|
|
|
|
DomItem tFile;
|
2024-01-24 13:39:30 +00:00
|
|
|
env->loadFile(FileToLoad::fromFileSystem(env, fileToFormat),
|
|
|
|
[&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
|
2024-01-08 16:52:18 +00:00
|
|
|
env->loadPendingDependencies();
|
2021-09-15 07:38:49 +00:00
|
|
|
MutableDomItem myFile = tFile.field(Fields::currentItem);
|
|
|
|
|
QQmlJS::Dom::OutWriter. Refactoring
The refactoring consists of:
- Changing writeOut & writeOutForFile API
to return boolean instead of MutableDomItem, which better reflects
the existing usecases improving consistency of the data model*
Moreover, previous API was exposing DomItem, which was not "committed
to base" (MutableDomItem.commitToBase()), meaning it was exposing the
"unmerged" Item alongside with the "temporary environment"
- Refactoring & renaming OutWriter::updatedFile
breaking it into smaller chunks preserving
only necessary functionality
- Adding some comments / documentation
Before this commit, the writeOut API was "exposing",so called,
"updatedFile", which is basically the copy of the original fileItem +
renewed scriptExpressions which were modified during the writeOut of
the original fileItem.
The idea behind the "mutating" Dom API is that one has to create a
MutableDomItem, do some changes to it and then "commit" them.
This process is also facilitated by the creation of separate Env.
(git analogy might be handy here:
We create a separate branch, where all the mutation will happen and then
we "merge" this branch)
However, in the writeOutForFile usecase this "updatedFile" was needed
only for the verifying of the consistency of the "writtenOut" DOM,
however the API was exposing it further back to the caller sites,
without "committing".
The potential issue here is inconsistency of the data Model.
On one side we have an original File Item owned by the Base Env,
on the other side we have an "updatedFile" which is owned by another Env.
Taking into account that there are no usecases requiring "exposing"
"updatedFile", but also no need for "committing" the changes,
It's arguably better to keep that temporary "updatedFile" locally,
not exposing it outside the writeOutForFile function. Thereby improving
consistency of the data model.
Change-Id: If45eca4b4d6d703e2a76d0580f124d0292af5ed8
Reviewed-by: Semih Yavuz <semih.yavuz@qt.io>
2023-12-05 09:42:23 +00:00
|
|
|
bool writtenOut;
|
2021-09-15 07:38:49 +00:00
|
|
|
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)
|
QQmlJS::Dom::OutWriter. Refactoring
The refactoring consists of:
- Changing writeOut & writeOutForFile API
to return boolean instead of MutableDomItem, which better reflects
the existing usecases improving consistency of the data model*
Moreover, previous API was exposing DomItem, which was not "committed
to base" (MutableDomItem.commitToBase()), meaning it was exposing the
"unmerged" Item alongside with the "temporary environment"
- Refactoring & renaming OutWriter::updatedFile
breaking it into smaller chunks preserving
only necessary functionality
- Adding some comments / documentation
Before this commit, the writeOut API was "exposing",so called,
"updatedFile", which is basically the copy of the original fileItem +
renewed scriptExpressions which were modified during the writeOut of
the original fileItem.
The idea behind the "mutating" Dom API is that one has to create a
MutableDomItem, do some changes to it and then "commit" them.
This process is also facilitated by the creation of separate Env.
(git analogy might be handy here:
We create a separate branch, where all the mutation will happen and then
we "merge" this branch)
However, in the writeOutForFile usecase this "updatedFile" was needed
only for the verifying of the consistency of the "writtenOut" DOM,
however the API was exposing it further back to the caller sites,
without "committing".
The potential issue here is inconsistency of the data Model.
On one side we have an original File Item owned by the Base Env,
on the other side we have an "updatedFile" which is owned by another Env.
Taking into account that there are no usecases requiring "exposing"
"updatedFile", but also no need for "committing" the changes,
It's arguably better to keep that temporary "updatedFile" locally,
not exposing it outside the writeOutForFile function. Thereby improving
consistency of the data model.
Change-Id: If45eca4b4d6d703e2a76d0580f124d0292af5ed8
Reviewed-by: Semih Yavuz <semih.yavuz@qt.io>
2023-12-05 09:42:23 +00:00
|
|
|
*didSucceed = writtenOut;
|
2021-09-15 07:38:49 +00:00
|
|
|
return resultStr;
|
|
|
|
}
|
|
|
|
|
2025-03-17 08:48:53 +00:00
|
|
|
void TestQmlformat::qml_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("file");
|
|
|
|
QTest::addColumn<QString>("fileFormatted");
|
|
|
|
QTest::addColumn<QStringList>("args");
|
|
|
|
QTest::addColumn<RunOption>("runOption");
|
|
|
|
|
|
|
|
QTest::newRow("example1") << "Example1.qml"
|
|
|
|
<< "Example1.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("annotation") << "Annotations.qml"
|
|
|
|
<< "Annotations.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
|
|
|
QTest::newRow("front inline") << "FrontInline.qml"
|
|
|
|
<< "FrontInline.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
|
|
|
QTest::newRow("if blocks") << "IfBlocks.qml"
|
|
|
|
<< "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 {} << 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 {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("QTBUG-85003") << "QtBug85003.qml"
|
|
|
|
<< "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 {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("property names")
|
|
|
|
<< "propertyNames.qml"
|
|
|
|
<< "propertyNames.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("empty object") << "emptyObject.qml"
|
|
|
|
<< "emptyObject.formatted.qml" << QStringList {}
|
|
|
|
<< RunOption::OnCopy;
|
|
|
|
QTest::newRow("arrow functions")
|
|
|
|
<< "arrowFunctions.qml"
|
|
|
|
<< "arrowFunctions.formatted.qml" << QStringList {} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("forWithLet")
|
|
|
|
<< "forWithLet.qml"
|
|
|
|
<< "forWithLet.formatted.qml" << QStringList {} << 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;
|
|
|
|
QTest::newRow("enumWithValues")
|
|
|
|
<< "enumWithValues.qml"
|
|
|
|
<< "enumWithValues.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("typeAnnotatedSignal")
|
|
|
|
<< "signal.qml"
|
|
|
|
<< "signal.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
|
|
|
//plainJS
|
|
|
|
QTest::newRow("nestedLambdaWithIfElse")
|
|
|
|
<< "lambdaWithIfElseInsideLambda.js"
|
|
|
|
<< "lambdaWithIfElseInsideLambda.formatted.js" << QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions")
|
|
|
|
<< "noSuperfluousSpaceInsertions.qml"
|
|
|
|
<< "noSuperfluousSpaceInsertions.formatted.qml" << QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions.fail_id")
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_id.qml"
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_id.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions.fail_QtObject")
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_QtObject.qml"
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_QtObject.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions.fail_signal")
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_signal.qml"
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_signal.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions.fail_enum")
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_enum.qml"
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_enum.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("noSuperfluousSpaceInsertions.fail_parameters")
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_parameters.qml"
|
|
|
|
<< "noSuperfluousSpaceInsertions.fail_parameters.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("nonInitializedPropertyInComponent")
|
|
|
|
<< "nonInitializedPropertyInComponent.qml"
|
|
|
|
<< "nonInitializedPropertyInComponent.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("fromAsIdentifier")
|
|
|
|
<< "fromAsIdentifier.qml"
|
|
|
|
<< "fromAsIdentifier.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
QTest::newRow("finalProperties")
|
|
|
|
<< "finalProperties.qml"
|
|
|
|
<< "finalProperties.formatted.qml"
|
|
|
|
<< QStringList{} << RunOption::OnCopy;
|
|
|
|
}
|
|
|
|
void TestQmlformat::qml()
|
|
|
|
{
|
|
|
|
QFETCH(QString, file);
|
|
|
|
QFETCH(QString, fileFormatted);
|
|
|
|
QFETCH(QStringList, args);
|
|
|
|
QFETCH(RunOption, runOption);
|
|
|
|
|
|
|
|
Q_UNUSED(args);
|
|
|
|
Q_UNUSED(runOption);
|
|
|
|
|
|
|
|
bool wasSuccessful;
|
|
|
|
LineWriterOptions opts;
|
|
|
|
opts.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
|
|
|
|
#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(fileFormatted);
|
|
|
|
QEXPECT_FAIL("noSuperfluousSpaceInsertions.fail_id",
|
|
|
|
"Not all cases have been covered yet (QTBUG-133315, QTBUG-123386)", Abort);
|
|
|
|
QEXPECT_FAIL("noSuperfluousSpaceInsertions.fail_QtObject",
|
|
|
|
"Not all cases have been covered yet (QTBUG-133315, QTBUG-123386)", Abort);
|
|
|
|
QEXPECT_FAIL("noSuperfluousSpaceInsertions.fail_signal",
|
|
|
|
"Not all cases have been covered yet (QTBUG-133315, QTBUG-123386)", Abort);
|
|
|
|
QEXPECT_FAIL("noSuperfluousSpaceInsertions.fail_enum",
|
|
|
|
"Not all cases have been covered yet (QTBUG-133315, QTBUG-123386)", Abort);
|
|
|
|
QEXPECT_FAIL("noSuperfluousSpaceInsertions.fail_parameters",
|
|
|
|
"Not all cases have been covered yet (QTBUG-133315, QTBUG-123386)", Abort);
|
|
|
|
QCOMPARE(output, exp);
|
|
|
|
}
|
|
|
|
|
2019-12-13 15:10:46 +00:00
|
|
|
QTEST_MAIN(TestQmlformat)
|
|
|
|
#include "tst_qmlformat.moc"
|