/* * Copyright 2014-2016 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ // Dedicated QML launcher with the ability to setup QQuickView/QQmlEngine // differently and with various extensions. Used internally to write Autopilot // test cases that exhibit specific behavior. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static QObject *s_testRootObject = 0; static QObject *testRootObject(QQmlEngine *engine, QJSEngine *jsEngine) { Q_UNUSED(jsEngine); if (!s_testRootObject) { s_testRootObject = new QObject(engine); } return s_testRootObject; } int main(int argc, const char *argv[]) { // QPlatformIntegration::ThreadedOpenGL setenv("QML_FORCE_THREADED_RENDERER", "1", 1); // QPlatformIntegration::BufferQueueingOpenGL setenv("QML_FIXED_ANIMATION_STEP", "1", 1); // Oxide and QWebEngine need a shared context QScopedPointer shareContext; shareContext.reset(new QOpenGLContext); #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) qt_gl_set_global_share_context(shareContext.data()); #elif QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) QOpenGLContextPrivate::setGlobalShareContext(shareContext.data()); #else QSGContext::setSharedOpenGLContext(shareContext.data()); #endif QGuiApplication::setApplicationName("UITK Launcher"); QGuiApplication application(argc, (char**)argv); QCommandLineParser args; QCommandLineOption _import("I", "Add to the list of import paths", "path"); QCommandLineOption _enableTouch("touch", "Enables mouse to touch conversion on desktop"); QCommandLineOption _testability("testability", "Loads the testability driver"); QCommandLineOption _frameless("frameless", "Run without borders"); QCommandLineOption _engine("engine", "Use quick engine from quick view"); QCommandLineOption _desktop_file_hint("desktop_file_hint", "Desktop file - ignored", "desktop_file"); QCommandLineOption _metricsOverlay("metrics-overlay", "Enable the metrics overlay"); QCommandLineOption _metricsLogging( "metrics-logging", "Enable metrics logging, can be 'stdout', 'lttng', a local or " "absolute filename", "device"); QCommandLineOption _metricsLoggingFilter( "metrics-logging-filter", "Filter metrics logging, is a list of events separated " "by a comma ('window', 'process', 'frame' or '*'), events not filtered are discarded", "filter"); args.addOption(_import); args.addOption(_enableTouch); args.addOption(_testability); args.addOption(_frameless); args.addOption(_engine); args.addOption(_desktop_file_hint); args.addOption(_metricsOverlay); args.addOption(_metricsLogging); args.addOption(_metricsLoggingFilter); args.addPositionalArgument("filename", "Document to be viewed"); args.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); args.addHelpOption(); if (!args.parse(application.arguments())) { qWarning() << args.errorText(); args.showHelp(1); } QString filename; if (args.positionalArguments().count() > 0) { filename = args.positionalArguments()[0]; } if (filename.isEmpty()) { // show usage and exit args.showHelp(1); } // Testability is only supported out of the box by QApplication not QGuiApplication if (args.isSet(_testability) || getenv("QT_LOAD_TESTABILITY")) { QLibrary testLib(QLatin1String("qttestability")); if (testLib.load()) { typedef void (*TasInitialize)(void); TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init"); if (initFunction) { initFunction(); } else { qCritical("Library qttestability resolve failed!"); return 1; } } else { qCritical("Library qttestability load failed!"); return 1; } } // Allow manual execution of unit tests using Qt.Test qmlRegisterSingletonType("Qt.test.qtestroot", 1, 0, "QTestRootObject", testRootObject); QPointer engine; QScopedPointer window; QString testCaseImport; // Let's see if the source file exists QFile sourceCode(filename); if (!sourceCode.open(QIODevice::ReadOnly)) { qCritical("%s", qPrintable(sourceCode.errorString())); return 1; } while (!sourceCode.atEnd()) { QByteArray line(sourceCode.readLine()); if (line.contains("{")) break; // Hack to avoid assertion if QtTest or Ubuntu.Test is used: // ASSERT: "QTest::TestLoggers::loggerCount() != 0" in file qtestlog.cpp, line 278 if ((line.startsWith("import ") && QString(line).split("//")[0].split("/*")[0].contains("Test"))) { testCaseImport = line; break; } } QUrl source(QUrl::fromLocalFile(filename)); // The default constructor affects the components tree (autopilot vis) if (args.isSet(_engine) || !testCaseImport.isEmpty()) { QQuickView *view(new QQuickView()); engine = view->engine(); if (args.isSet(_import)) { QStringList paths = args.values(_import); Q_FOREACH(const QString &path, paths) { engine->addImportPath(path); } } view->setSource(source); while (view->status() == QQuickView::Loading) QCoreApplication::processEvents(); if (view->errors().count() > 0) { args.showHelp(3); } // An unsupported root object is not technically an error if (!view->rootObject()) { if (!testCaseImport.isEmpty()) qCritical("Note: QtTest or Ubuntu.Test was detected here: %s", qPrintable(testCaseImport)); return 1; } window.reset(view); } else { engine = new QQmlEngine(); if (args.isSet(_import)) { QStringList paths = args.values(_import); Q_FOREACH(const QString &path, paths) { engine->addImportPath(path); } } QObject::connect(engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); QPointer component(new QQmlComponent(engine)); component->loadUrl(source, QQmlComponent::Asynchronous); while (component->isLoading()) QCoreApplication::processEvents(); QObject *toplevel(component->create()); if (!toplevel && component->isError()) { qCritical("%s", qPrintable(component->errorString())); return 1; } window.reset(qobject_cast(toplevel)); if (window) engine->setIncubationController(window->incubationController()); else { QQuickItem *rootItem = qobject_cast(toplevel); if (rootItem) { QQuickView *view(new QQuickView(engine, 0)); window.reset(view); view->setResizeMode(QQuickView::SizeRootObjectToView); view->setContent(source, component, rootItem); } } } // Application monitoring. UMApplicationMonitor* applicationMonitor = UMApplicationMonitor::instance(); if (args.isSet(_metricsLoggingFilter)) { QStringList filterList = QString(args.value(_metricsLoggingFilter)).split( QChar(','), QString::SkipEmptyParts); UMApplicationMonitor::LoggingFilters filter = 0; const int size = filterList.size(); for (int i = 0; i < size; ++i) { if (filterList[i] == "*") { filter |= UMApplicationMonitor::AllEvents; break; } else if (filterList[i] == "window") { filter |= UMApplicationMonitor::WindowEvent; } else if (filterList[i] == "process") { filter |= UMApplicationMonitor::ProcessEvent; } else if (filterList[i] == "frame") { filter |= UMApplicationMonitor::FrameEvent; } else if (filterList[i] == "generic") { filter |= UMApplicationMonitor::GenericEvent; } } applicationMonitor->setLoggingFilter(filter); } if (args.isSet(_metricsLogging)) { UMLogger* logger; QString device = args.value(_metricsLogging); if (device.isEmpty() || device == "stdout") { logger = new UMFileLogger(stdout); } else if (device == "lttng") { logger = new UMLTTNGLogger(); } else { logger = new UMFileLogger(device); } if (logger->isOpen()) { applicationMonitor->installLogger(logger); applicationMonitor->setLogging(true); } else { delete logger; } } if (args.isSet(_metricsOverlay)) { applicationMonitor->setOverlay(true); } if (window->title().isEmpty()) window->setTitle("UI Toolkit QQuickView"); if (args.isSet(_frameless)) { window->setFlags(Qt::FramelessWindowHint); } window->show(); if (args.isSet(_enableTouch)) { // has no effect if we have touch screen new UT_PREPEND_NAMESPACE(MouseTouchAdaptor)(&application); } return application.exec(); }