~zsombi/ubuntu-ui-toolkit/listItemFocusRegression

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

// 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 <iostream>
#include <QtCore/qdebug.h>
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickView>
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlComponent>
#include <QtCore/QFileInfo>
#include <QLibrary>
#include <QOpenGLContext>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <QtCore/QCommandLineParser>
#include <QtCore/QCommandLineOption>
#include <UbuntuToolkit/private/mousetouchadaptor_p.h>
#include <UbuntuMetrics/applicationmonitor.h>
#include <QtGui/QTouchDevice>
#include <QtQml/qqml.h>

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<QOpenGLContext> 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 <path> 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, <device> can be 'stdout', 'lttng' (Linux "
        "only), a local or absolute filename", "device");
    QCommandLineOption _metricsLoggingFilter(
        "metrics-logging-filter", "Filter metrics logging, <filter> 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<QObject>("Qt.test.qtestroot", 1, 0, "QTestRootObject", testRootObject);

    QPointer<QQmlEngine> engine;
    QScopedPointer<QQuickWindow> 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<QQmlComponent> 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<QQuickWindow *>(toplevel));
        if (window)
            engine->setIncubationController(window->incubationController());
        else {
            QQuickItem *rootItem = qobject_cast<QQuickItem *>(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);
#if defined(Q_OS_LINUX)
        } else if (device == "lttng") {
            logger = new UMLTTNGLogger();
#endif  // defined(Q_OS_LINUX)
        } 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();
}