diff options
author | Lars Knoll <lars.knoll@qt.io> | 2017-10-22 12:05:18 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2017-10-22 12:26:28 +0200 |
commit | aceb0d0cd2da89aebbf17729869b9e977290c826 (patch) | |
tree | b29e815e32277710058bc32c571281937183fd43 /src/qmltest | |
parent | 6ecbe6441f825489b5fafde1a274952933254c7f (diff) | |
parent | 1bb9c7b517e7995201d2a31cd1e852c02ad8c1bc (diff) |
Merge remote-tracking branch 'origin/dev' into HEAD
Conflicts:
src/qml/compiler/qv4codegen.cpp
src/qml/compiler/qv4compileddata.cpp
src/qml/compiler/qv4compileddata_p.h
src/qml/compiler/qv4isel_moth_p.h
src/qml/compiler/qv4ssa.cpp
src/qml/jit/qv4assembler_p.h
src/qml/jit/qv4isel_masm_p.h
src/qml/jit/qv4regalloc.cpp
src/qml/jsruntime/qv4engine.cpp
src/qml/jsruntime/qv4qmlcontext_p.h
src/qml/jsruntime/qv4regexp.cpp
src/qml/jsruntime/qv4regexp_p.h
src/qml/jsruntime/qv4regexpobject.cpp
src/qml/jsruntime/qv4runtime.cpp
src/qml/jsruntime/qv4vme_moth.cpp
src/qml/qml/v8/qqmlbuiltinfunctions.cpp
tests/auto/qml/qml.pro
tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
tools/qmlcachegen/qmlcachegen.cpp
Change-Id: I1577e195c736f3414089036b957a01cb91a3ca23
Diffstat (limited to 'src/qmltest')
-rw-r--r-- | src/qmltest/quicktest.cpp | 162 |
1 files changed, 155 insertions, 7 deletions
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 993a411013..3cd1694dd0 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -63,6 +63,8 @@ #include <QtCore/QTranslator> #include <QtTest/QSignalSpy> +#include <private/qqmlcomponent_p.h> + #ifdef QT_QMLTEST_WITH_WIDGETS #include <QtWidgets/QApplication> #endif @@ -195,6 +197,133 @@ bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000) return spy.size(); } +using namespace QV4::CompiledData; + +class TestCaseCollector +{ +public: + typedef QList<QString> TestCaseList; + + TestCaseCollector(const QFileInfo &fileInfo, QQmlEngine *engine) + { + QQmlComponent component(engine, fileInfo.absoluteFilePath()); + m_errors += component.errors(); + + if (component.isReady()) { + CompilationUnit *rootCompilationUnit = QQmlComponentPrivate::get(&component)->compilationUnit; + TestCaseEnumerationResult result = enumerateTestCases(rootCompilationUnit); + m_testCases = result.testCases + result.finalizedPartialTestCases(); + m_errors += result.errors; + } + } + + TestCaseList testCases() const { return m_testCases; } + QList<QQmlError> errors() const { return m_errors; } + +private: + TestCaseList m_testCases; + QList<QQmlError> m_errors; + + struct TestCaseEnumerationResult + { + TestCaseList testCases; + QList<QQmlError> errors; + + // Partially constructed test cases + bool isTestCase = false; + TestCaseList testFunctions; + QString testCaseName; + + TestCaseList finalizedPartialTestCases() const + { + TestCaseList result; + for (const QString &function : testFunctions) + result << QString(QStringLiteral("%1::%2")).arg(testCaseName).arg(function); + return result; + } + + TestCaseEnumerationResult &operator<<(const TestCaseEnumerationResult &other) + { + testCases += other.testCases + other.finalizedPartialTestCases(); + errors += other.errors; + return *this; + } + }; + + TestCaseEnumerationResult enumerateTestCases(CompilationUnit *compilationUnit, const Object *object = nullptr) + { + QQmlType testCaseType; + for (quint32 i = 0; i < compilationUnit->data->nImports; ++i) { + const Import *import = compilationUnit->data->importAt(i); + if (compilationUnit->stringAt(import->uriIndex) != QLatin1Literal("QtTest")) + continue; + + QString testCaseTypeName(QStringLiteral("TestCase")); + QString typeQualifier = compilationUnit->stringAt(import->qualifierIndex); + if (!typeQualifier.isEmpty()) + testCaseTypeName = typeQualifier % QLatin1Char('.') % testCaseTypeName; + + testCaseType = compilationUnit->typeNameCache->query(testCaseTypeName).type; + if (testCaseType.isValid()) + break; + } + + TestCaseEnumerationResult result; + + if (!object) // Start at root of compilation unit if not enumerating a specific child + object = compilationUnit->objectAt(0); + + if (CompilationUnit *superTypeUnit = compilationUnit->resolvedTypes.value(object->inheritedTypeNameIndex)->compilationUnit) { + // We have a non-C++ super type, which could indicate we're a subtype of a TestCase + if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl()) + result.isTestCase = true; + else + result = enumerateTestCases(superTypeUnit); + + if (result.isTestCase) { + // Look for override of name in this type + for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { + if (compilationUnit->stringAt(binding->propertyNameIndex) == QLatin1Literal("name")) { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + result.testCaseName = compilationUnit->stringAt(binding->stringIndex); + } else { + QQmlError error; + error.setUrl(compilationUnit->url()); + error.setLine(binding->location.line); + error.setColumn(binding->location.column); + error.setDescription(QStringLiteral("the 'name' property of a TestCase must be a literal string")); + result.errors << error; + } + break; + } + } + + // Look for additional functions in this type + auto functionsEnd = compilationUnit->objectFunctionsEnd(object); + for (auto function = compilationUnit->objectFunctionsBegin(object); function != functionsEnd; ++function) { + QString functionName = compilationUnit->stringAt(function->nameIndex); + if (!(functionName.startsWith(QLatin1Literal("test_")) || functionName.startsWith(QLatin1Literal("benchmark_")))) + continue; + + if (functionName.endsWith(QLatin1Literal("_data"))) + continue; + + result.testFunctions << functionName; + } + } + } + + for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + const Object *child = compilationUnit->objectAt(binding->value.objectIndex); + result << enumerateTestCases(compilationUnit, child); + } + } + + return result; + } +}; + int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir) { // Peek at arguments to check for '-widgets' argument @@ -339,7 +468,32 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD if (!fi.exists()) continue; - QQuickView view ; + QQmlEngine engine; + for (const QString &path : qAsConst(imports)) + engine.addImportPath(path); + for (const QString &path : qAsConst(pluginPaths)) + engine.addPluginPath(path); + + TestCaseCollector testCaseCollector(fi, &engine); + if (!testCaseCollector.errors().isEmpty()) { + for (const QQmlError &error : testCaseCollector.errors()) + qWarning() << error; + exit(1); + } + + TestCaseCollector::TestCaseList availableTestFunctions = testCaseCollector.testCases(); + if (QTest::printAvailableFunctions) { + for (const QString &function : availableTestFunctions) + qDebug("%s()", qPrintable(function)); + continue; + } + + static const QSet<QString> commandLineTestFunctions = QTest::testFunctions.toSet(); + if (!commandLineTestFunctions.isEmpty() && + !availableTestFunctions.toSet().intersects(commandLineTestFunctions)) + continue; + + QQuickView view(&engine, nullptr); view.setFlags(Qt::Window | Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); @@ -350,10 +504,6 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD &eventLoop, SLOT(quit())); view.rootContext()->setContextProperty (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead - for (const QString &path : qAsConst(imports)) - view.engine()->addImportPath(path); - for (const QString &path : qAsConst(pluginPaths)) - view.engine()->addPluginPath(path); view.setObjectName(fi.baseName()); view.setTitle(view.objectName()); @@ -364,8 +514,6 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD else view.setSource(QUrl::fromLocalFile(path)); - if (QTest::printAvailableFunctions) - continue; while (view.status() == QQuickView::Loading) QTest::qWait(10); if (view.status() == QQuickView::Error) { |