From c42a5dfb51ceec1783f4fb770180fadf116acfc3 Mon Sep 17 00:00:00 2001 From: Paul Wicking <paul.wicking@qt.io> Date: Fri, 13 Jul 2018 13:24:23 +0200 Subject: [PATCH] Duplicate tests that use examples from qtdeclarative Moving examples from qtdeclarative to qtdoc broke tests that operate on the example sources. This patch duplicates those tests and the documentation snippets that serve as test data to reestablish test coverage. Task-number: QTBUG-69383 Change-Id: I31fd1729f794081a11844be9c27ed4f9a8a5ce73 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io> --- tests/auto/auto.pro | 8 + tests/auto/qml/qml.pro | 9 + tests/auto/qml/qmlmin/qmlmin.pro | 12 + tests/auto/qml/qmlmin/tst_qmlmin.cpp | 168 +++++++++++ tests/auto/qml/qqmlparser/qqmlparser.pro | 9 + tests/auto/qml/qqmlparser/tst_qqmlparser.cpp | 272 ++++++++++++++++++ tests/auto/quick/examples/data/dummytest.qml | 6 + tests/auto/quick/examples/examples.pro | 12 + tests/auto/quick/examples/tst_examples.cpp | 281 +++++++++++++++++++ tests/auto/quick/quick.pro | 7 + tests/tests.pro | 2 + 11 files changed, 786 insertions(+) create mode 100644 tests/auto/auto.pro create mode 100644 tests/auto/qml/qml.pro create mode 100644 tests/auto/qml/qmlmin/qmlmin.pro create mode 100644 tests/auto/qml/qmlmin/tst_qmlmin.cpp create mode 100644 tests/auto/qml/qqmlparser/qqmlparser.pro create mode 100644 tests/auto/qml/qqmlparser/tst_qqmlparser.cpp create mode 100644 tests/auto/quick/examples/data/dummytest.qml create mode 100644 tests/auto/quick/examples/examples.pro create mode 100644 tests/auto/quick/examples/tst_examples.cpp create mode 100644 tests/auto/quick/quick.pro create mode 100644 tests/tests.pro diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 00000000..bc6a6dff --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,8 @@ +TEMPLATE=subdirs +qtHaveModule(quick) { + SUBDIRS += quick +} +qtHaveModule(qml) { + SUBDIRS += qml +} + diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro new file mode 100644 index 00000000..63775a12 --- /dev/null +++ b/tests/auto/qml/qml.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs +QT_FOR_CONFIG += qml-private + +qtConfig(qml-devtools): + SUBDIRS += qmlmin + +qtConfig(private_tests): \ + SUBDIRS += qqmlparser + diff --git a/tests/auto/qml/qmlmin/qmlmin.pro b/tests/auto/qml/qmlmin/qmlmin.pro new file mode 100644 index 00000000..18dba551 --- /dev/null +++ b/tests/auto/qml/qmlmin/qmlmin.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qmlmin +QT += qml testlib gui-private +macos:CONFIG -= app_bundle + +SOURCES += tst_qmlmin.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" + +# Boot2qt is cross compiled but it has sources available +!boot2qt { + cross_compile: DEFINES += QTEST_CROSS_COMPILED +} diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp new file mode 100644 index 00000000..fa13ea72 --- /dev/null +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QLibraryInfo> +#include <QDir> +#if QT_CONFIG(process) +#include <QProcess> +#endif +#include <QDebug> +#include <QQmlError> +#include <cstdlib> + +class tst_qmlmin : public QObject +{ + Q_OBJECT +public: + tst_qmlmin(); + +private slots: + void initTestCase(); +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled + void qmlMinify_data(); + void qmlMinify(); +#endif + +private: + QString qmlminPath; + QStringList excludedDirs; + QStringList invalidFiles; + + QStringList findFiles(const QDir &); + bool isInvalidFile(const QFileInfo &fileName) const; +}; + +tst_qmlmin::tst_qmlmin() +{ +} + +void tst_qmlmin::initTestCase() +{ + qmlminPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmlmin"); +#ifdef Q_OS_WIN + qmlminPath += QLatin1String(".exe"); +#endif + if (!QFileInfo(qmlminPath).exists()) { + QString message = QString::fromLatin1("qmlmin executable not found (looked for %0)") + .arg(qmlminPath); + QFAIL(qPrintable(message)); + } + + // Add directories you want excluded here + // excludedDirs << "exclude/this/dir"; + + // Add invalid files (i.e. files with syntax errors) + // invalidFiles << "exclude/this/file.txt"; +} + +QStringList tst_qmlmin::findFiles(const QDir &d) +{ + for (int ii = 0; ii < excludedDirs.count(); ++ii) { + QString s = excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return QStringList(); + } + + QStringList rv; + + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"), + QDir::Files); + foreach (const QString &file, files) { + rv << d.absoluteFilePath(file); + } + + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + rv << findFiles(sub); + } + + return rv; +} + +bool tst_qmlmin::isInvalidFile(const QFileInfo &fileName) const +{ + foreach (const QString &invalidFile, invalidFiles) { + if (fileName.absoluteFilePath().endsWith(invalidFile)) + return true; + } + return false; +} + +/* +This test runs all the examples in the Qt QML UI source tree and ensures +that they start and exit cleanly. + +Examples are any .qml files under the examples/ directory that start +with a lower case letter. +*/ + +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +void tst_qmlmin::qmlMinify_data() +{ + QTest::addColumn<QString>("file"); + + QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; + QString tests = QLatin1String(SRCDIR) + "/../../../../tests/"; + + QStringList files; + files << findFiles(QDir(examples)); + files << findFiles(QDir(tests)); + + foreach (const QString &file, files) + QTest::newRow(qPrintable(file)) << file; +} +#endif + +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +void tst_qmlmin::qmlMinify() +{ + QFETCH(QString, file); + + QProcess qmlminify; + + // Restrict line width to 100 characters + qmlminify.start(qmlminPath, QStringList() << QLatin1String("--verify-only") << QLatin1String("-w100") << file); + qmlminify.waitForFinished(); + + QCOMPARE(qmlminify.error(), QProcess::UnknownError); + QCOMPARE(qmlminify.exitStatus(), QProcess::NormalExit); + + if (isInvalidFile(file)) + QCOMPARE(qmlminify.exitCode(), EXIT_FAILURE); // cannot minify files with syntax errors + else + QCOMPARE(qmlminify.exitCode(), 0); +} +#endif + +QTEST_MAIN(tst_qmlmin) + +#include "tst_qmlmin.moc" diff --git a/tests/auto/qml/qqmlparser/qqmlparser.pro b/tests/auto/qml/qqmlparser/qqmlparser.pro new file mode 100644 index 00000000..fa85b93c --- /dev/null +++ b/tests/auto/qml/qqmlparser/qqmlparser.pro @@ -0,0 +1,9 @@ +CONFIG += testcase +TARGET = tst_qqmlparser +QT += qml-private testlib +macos:CONFIG -= app_bundle + +SOURCES += tst_qqmlparser.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" + +cross_compile: DEFINES += QTEST_CROSS_COMPILED diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp new file mode 100644 index 00000000..e30481e3 --- /dev/null +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#include <private/qqmljsengine_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> + +#include <qtest.h> +#include <QDir> +#include <QDebug> +#include <cstdlib> + +class tst_qqmlparser : public QObject +{ + Q_OBJECT +public: + tst_qqmlparser(); + +private slots: + void initTestCase(); +#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled + void qmlParser_data(); + void qmlParser(); +#endif + void invalidEscapeSequence(); + void stringLiteral(); + void noSubstitutionTemplateLiteral(); + void templateLiteral(); + +private: + QStringList excludedDirs; + + QStringList findFiles(const QDir &); +}; + +namespace check { + +using namespace QQmlJS; + +class Check: public AST::Visitor +{ + QList<AST::Node *> nodeStack; + +public: + void operator()(AST::Node *node) + { + AST::Node::accept(node, this); + } + + void checkNode(AST::Node *node) + { + if (! nodeStack.isEmpty()) { + AST::Node *parent = nodeStack.last(); + const quint32 parentBegin = parent->firstSourceLocation().begin(); + const quint32 parentEnd = parent->lastSourceLocation().end(); + + if (node->firstSourceLocation().begin() < parentBegin) + qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn + << "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn; + if (node->lastSourceLocation().end() > parentEnd) + qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn + << "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn; + + QVERIFY(node->firstSourceLocation().begin() >= parentBegin); + QVERIFY(node->lastSourceLocation().end() <= parentEnd); + } + } + + virtual bool preVisit(AST::Node *node) + { + checkNode(node); + nodeStack.append(node); + return true; + } + + virtual void postVisit(AST::Node *) + { + nodeStack.removeLast(); + } +}; + +} + +tst_qqmlparser::tst_qqmlparser() +{ +} + +void tst_qqmlparser::initTestCase() +{ + // Add directories you want excluded here: + // excludedDirs << "exclude/this/dir"; +} + +QStringList tst_qqmlparser::findFiles(const QDir &d) +{ + for (int ii = 0; ii < excludedDirs.count(); ++ii) { + QString s = excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return QStringList(); + } + + QStringList rv; + + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml") << QLatin1String("*.js"), + QDir::Files); + foreach (const QString &file, files) { + rv << d.absoluteFilePath(file); + } + + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + rv << findFiles(sub); + } + + return rv; +} + +/* +This test checks all the qml and js files in the QtQml UI source tree +and ensures that the subnode's source locations are inside parent node's source locations +*/ + +#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +void tst_qqmlparser::qmlParser_data() +{ + QTest::addColumn<QString>("file"); + + QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; + QString tests = QLatin1String(SRCDIR) + "/../../../../tests/"; + + QStringList files; + files << findFiles(QDir(examples)); + files << findFiles(QDir(tests)); + + foreach (const QString &file, files) + QTest::newRow(qPrintable(file)) << file; +} +#endif + +#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +void tst_qqmlparser::qmlParser() +{ + QFETCH(QString, file); + + using namespace QQmlJS; + + QString code; + + QFile f(file); + if (f.open(QFile::ReadOnly)) + code = QString::fromUtf8(f.readAll()); + + const bool qmlMode = file.endsWith(QLatin1String(".qml")); + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(code, 1, qmlMode); + Parser parser(&engine); + bool ok = qmlMode ? parser.parse() : parser.parseProgram(); + + if (ok) { + check::Check chk; + chk(parser.rootNode()); + } +} +#endif + +void tst_qqmlparser::invalidEscapeSequence() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(QLatin1String("\"\\"), 1); + Parser parser(&engine); + parser.parse(); +} + +void tst_qqmlparser::stringLiteral() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + QLatin1String code("'hello string'"); + lexer.setCode(code , 1); + Parser parser(&engine); + QVERIFY(parser.parseExpression()); + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expression); + QVERIFY(literal); + QCOMPARE(literal->value, "hello string"); + QCOMPARE(literal->firstSourceLocation().begin(), 0); + QCOMPARE(literal->lastSourceLocation().end(), code.size()); +} + +void tst_qqmlparser::noSubstitutionTemplateLiteral() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + QLatin1String code("`hello template`"); + lexer.setCode(code, 1); + Parser parser(&engine); + QVERIFY(parser.parseExpression()); + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + + auto *literal = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression); + QVERIFY(literal); + + QCOMPARE(literal->value, "hello template"); + QCOMPARE(literal->firstSourceLocation().begin(), 0); + QCOMPARE(literal->lastSourceLocation().end(), code.size()); +} + +void tst_qqmlparser::templateLiteral() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + QLatin1String code("`one plus one equals ${1+1}!`"); + lexer.setCode(code, 1); + Parser parser(&engine); + QVERIFY(parser.parseExpression()); + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + + auto *templateLiteral = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression); + QVERIFY(templateLiteral); + + QCOMPARE(templateLiteral->firstSourceLocation().begin(), 0); + auto *e = templateLiteral->expression; + QVERIFY(e); +} + +QTEST_MAIN(tst_qqmlparser) + +#include "tst_qqmlparser.moc" diff --git a/tests/auto/quick/examples/data/dummytest.qml b/tests/auto/quick/examples/data/dummytest.qml new file mode 100644 index 00000000..b20e907f --- /dev/null +++ b/tests/auto/quick/examples/data/dummytest.qml @@ -0,0 +1,6 @@ +import Qt.VisualTest 4.6 + +VisualTest { + Frame { msec: 0 } + Frame { msec: 10 } +} diff --git a/tests/auto/quick/examples/examples.pro b/tests/auto/quick/examples/examples.pro new file mode 100644 index 00000000..4e7ed190 --- /dev/null +++ b/tests/auto/quick/examples/examples.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +testcase.timeout = 400 # this test is slow +TARGET = tst_examples +macos:CONFIG -= app_bundle + +SOURCES += tst_examples.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" + +#temporary +QT += core-private gui-private qml-private quick-private testlib +!qtHaveModule(xmlpatterns): DEFINES += QT_NO_XMLPATTERNS + diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp new file mode 100644 index 00000000..02dc7042 --- /dev/null +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QLibraryInfo> +#include <QDir> +#include <QDebug> +#include <QtQuick/QQuickItem> +#include <QtQuick/QQuickView> +#include <QQmlComponent> +#include <QQmlEngine> +#include <QQmlError> + +static QtMessageHandler testlibMsgHandler = nullptr; +void msgHandlerFilter(QtMsgType type, const QMessageLogContext &ctxt, const QString &msg) +{ + if (type == QtCriticalMsg || type == QtFatalMsg) + (*testlibMsgHandler)(type, ctxt, msg); +} + +class tst_examples : public QObject +{ + Q_OBJECT +public: + tst_examples(); + ~tst_examples(); + +private slots: + void init(); + void cleanup(); + + void sgexamples_data(); + void sgexamples(); + + void namingConvention(); +private: + QStringList excludedDirs; + QStringList excludedFiles; + + void namingConvention(const QDir &); + QStringList findQmlFiles(const QDir &); + + QQmlEngine engine; +}; + +tst_examples::tst_examples() +{ + // Add files to exclude here + excludedFiles << "snippets/qml/listmodel/listmodel.qml"; //Just a ListModel, no root QQuickItem + excludedFiles << "examples/quick/demos/photosurface/photosurface.qml"; // root item is Window rather than Item + + // Add directories you want excluded here + excludedDirs << "shared"; //Not an example + excludedDirs << "snippets/qml/path"; //No root QQuickItem + excludedDirs << "examples/qml/qmlextensionplugins"; //Requires special import search path + excludedDirs << "examples/quick/tutorials/gettingStartedQml"; //C++ example, but no cpp files in root dir + + // These snippets are not expected to run on their own. + excludedDirs << "snippets/qml/visualdatamodel_rootindex"; + excludedDirs << "snippets/qml/qtbinding"; + excludedDirs << "snippets/qml/imports"; + excludedFiles << "snippets/qml/image-ext.qml"; + excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources + excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources + +#ifdef QT_NO_XMLPATTERNS + excludedDirs << "demos/twitter"; + excludedDirs << "demos/flickr"; + excludedDirs << "demos/photoviewer"; + excludedFiles << "snippets/qml/xmlrole.qml"; + excludedFiles << "particles/itemparticle/particleview.qml"; + excludedFiles << "views/visualdatamodel/slideshow.qml"; +#endif + +#if !QT_CONFIG(opengl) + //No support for Particles + excludedFiles << "examples/qml/dynamicscene/dynamicscene.qml"; + excludedFiles << "examples/quick/animation/basics/color-animation.qml"; + excludedFiles << "examples/quick/particles/affectors/content/age.qml"; + excludedFiles << "examples/quick/touchinteraction/multipointtouch/bearwhack.qml"; + excludedFiles << "examples/quick/touchinteraction/multipointtouch/multiflame.qml"; + excludedDirs << "examples/quick/particles"; + // No Support for ShaderEffect + excludedFiles << "src/quick/doc/snippets/qml/animators.qml"; +#endif + +} + +tst_examples::~tst_examples() +{ +} + +void tst_examples::init() +{ + if (!qstrcmp(QTest::currentTestFunction(), "sgsnippets")) + testlibMsgHandler = qInstallMessageHandler(msgHandlerFilter); +} + +void tst_examples::cleanup() +{ + if (!qstrcmp(QTest::currentTestFunction(), "sgsnippets")) + qInstallMessageHandler(testlibMsgHandler); +} + +/* +This tests that the examples follow the naming convention required +to have them tested by the examples() test. +*/ +void tst_examples::namingConvention(const QDir &d) +{ + for (int ii = 0; ii < excludedDirs.count(); ++ii) { + QString s = excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return; + } + + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), + QDir::Files); + + bool seenQml = !files.isEmpty(); + bool seenLowercase = false; + + foreach (const QString &file, files) { + if (file.at(0).isLower()) + seenLowercase = true; + } + + if (!seenQml) { + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + namingConvention(sub); + } + } else if (!seenLowercase) { + // QTBUG-28271 don't fail, but rather warn only + qWarning() << QString( + "Directory %1 violates naming convention; expected at least one qml file " + "starting with lower case, got: %2" + ).arg(d.absolutePath()).arg(files.join(",")); + +// QFAIL(qPrintable(QString( +// "Directory %1 violates naming convention; expected at least one qml file " +// "starting with lower case, got: %2" +// ).arg(d.absolutePath()).arg(files.join(",")))); + } +} + +void tst_examples::namingConvention() +{ + QStringList examplesLocations; + examplesLocations << QLibraryInfo::location(QLibraryInfo::ExamplesPath) + QLatin1String("/qml"); + examplesLocations << QLibraryInfo::location(QLibraryInfo::ExamplesPath) + QLatin1String("/quick"); + + foreach (const QString &examples, examplesLocations) { + QDir d(examples); + if (d.exists()) + namingConvention(d); + } +} + +QStringList tst_examples::findQmlFiles(const QDir &d) +{ + for (int ii = 0; ii < excludedDirs.count(); ++ii) { + QString s = excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return QStringList(); + } + + QStringList rv; + + QStringList cppfiles = d.entryList(QStringList() << QLatin1String("*.cpp"), QDir::Files); + if (cppfiles.isEmpty()) { + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), + QDir::Files); + foreach (const QString &file, files) { + if (file.at(0).isLower()) { + bool superContinue = false; + for (int ii = 0; ii < excludedFiles.count(); ++ii) { + QString e = excludedFiles.at(ii); + if (d.absoluteFilePath(file).endsWith(e)) { + superContinue = true; + break; + } + } + if (superContinue) + continue; + rv << d.absoluteFilePath(file); + } + } + } + + + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + rv << findQmlFiles(sub); + } + + return rv; +} + +/* +This test runs all the examples in the QtQml UI source tree and ensures +that they start and exit cleanly. + +Examples are any .qml files under the examples/ directory that start +with a lower case letter. +*/ +void tst_examples::sgexamples_data() +{ + QTest::addColumn<QString>("file"); + + QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; + + QStringList files; + files << findQmlFiles(QDir(examples)); + + foreach (const QString &file, files) + QTest::newRow(qPrintable(file)) << file; +} + +void tst_examples::sgexamples() +{ + QFETCH(QString, file); + QQuickWindow window; + window.setPersistentOpenGLContext(true); + window.setPersistentSceneGraph(true); + + QQmlComponent component(&engine, QUrl::fromLocalFile(file)); + if (component.status() == QQmlComponent::Error) + qWarning() << component.errors(); + QCOMPARE(component.status(), QQmlComponent::Ready); + + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQuickItem *root = qobject_cast<QQuickItem *>(object.data()); + if (!root) + component.completeCreate(); + QVERIFY(root); + + window.resize(240, 320); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + root->setParentItem(window.contentItem()); + component.completeCreate(); + + qApp->processEvents(); +} + +QTEST_MAIN(tst_examples) + +#include "tst_examples.moc" diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro new file mode 100644 index 00000000..b2542cf2 --- /dev/null +++ b/tests/auto/quick/quick.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +!cross_compile: PRIVATETESTS += examples + +qtConfig(private_tests) { + SUBDIRS += $$PRIVATETESTS +} diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 00000000..85e4f3a5 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += auto -- GitLab