summaryrefslogtreecommitdiffstats
path: root/tests/auto/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/tools')
-rw-r--r--tests/auto/tools/moc/cxx-attributes.h103
-rw-r--r--tests/auto/tools/moc/cxx11-enums.h18
-rw-r--r--tests/auto/tools/moc/moc.pro3
-rw-r--r--tests/auto/tools/moc/parse-defines.h11
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp61
-rw-r--r--tests/auto/tools/qmakelib/qmakelib.pro2
-rw-r--r--tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected68
-rw-r--r--tests/auto/tools/rcc/tst_rcc.cpp230
-rw-r--r--tests/auto/tools/uic/tst_uic.cpp166
9 files changed, 512 insertions, 150 deletions
diff --git a/tests/auto/tools/moc/cxx-attributes.h b/tests/auto/tools/moc/cxx-attributes.h
new file mode 100644
index 0000000000..eff6a3ec41
--- /dev/null
+++ b/tests/auto/tools/moc/cxx-attributes.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef CXXATTRIBUTE_H
+#define CXXATTRIBUTE_H
+
+#include <QtCore/QObject>
+
+class CppAttribute : public QObject
+{
+ Q_OBJECT
+signals:
+ [[deprecated]] void deprecatedSignal();
+
+public slots:
+ [[deprecated]] void deprecatedSlot() {}
+ [[deprecated]] [[tst_moc::maybe_unused]] int deprecatedSlot2() { return 42; }
+ [[deprecated("reason")]] void deprecatedReason() {}
+ [[deprecated("reason[")]] void deprecatedReasonWithLBRACK() {}
+ [[deprecated("reason[[")]] void deprecatedReasonWith2LBRACK() {}
+ [[deprecated("reason]")]] void deprecatedReasonWithRBRACK() {}
+ [[deprecated("reason]]")]] void deprecatedReasonWith2RBRACK() {}
+ void slotWithArguments([[tst_moc::maybe_unused]] int) {}
+#if !defined(_MSC_VER) || _MSC_VER >= 1912
+ // On MSVC it causes:
+ // moc_cxx-attributes.cpp(133): fatal error C1001: An internal error has occurred in the compiler.
+ Q_INVOKABLE [[tst_moc::noreturn]] void noreturnSlot() { throw "unused"; }
+ [[tst_moc::noreturn]] Q_SCRIPTABLE void noreturnSlot2() { throw "unused"; }
+ [[deprecated]] int returnInt() { return 0; }
+ Q_SLOT [[tst_moc::noreturn]] [[deprecated]] void noreturnDeprecatedSlot() { throw "unused"; }
+ Q_INVOKABLE void noreturnSlot3() [[tst_moc::noreturn]] { throw "unused"; }
+#endif
+};
+
+#ifdef Q_MOC_RUN
+# define TEST_COMPILER_DEPRECATION [[deprecated]]
+# define TEST_COMPILER_DEPRECATION_X(x) [[deprecated(x)]]
+#else
+# define TEST_COMPILER_DEPRECATION Q_DECL_ENUMERATOR_DEPRECATED
+# define TEST_COMPILER_DEPRECATION_X(x) Q_DECL_ENUMERATOR_DEPRECATED_X(x)
+#endif
+
+namespace TestQNamespaceDeprecated {
+ Q_NAMESPACE
+ enum class TestEnum1 {
+ Key1 = 11,
+ Key2 TEST_COMPILER_DEPRECATION,
+ Key3 TEST_COMPILER_DEPRECATION_X("reason"),
+ Key4 TEST_COMPILER_DEPRECATION_X("reason["),
+ Key5 TEST_COMPILER_DEPRECATION_X("reason[["),
+ Key6 TEST_COMPILER_DEPRECATION_X("reason]"),
+ Key7 TEST_COMPILER_DEPRECATION_X("reason]]"),
+ };
+ Q_ENUM_NS(TestEnum1)
+
+ // try to dizzy moc by adding a struct in between
+ struct TestGadget {
+ Q_GADGET
+ public:
+ enum class TestGEnum1 {
+ Key1 = 13,
+ Key2 TEST_COMPILER_DEPRECATION,
+ Key3 TEST_COMPILER_DEPRECATION_X("reason")
+ };
+ Q_ENUM(TestGEnum1)
+ };
+
+ enum class TestFlag1 {
+ None = 0,
+ Flag1 = 1,
+ Flag2 TEST_COMPILER_DEPRECATION = 2,
+ Flag3 TEST_COMPILER_DEPRECATION_X("reason") = 3,
+ Any = Flag1 | Flag2 | Flag3
+ };
+ Q_FLAG_NS(TestFlag1)
+}
+
+#endif // CXXATTRIBUTE_H
diff --git a/tests/auto/tools/moc/cxx11-enums.h b/tests/auto/tools/moc/cxx11-enums.h
index cc14c0acda..d5bd228f12 100644
--- a/tests/auto/tools/moc/cxx11-enums.h
+++ b/tests/auto/tools/moc/cxx11-enums.h
@@ -30,7 +30,6 @@
#define CXX11_ENUMS_H
#include <QtCore/QObject>
-#if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_MOC_RUN)
class CXX11Enums
{
Q_GADGET
@@ -73,21 +72,4 @@ public:
Q_FLAGS(ClassFlags)
};
-#else
-//workaround to get the moc compiled code to compile
-class CXX11Enums
-{
- Q_GADGET
-public:
- struct EnumClass { enum { A0, A1, A2, A3 }; };
- struct TypedEnumClass { enum { C0, C1, C2, C3 }; };
- enum NormalEnum { D2 = 2, D3, D0 =0 , D1 };
- enum TypedEnum { B0, B1 , B2, B3 };
-};
-
-class CXX11Enums2 : public CXX11Enums
-{
- Q_GADGET
-};
-#endif
#endif // CXX11_ENUMS_H
diff --git a/tests/auto/tools/moc/moc.pro b/tests/auto/tools/moc/moc.pro
index 3cbc9ebb5e..ad8c093add 100644
--- a/tests/auto/tools/moc/moc.pro
+++ b/tests/auto/tools/moc/moc.pro
@@ -29,7 +29,8 @@ HEADERS += using-namespaces.h no-keywords.h task87883.h c-comments.h backslash-n
non-gadget-parent-class.h grand-parent-gadget-class.h \
related-metaobjects-in-gadget.h \
related-metaobjects-name-conflict.h \
- namespace.h cxx17-namespaces.h
+ namespace.h cxx17-namespaces.h \
+ cxx-attributes.h
if(*-g++*|*-icc*|*-clang*|*-llvm):!win32-*: HEADERS += os9-newlines.h win-newlines.h
diff --git a/tests/auto/tools/moc/parse-defines.h b/tests/auto/tools/moc/parse-defines.h
index 7b0fa29d7c..bd22b0b9af 100644
--- a/tests/auto/tools/moc/parse-defines.h
+++ b/tests/auto/tools/moc/parse-defines.h
@@ -51,7 +51,6 @@
#define PD_CLASSINFO Q_CLASSINFO
-#if defined(Q_COMPILER_VARIADIC_MACROS) || defined (Q_MOC_RUN)
#define PD_VARARG(x, ...) x(__VA_ARGS__)
#if defined(Q_CC_GNU) || defined(Q_MOC_RUN)
@@ -61,7 +60,6 @@
#define PD_VARARGEXT(x, ...) x(__VA_ARGS__)
#endif
-#endif
#define PD_ADD_SUFFIX(x) PD_DEFINE1(x,_SUFFIX)
#define PD_DEFINE_ITSELF PD_ADD_SUFFIX(PD_DEFINE_ITSELF)
@@ -100,7 +98,6 @@ public slots:
PD_TEST_IDENTIFIER_ARG(void, combined6()) {}
-#if defined(Q_COMPILER_VARIADIC_MACROS) || defined (Q_MOC_RUN)
PD_VARARG(void vararg1) {}
PD_VARARG(void vararg2, int) {}
PD_VARARG(void vararg3, int, int) {}
@@ -108,14 +105,6 @@ public slots:
PD_VARARGEXT(void vararg4) {}
PD_VARARGEXT(void vararg5, int) {}
PD_VARARGEXT(void vararg6, int, int) {}
-#else
- void vararg1() {}
- void vararg2(int) {}
- void vararg3(int,int) {}
- void vararg4() {}
- void vararg5(int) {}
- void vararg6(int,int) {}
-#endif
#define OUTERFUNCTION(x) x
#define INNERFUNCTION(x) OUTERFUNCTION(x)
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
index 50321c322b..ec4a44e672 100644
--- a/tests/auto/tools/moc/tst_moc.cpp
+++ b/tests/auto/tools/moc/tst_moc.cpp
@@ -71,6 +71,7 @@
#include "grand-parent-gadget-class.h"
#include "namespace.h"
#include "cxx17-namespaces.h"
+#include "cxx-attributes.h"
#ifdef Q_MOC_RUN
// check that moc can parse these constructs, they are being used in Windows winsock2.h header
@@ -704,6 +705,7 @@ private slots:
void optionsFileError();
void testQNamespace();
void cxx17Namespaces();
+ void cxxAttributes();
signals:
void sigWithUnsignedArg(unsigned foo);
@@ -1424,6 +1426,16 @@ void tst_Moc::environmentIncludePaths()
// plugin_metadata.h contains a plugin which we register here. Since we're not building this
// application as a plugin, we need top copy some of the initializer code found in qplugin.h:
extern "C" QObject *qt_plugin_instance();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+extern "C" QPluginMetaData qt_plugin_query_metadata();
+class StaticPluginInstance{
+public:
+ StaticPluginInstance() {
+ QStaticPlugin plugin(qt_plugin_instance, qt_plugin_query_metadata);
+ qRegisterStaticPluginFunction(plugin);
+ }
+};
+#else
extern "C" const char *qt_plugin_query_metadata();
class StaticPluginInstance{
public:
@@ -1432,6 +1444,7 @@ public:
qRegisterStaticPluginFunction(plugin);
}
};
+#endif
static StaticPluginInstance staticInstance;
void tst_Moc::specifyMetaTagsFromCmdline() {
@@ -3848,6 +3861,14 @@ static void checkEnum(const QMetaEnum &enumerator, const QByteArray &name, const
}
}
+class EnumFromNamespaceClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(FooNamespace::Enum1 prop READ prop CONSTANT)
+public:
+ FooNamespace::Enum1 prop() { return FooNamespace::Enum1::Key2; }
+};
+
void tst_Moc::testQNamespace()
{
QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 4);
@@ -3875,6 +3896,11 @@ void tst_Moc::testQNamespace()
QCOMPARE(FooNamespace::staticMetaObject.enumeratorCount(), 1);
QCOMPARE(FooNamespace::FooNestedNamespace::staticMetaObject.enumeratorCount(), 2);
QCOMPARE(FooNamespace::FooNestedNamespace::FooMoreNestedNamespace::staticMetaObject.enumeratorCount(), 1);
+
+ EnumFromNamespaceClass obj;
+ const QVariant prop = obj.property("prop");
+ QCOMPARE(prop.type(), QMetaType::Int);
+ QCOMPARE(prop.toInt(), int(FooNamespace::Enum1::Key2));
}
void tst_Moc::cxx17Namespaces()
@@ -3896,6 +3922,41 @@ void tst_Moc::cxx17Namespaces()
QCOMPARE(QMetaEnum::fromType<CXX17Namespace::A::B::C::D::ClassInNamespace::GadEn>().value(0), 3);
}
+void tst_Moc::cxxAttributes()
+{
+ auto so = CppAttribute::staticMetaObject;
+ QCOMPARE(so.className(), "CppAttribute");
+ QCOMPARE(so.enumeratorCount(), 0);
+ QVERIFY(so.indexOfSignal("deprecatedSignal") != 1);
+ for (auto a: {"deprecatedSlot", "deprecatedSlot2", "deprecatedReason", "deprecatedReasonWithLBRACK",
+ "deprecatedReasonWith2LBRACK", "deprecatedReasonWithRBRACK", "deprecatedReasonWith2RBRACK",
+ "slotWithArguments"
+#if !defined(_MSC_VER) || _MSC_VER >= 1912
+ , "noreturnSlot", "noreturnSlot2", "returnInt", "noreturnDeprecatedSlot",
+ "noreturnSlot3"
+#endif
+ }) {
+ QVERIFY(so.indexOfSlot(a) != 1);
+ }
+
+ QCOMPARE(TestQNamespaceDeprecated::staticMetaObject.enumeratorCount(), 2);
+ checkEnum(TestQNamespaceDeprecated::staticMetaObject.enumerator(0), "TestEnum1",
+ {{"Key1", 11}, {"Key2", 12}, {"Key3", 13}, {"Key4", 14}, {"Key5", 15}, {"Key6", 16},
+ {"Key7", 17}});
+ checkEnum(TestQNamespaceDeprecated::staticMetaObject.enumerator(1), "TestFlag1",
+ {{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Flag3", 3}, {"Any", 1 | 2 | 3}});
+
+ QCOMPARE(TestQNamespaceDeprecated::TestGadget::staticMetaObject.enumeratorCount(), 1);
+ checkEnum(TestQNamespaceDeprecated::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1",
+ {{"Key1", 13}, {"Key2", 14}, {"Key3", 15}});
+
+ QMetaEnum meta = QMetaEnum::fromType<TestQNamespaceDeprecated::TestEnum1>();
+ QVERIFY(meta.isValid());
+ QCOMPARE(meta.name(), "TestEnum1");
+ QCOMPARE(meta.enclosingMetaObject(), &TestQNamespaceDeprecated::staticMetaObject);
+ QCOMPARE(meta.keyCount(), 7);
+}
+
QTEST_MAIN(tst_Moc)
// the generated code must compile with QT_NO_KEYWORDS
diff --git a/tests/auto/tools/qmakelib/qmakelib.pro b/tests/auto/tools/qmakelib/qmakelib.pro
index 29f17f6a14..5e9e9fe637 100644
--- a/tests/auto/tools/qmakelib/qmakelib.pro
+++ b/tests/auto/tools/qmakelib/qmakelib.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
TARGET = tst_qmakelib
QT = core testlib
-win32: LIBS += -ladvapi32
+win32: QMAKE_USE += advapi32
INCLUDEPATH += ../../../../qmake/library
VPATH += ../../../../qmake/library
diff --git a/tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected
new file mode 100644
index 0000000000..9dcd131af5
--- /dev/null
+++ b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected
@@ -0,0 +1,68 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 5.14.0
+# WARNING! All changes made in this file will be lost!
+
+from PySide2 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x00\x02\
+0\
+1\
+\x00\x00\x00#\
+0\
+123456789 012345\
+6789 0123456789 \
+12\
+\x00\x00\x00\x01\
+@\
+\
+\x00\x00\x00\x00\
+\
+"
+
+qt_resource_name = b"\
+\x00\x04\
+\x00\x06\xa8\xa1\
+\x00d\
+\x00a\x00t\x00a\
+\x00\x0a\
+\x04\x08\x0a\xb4\
+\x00d\
+\x00a\x00t\x00a\x00-\x002\x00.\x00t\x00x\x00t\
+\x00\x0b\
+\x00\xb5Ot\
+\x00d\
+\x00a\x00t\x00a\x00-\x003\x005\x00.\x00t\x00x\x00t\
+\x00\x0a\
+\x04\x11\x0a\xb4\
+\x00d\
+\x00a\x00t\x00a\x00-\x001\x00.\x00t\x00x\x00t\
+\x00\x0a\
+\x04\x0e\x0a\xb4\
+\x00d\
+\x00a\x00t\x00a\x00-\x000\x00.\x00t\x00x\x00t\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00(\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06\
+IGNORE: (time stamp)
+\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+IGNORE: (time stamp)
+\x00\x00\x00^\x00\x00\x00\x00\x00\x01\x00\x00\x002\
+IGNORE: (time stamp)
+\x00\x00\x00D\x00\x00\x00\x00\x00\x01\x00\x00\x00-\
+IGNORE: (time stamp)
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/tests/auto/tools/rcc/tst_rcc.cpp b/tests/auto/tools/rcc/tst_rcc.cpp
index c810c6e364..24fd79cf19 100644
--- a/tests/auto/tools/rcc/tst_rcc.cpp
+++ b/tests/auto/tools/rcc/tst_rcc.cpp
@@ -46,6 +46,33 @@
typedef QMap<QString, QString> QStringMap;
Q_DECLARE_METATYPE(QStringMap)
+static QByteArray msgProcessStartFailed(const QProcess &p)
+{
+ const QString result = QLatin1String("Could not start \"")
+ + QDir::toNativeSeparators(p.program()) + QLatin1String("\": ")
+ + p.errorString();
+ return result.toLocal8Bit();
+}
+
+static QByteArray msgProcessTimeout(const QProcess &p)
+{
+ return '"' + QDir::toNativeSeparators(p.program()).toLocal8Bit()
+ + "\" timed out.";
+}
+
+static QByteArray msgProcessCrashed(QProcess &p)
+{
+ return '"' + QDir::toNativeSeparators(p.program()).toLocal8Bit()
+ + "\" crashed.\n" + p.readAllStandardError();
+}
+
+static QByteArray msgProcessFailed(QProcess &p)
+{
+ return '"' + QDir::toNativeSeparators(p.program()).toLocal8Bit()
+ + "\" returned " + QByteArray::number(p.exitCode()) + ":\n"
+ + p.readAllStandardError();
+}
+
class tst_rcc : public QObject
{
Q_OBJECT
@@ -62,10 +89,13 @@ private slots:
void readback_data();
void readback();
+ void python();
+
void cleanupTestCase();
private:
QString m_rcc;
+ QString m_dataPath;
};
void tst_rcc::initTestCase()
@@ -74,20 +104,19 @@ void tst_rcc::initTestCase()
// we must force a certain hash order when testing or tst_rcc will fail, see QTBUG-25078
QVERIFY(qputenv("QT_RCC_TEST", "1"));
m_rcc = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/rcc");
-}
-QString findExpectedFile(const QString &base)
-{
- QString expectedrccfile = base;
+ m_dataPath = QFINDTESTDATA("data");
+ QVERIFY(!m_dataPath.isEmpty());
+}
- // Must be updated with each minor release.
- if (QFileInfo(expectedrccfile + QLatin1String(".450")).exists())
- expectedrccfile += QLatin1String(".450");
- return expectedrccfile;
+static inline bool isPythonComment(const QString &line)
+{
+ return line.startsWith(QLatin1Char('#'));
}
-static QString doCompare(const QStringList &actual, const QStringList &expected)
+static QString doCompare(const QStringList &actual, const QStringList &expected,
+ const QString &timeStampPath)
{
if (actual.size() != expected.size()) {
return QString("Length count different: actual: %1, expected: %2")
@@ -95,13 +124,21 @@ static QString doCompare(const QStringList &actual, const QStringList &expected)
}
QByteArray ba;
+ const bool isPython = isPythonComment(expected.constFirst());
for (int i = 0, n = expected.size(); i != n; ++i) {
QString expectedLine = expected.at(i);
if (expectedLine.startsWith("IGNORE:"))
continue;
+ if (isPython && isPythonComment(expectedLine) && isPythonComment(actual.at(i)))
+ continue;
if (expectedLine.startsWith("TIMESTAMP:")) {
const QString relativePath = expectedLine.mid(strlen("TIMESTAMP:"));
- const quint64 timeStamp = QFileInfo(relativePath).lastModified().toMSecsSinceEpoch();
+ const QFileInfo fi(timeStampPath + QLatin1Char('/') + relativePath);
+ if (!fi.isFile()) {
+ ba.append("File " + fi.absoluteFilePath().toUtf8() + " does not exist!");
+ break;
+ }
+ const quint64 timeStamp = quint64(fi.lastModified().toMSecsSinceEpoch());
expectedLine.clear();
for (int shift = 56; shift >= 0; shift -= 8) {
expectedLine.append(QLatin1String("0x"));
@@ -110,7 +147,7 @@ static QString doCompare(const QStringList &actual, const QStringList &expected)
}
}
if (expectedLine != actual.at(i)) {
- qDebug() << "LINES" << i << "DIFFER";
+ qDebug() << "LINES" << (i + 1) << "DIFFER";
ba.append(
"\n<<<<<< actual\n" + actual.at(i) + "\n======\n" + expectedLine
+ "\n>>>>>> expected\n"
@@ -126,49 +163,53 @@ void tst_rcc::rcc_data()
QTest::addColumn<QString>("qrcfile");
QTest::addColumn<QString>("expected");
- QString dataPath = QFINDTESTDATA("data/images/");
- if (dataPath.isEmpty())
- QFAIL("data path not found");
- QTest::newRow("images") << dataPath << "images.qrc" << "images.expected";
+ const QString imagesPath = m_dataPath + QLatin1String("/images");
+ QTest::newRow("images") << imagesPath << "images.qrc" << "images.expected";
- QString sizesPath = QFINDTESTDATA("data/sizes/");
- if (sizesPath.isEmpty())
- QFAIL("data path not found");
+ const QString sizesPath = m_dataPath + QLatin1String("/sizes");
QTest::newRow("size-0") << sizesPath << "size-0.qrc" << "size-0.expected";
QTest::newRow("size-1") << sizesPath << "size-1.qrc" << "size-1.expected";
QTest::newRow("size-2-0-35-1") << sizesPath << "size-2-0-35-1.qrc" << "size-2-0-35-1.expected";
}
+static QStringList readLinesFromFile(const QString &fileName,
+ QString::SplitBehavior splitBehavior)
+{
+ QFile file(fileName);
+
+ bool ok = file.open(QIODevice::ReadOnly | QIODevice::Text);
+ if (!ok) {
+ QWARN(qPrintable(QString::fromLatin1("Could not open testdata file %1: %2")
+ .arg(fileName, file.errorString())));
+ }
+
+ return QString::fromUtf8(file.readAll()).split(QLatin1Char('\n'), splitBehavior);
+}
+
void tst_rcc::rcc()
{
QFETCH(QString, directory);
QFETCH(QString, qrcfile);
QFETCH(QString, expected);
- if (!QDir::setCurrent(directory)) {
- QString message = QString::fromLatin1("Unable to cd from '%1' to '%2'").arg(QDir::currentPath(), directory);
- QFAIL(qPrintable(message));
- }
-
// If the file expectedoutput.txt exists, compare the
// console output with the content of that file
- const QString expected2 = findExpectedFile(expected);
- QFile expectedFile(expected2);
- if (!expectedFile.exists()) {
- qDebug() << "NO EXPECTATIONS? " << expected2;
- return;
- }
// Launch; force no compression, otherwise the output would be different
// depending on the compression algorithm we're using
QProcess process;
+ process.setWorkingDirectory(directory);
process.start(m_rcc, { "-no-compress", qrcfile });
+ QVERIFY2(process.waitForStarted(), msgProcessStartFailed(process).constData());
if (!process.waitForFinished()) {
- const QString path = QString::fromLocal8Bit(qgetenv("PATH"));
- QString message = QString::fromLatin1("'%1' could not be found when run from '%2'. Path: '%3' ").
- arg(m_rcc, QDir::currentPath(), path);
- QFAIL(qPrintable(message));
+ process.kill();
+ QFAIL(msgProcessTimeout(process).constData());
}
+ QVERIFY2(process.exitStatus() == QProcess::NormalExit,
+ msgProcessCrashed(process).constData());
+ QVERIFY2(process.exitCode() == 0,
+ msgProcessFailed(process).constData());
+
const QChar cr = QLatin1Char('\r');
const QString err = QString::fromLocal8Bit(process.readAllStandardError()).remove(cr);
const QString out = QString::fromLatin1(process.readAllStandardOutput()).remove(cr);
@@ -181,63 +222,20 @@ void tst_rcc::rcc()
const QChar nl = QLatin1Char('\n');
const QStringList actualLines = out.split(nl);
- QVERIFY(expectedFile.open(QIODevice::ReadOnly|QIODevice::Text));
- const QStringList expectedLines = QString::fromLatin1(expectedFile.readAll()).split(nl);
+ const QStringList expectedLines =
+ readLinesFromFile(directory + QLatin1Char('/') + expected, QString::KeepEmptyParts);
+ QVERIFY(!expectedLines.isEmpty());
- const QString diff = doCompare(actualLines, expectedLines);
+ const QString diff = doCompare(actualLines, expectedLines, directory);
if (diff.size())
QFAIL(qPrintable(diff));
}
-
-
-static void createRccBinaryData(const QString &rcc, const QString &baseDir,
- const QString &qrcFileName, const QString &rccFileName)
-{
- QString currentDir = QDir::currentPath();
- QDir::setCurrent(baseDir);
-
- // same as above: force no compression
- QProcess rccProcess;
- rccProcess.start(rcc, { "-binary", "-no-compress", "-o", rccFileName, qrcFileName });
- bool ok = rccProcess.waitForFinished();
- if (!ok) {
- QString errorString = QString::fromLatin1("Could not start rcc (is it in PATH?): %1").arg(rccProcess.errorString());
- QFAIL(qPrintable(errorString));
- }
-
- QByteArray output = rccProcess.readAllStandardOutput();
- if (!output.isEmpty()) {
- QString errorMessage = QString::fromLatin1("rcc stdout: %1").arg(QString::fromLocal8Bit(output));
- QWARN(qPrintable(errorMessage));
- }
-
- output = rccProcess.readAllStandardError();
- if (!output.isEmpty()) {
- QString errorMessage = QString::fromLatin1("rcc stderr: %1").arg(QString::fromLocal8Bit(output));
- QWARN(qPrintable(errorMessage));
- }
-
- QDir::setCurrent(currentDir);
-}
-
-static QStringList readLinesFromFile(const QString &fileName)
-{
- QFile file(fileName);
-
- bool ok = file.open(QIODevice::ReadOnly | QIODevice::Text);
- if (!ok)
- QWARN(qPrintable(QString::fromLatin1("Could not open testdata file %1: %2").arg(fileName, file.errorString())));
-
- QStringList lines = QString::fromUtf8(file.readAll()).split(QLatin1Char('\n'), QString::SkipEmptyParts);
- return lines;
-}
-
static QStringMap readExpectedFiles(const QString &fileName)
{
QStringMap expectedFiles;
- QStringList lines = readLinesFromFile(fileName);
+ QStringList lines = readLinesFromFile(fileName, QString::SkipEmptyParts);
foreach (const QString &line, lines) {
QString resourceFileName = line.section(QLatin1Char(' '), 0, 0, QString::SectionSkipEmpty);
QString actualFileName = line.section(QLatin1Char(' '), 1, 1, QString::SectionSkipEmpty);
@@ -268,9 +266,7 @@ void tst_rcc::binary_data()
QTest::addColumn<QString>("baseDirectory");
QTest::addColumn<QStringMap>("expectedFiles");
- QString dataPath = QFINDTESTDATA("data/binary/");
- if (dataPath.isEmpty())
- QFAIL("data path not found");
+ QString dataPath = m_dataPath + QLatin1String("/binary/");
QDirIterator iter(dataPath, QStringList() << QLatin1String("*.qrc"));
while (iter.hasNext())
@@ -279,12 +275,33 @@ void tst_rcc::binary_data()
QFileInfo qrcFileInfo = iter.fileInfo();
QString absoluteBaseName = QFileInfo(qrcFileInfo.absolutePath(), qrcFileInfo.baseName()).absoluteFilePath();
QString rccFileName = absoluteBaseName + QLatin1String(".rcc");
- createRccBinaryData(m_rcc, dataPath, qrcFileInfo.absoluteFilePath(), rccFileName);
+
+ // same as above: force no compression
+ QProcess rccProcess;
+ rccProcess.setWorkingDirectory(dataPath);
+ rccProcess.start(m_rcc, { "-binary", "-no-compress", "-o", rccFileName, qrcFileInfo.absoluteFilePath() });
+ QVERIFY2(rccProcess.waitForStarted(), msgProcessStartFailed(rccProcess).constData());
+ if (!rccProcess.waitForFinished()) {
+ rccProcess.kill();
+ QFAIL(msgProcessTimeout(rccProcess).constData());
+ }
+ QVERIFY2(rccProcess.exitStatus() == QProcess::NormalExit,
+ msgProcessCrashed(rccProcess).constData());
+ QVERIFY2(rccProcess.exitCode() == 0,
+ msgProcessFailed(rccProcess).constData());
+
+ QByteArray output = rccProcess.readAllStandardOutput();
+ if (!output.isEmpty())
+ qWarning("rcc stdout: %s", output.constData());
+
+ output = rccProcess.readAllStandardError();
+ if (!output.isEmpty())
+ qWarning("rcc stderr: %s", output.constData());
QString localeFileName = absoluteBaseName + QLatin1String(".locale");
QFile localeFile(localeFileName);
if (localeFile.exists()) {
- QStringList locales = readLinesFromFile(localeFileName);
+ QStringList locales = readLinesFromFile(localeFileName, QString::SkipEmptyParts);
foreach (const QString &locale, locales) {
QString expectedFileName = QString::fromLatin1("%1.%2.%3").arg(absoluteBaseName, locale, QLatin1String("expected"));
QStringMap expectedFiles = readExpectedFiles(expectedFileName);
@@ -386,16 +403,12 @@ void tst_rcc::readback()
QFETCH(QString, resourceName);
QFETCH(QString, fileSystemName);
- QString dataPath = QFINDTESTDATA("data/");
- if (dataPath.isEmpty())
- QFAIL("data path not found");
-
QFile resourceFile(resourceName);
QVERIFY(resourceFile.open(QIODevice::ReadOnly));
QByteArray resourceData = resourceFile.readAll();
resourceFile.close();
- QFile fileSystemFile(dataPath + fileSystemName);
+ QFile fileSystemFile(m_dataPath + QLatin1Char('/') + fileSystemName);
QVERIFY(fileSystemFile.open(QIODevice::ReadOnly));
QByteArray fileSystemData = fileSystemFile.readAll();
fileSystemFile.close();
@@ -403,12 +416,39 @@ void tst_rcc::readback()
QCOMPARE(resourceData, fileSystemData);
}
+void tst_rcc::python()
+{
+ const QString path = m_dataPath + QLatin1String("/sizes");
+ const QString testFileRoot = path + QLatin1String("/size-2-0-35-1");
+ const QString qrcFile = testFileRoot + QLatin1String(".qrc");
+ const QString expectedFile = testFileRoot + QLatin1String("_python.expected");
+ const QString actualFile = testFileRoot + QLatin1String(".rcc");
+
+ QProcess process;
+ process.setWorkingDirectory(path);
+ process.start(m_rcc, { "-g", "python", "-o", actualFile, qrcFile});
+ QVERIFY2(process.waitForStarted(), msgProcessStartFailed(process).constData());
+ if (!process.waitForFinished()) {
+ process.kill();
+ QFAIL(msgProcessTimeout(process).constData());
+ }
+ QVERIFY2(process.exitStatus() == QProcess::NormalExit,
+ msgProcessCrashed(process).constData());
+ QVERIFY2(process.exitCode() == 0,
+ msgProcessFailed(process).constData());
+
+ const auto actualLines = readLinesFromFile(actualFile, QString::KeepEmptyParts);
+ QVERIFY(!actualLines.isEmpty());
+ const auto expectedLines = readLinesFromFile(expectedFile, QString::KeepEmptyParts);
+ QVERIFY(!expectedLines.isEmpty());
+ const QString diff = doCompare(actualLines, expectedLines, path);
+ if (!diff.isEmpty())
+ QFAIL(qPrintable(diff));
+}
+
void tst_rcc::cleanupTestCase()
{
- QString dataPath = QFINDTESTDATA("data/binary/");
- if (dataPath.isEmpty())
- return;
- QDir dataDir(dataPath);
+ QDir dataDir(m_dataPath + QLatin1String("/binary"));
QFileInfoList entries = dataDir.entryInfoList(QStringList() << QLatin1String("*.rcc"));
foreach (const QFileInfo &entry, entries)
QFile::remove(entry.absoluteFilePath());
diff --git a/tests/auto/tools/uic/tst_uic.cpp b/tests/auto/tools/uic/tst_uic.cpp
index f9553028fe..5b2f1f008b 100644
--- a/tests/auto/tools/uic/tst_uic.cpp
+++ b/tests/auto/tools/uic/tst_uic.cpp
@@ -36,17 +36,27 @@
#include <QtCore/QTemporaryDir>
#include <QtCore/QRegularExpression>
#include <QtCore/QStandardPaths>
+#include <QtCore/QVector>
#include <cstdio>
static const char keepEnvVar[] = "UIC_KEEP_GENERATED_FILES";
static const char diffToStderrEnvVar[] = "UIC_STDERR_DIFF";
+struct TestEntry
+{
+ QByteArray name;
+ QString baselineBaseName;
+ QString generatedFileName;
+};
+
class tst_uic : public QObject
{
Q_OBJECT
public:
+ using TestEntries = QVector<TestEntry>;
+
tst_uic();
private Q_SLOTS:
@@ -63,13 +73,20 @@ private Q_SLOTS:
void compare();
void compare_data() const;
+ void python();
+ void python_data() const;
+
void runCompare();
private:
+ void populateTestEntries();
+
const QString m_command;
QString m_baseline;
QTemporaryDir m_generated;
+ TestEntries m_testEntries;
QRegularExpression m_versionRegexp;
+ QString m_python;
};
tst_uic::tst_uic()
@@ -85,6 +102,32 @@ static QByteArray msgProcessStartFailed(const QString &command, const QString &w
return result.toLocal8Bit();
}
+// Locate Python and check whether PySide2 is installed
+static QString locatePython(QTemporaryDir &generatedDir)
+{
+ QString python = QStandardPaths::findExecutable(QLatin1String("python"));
+ if (python.isEmpty()) {
+ qWarning("Cannot locate python, skipping tests");
+ return QString();
+ }
+ QFile importTestFile(generatedDir.filePath(QLatin1String("import_test.py")));
+ if (!importTestFile.open(QIODevice::WriteOnly| QIODevice::Text))
+ return QString();
+ importTestFile.write("import PySide2.QtCore\n");
+ importTestFile.close();
+ QProcess process;
+ process.start(python, {importTestFile.fileName()});
+ if (!process.waitForStarted() || !process.waitForFinished())
+ return QString();
+ if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) {
+ const QString stdErr = QString::fromLocal8Bit(process.readAllStandardError()).simplified();
+ qWarning("PySide2 is not installed (%s)", qPrintable(stdErr));
+ return QString();
+ }
+ importTestFile.remove();
+ return python;
+}
+
void tst_uic::initTestCase()
{
QVERIFY2(m_generated.isValid(), qPrintable(m_generated.errorString()));
@@ -105,7 +148,28 @@ void tst_uic::initTestCase()
arg(QDir::currentPath());
if (!outLines.empty())
msg += outLines.front();
+ populateTestEntries();
+ QVERIFY(!m_testEntries.isEmpty());
qDebug("%s", qPrintable(msg));
+
+ m_python = locatePython(m_generated);
+}
+
+void tst_uic::populateTestEntries()
+{
+ const QString generatedPrefix = m_generated.path() + QLatin1Char('/');
+ QDir baseline(m_baseline);
+ const QString baseLinePrefix = baseline.path() + QLatin1Char('/');
+ const QFileInfoList baselineFiles =
+ baseline.entryInfoList(QStringList(QString::fromLatin1("*.ui")), QDir::Files);
+ m_testEntries.reserve(baselineFiles.size());
+ for (const QFileInfo &baselineFile : baselineFiles) {
+ const QString baseName = baselineFile.baseName();
+ const QString baselineBaseName = baseLinePrefix + baseName;
+ const QString generatedFile = generatedPrefix + baselineFile.fileName()
+ + QLatin1String(".h");
+ m_testEntries.append(TestEntry{baseName.toLocal8Bit(), baselineBaseName, generatedFile});
+ }
}
static const char helpFormat[] = R"(
@@ -171,22 +235,12 @@ void tst_uic::run_data() const
QTest::addColumn<QString>("generatedFile");
QTest::addColumn<QStringList>("options");
- QDir generated(m_generated.path());
- QDir baseline(m_baseline);
- const QFileInfoList baselineFiles = baseline.entryInfoList(QStringList("*.ui"), QDir::Files);
- foreach (const QFileInfo &baselineFile, baselineFiles) {
- const QString generatedFile = generated.absolutePath()
- + QLatin1Char('/') + baselineFile.fileName()
- + QLatin1String(".h");
-
+ for (const TestEntry &te : m_testEntries) {
QStringList options;
- if (baselineFile.fileName() == QLatin1String("qttrid.ui"))
+ if (te.name == QByteArrayLiteral("qttrid"))
options << QStringList(QLatin1String("-idbased"));
-
- QTest::newRow(qPrintable(baselineFile.baseName()))
- << baselineFile.absoluteFilePath()
- << generatedFile
- << options;
+ QTest::newRow(te.name.constData()) << (te.baselineBaseName + QLatin1String(".ui"))
+ << te.generatedFileName << options;
}
}
@@ -264,15 +318,9 @@ void tst_uic::compare_data() const
QTest::addColumn<QString>("originalFile");
QTest::addColumn<QString>("generatedFile");
- QDir generated(m_generated.path());
- QDir baseline(m_baseline);
- const QFileInfoList baselineFiles = baseline.entryInfoList(QStringList("*.h"), QDir::Files);
- foreach (const QFileInfo &baselineFile, baselineFiles) {
- const QString generatedFile = generated.absolutePath()
- + QLatin1Char('/') + baselineFile.fileName();
- QTest::newRow(qPrintable(baselineFile.baseName()))
- << baselineFile.absoluteFilePath()
- << generatedFile;
+ for (const TestEntry &te : m_testEntries) {
+ QTest::newRow(te.name.constData()) << (te.baselineBaseName + QLatin1String(".ui.h"))
+ << te.generatedFileName;
}
}
@@ -280,7 +328,7 @@ void tst_uic::runTranslation()
{
QProcess process;
- QDir baseline(m_baseline);
+ const QDir baseline(m_baseline);
QDir generated(m_generated.path());
generated.mkdir(QLatin1String("translation"));
@@ -327,5 +375,75 @@ void tst_uic::runCompare()
QCOMPARE(generatedFileContents, originalFileContents);
}
+// Let uic generate Python code and verify that it is syntactically
+// correct by compiling it into .pyc. This test is executed only
+// when python with an installed Qt for Python is detected (see locatePython()).
+
+static inline QByteArray msgCompilePythonFailed(const QByteArray &error)
+{
+ // If there is a line with blanks and caret indicating an error in the line
+ // above, insert the cursor into the offending line and remove the caret.
+ QByteArrayList lines = error.trimmed().split('\n');
+ for (int i = lines.size() - 1; i > 0; --i) {
+ const auto &line = lines.at(i);
+ const int caret = line.indexOf('^');
+ if (caret == 0 || (caret > 0 && line.at(caret - 1) == ' ')) {
+ lines.removeAt(i);
+ lines[i - 1].insert(caret, '|');
+ break;
+ }
+ }
+ return lines.join('\n');
+}
+
+void tst_uic::python_data() const
+{
+ QTest::addColumn<QString>("originalFile");
+ QTest::addColumn<QString>("generatedFile");
+
+ const int size = m_python.isEmpty()
+ ? qMin(1, m_testEntries.size()) : m_testEntries.size();
+ for (int i = 0; i < size; ++i) {
+ const TestEntry &te = m_testEntries.at(i);
+ // qprintsettingsoutput: variable named 'from' clashes with Python
+ if (!te.baselineBaseName.endsWith(QLatin1String("/qprintsettingsoutput"))) {
+ QString generatedFile = te.generatedFileName;
+ generatedFile.chop(1); // foo.h -> foo.py
+ generatedFile.append(QLatin1String("py"));
+ QTest::newRow(te.name.constData())
+ << (te.baselineBaseName + QLatin1String(".ui"))
+ << generatedFile;
+ }
+ }
+}
+
+void tst_uic::python()
+{
+ QFETCH(QString, originalFile);
+ QFETCH(QString, generatedFile);
+ if (m_python.isEmpty())
+ QSKIP("Python was not found");
+
+ QStringList uicArguments{QLatin1String("-g"), QLatin1String("python"),
+ originalFile, QLatin1String("-o"), generatedFile};
+ QProcess process;
+ process.setWorkingDirectory(m_generated.path());
+ process.start(m_command, uicArguments);
+ QVERIFY2(process.waitForStarted(), msgProcessStartFailed(m_command, process.errorString()));
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ QCOMPARE(process.exitCode(), 0);
+ QVERIFY(QFileInfo::exists(generatedFile));
+
+ // Test Python code generation by compiling the file
+ QStringList compileArguments{QLatin1String("-m"), QLatin1String("py_compile"), generatedFile};
+ process.start(m_python, compileArguments);
+ QVERIFY2(process.waitForStarted(), msgProcessStartFailed(m_command, process.errorString()));
+ QVERIFY(process.waitForFinished());
+ const bool compiled = process.exitStatus() == QProcess::NormalExit
+ && process.exitCode() == 0;
+ QVERIFY2(compiled, msgCompilePythonFailed(process.readAllStandardError()).constData());
+}
+
QTEST_MAIN(tst_uic)
#include "tst_uic.moc"