/*
* 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();
}