15
15
// License along with this library; if not, write to the Free Software
16
16
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
#include <QDirIterator>
18
20
#include <QGuiApplication>
19
21
#include <QLatin1String>
23
#include <QQmlContext>
28
#include <QStringList>
31
#include <QTextStream>
21
32
#include <QtGlobal>
22
#include <QtQuickTest/quicktest.h>
33
#include <QtQuickTest/private/qtestoptions_p.h>
34
#include <QtQuickTest/private/quicktestresult_p.h>
23
35
#include <QtQuickVersion>
24
37
#if defined(ENABLE_COMPOSITING)
25
38
#include <QOpenGLContext>
26
39
#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
46
#include "qt/core/api/oxideqglobal.h"
48
// We don't use quick_test_main() here for running the qmltest binary as we want to
49
// be able to have a per-test datadir. However, some of quick_test_main() is
50
// duplicated here because we still use other bits of the QtQuickTest module
52
class QTestRootObject : public QObject
55
Q_PROPERTY(bool windowShown READ windowShown NOTIFY windowShownChanged)
56
Q_PROPERTY(bool hasTestCase READ hasTestCase WRITE setHasTestCase NOTIFY hasTestCaseChanged)
59
QTestRootObject(QObject* parent = 0)
61
has_quit_(false), window_shown_(false), has_test_case_(false) {}
63
static QTestRootObject* instance() {
64
static QPointer<QTestRootObject> object = new QTestRootObject();
70
bool hasTestCase() const { return has_test_case_; }
71
void setHasTestCase(bool value) { has_test_case_ = value; emit hasTestCaseChanged(); }
73
bool windowShown() const { return window_shown_; }
74
void setWindowShown(bool value) { window_shown_ = value; emit windowShownChanged(); }
76
void init() { setWindowShown(false); setHasTestCase(false); has_quit_ = false; }
79
void windowShownChanged();
80
void hasTestCaseChanged();
83
void quit() { has_quit_ = true; }
87
bool has_test_case_:1;
90
static QObject* GetTestRootObject(QQmlEngine* engine, QJSEngine* js_engine)
94
return QTestRootObject::instance();
97
static void HandleCompileErrors(const QFileInfo& fi, QQuickView* view) {
98
const QList<QQmlError> errors = view->errors();
100
QuickTestResult results;
101
results.setTestCaseName(fi.baseName());
102
results.startLogging();
103
results.setFunctionName(QLatin1String("compile"));
106
QTextStream str(&message);
107
str << "\n " << QDir::toNativeSeparators(fi.absoluteFilePath()) << " produced "
108
<< errors.size() << " error(s):\n";
109
for (QList<QQmlError>::const_iterator it = errors.begin();
110
it != errors.end(); ++it) {
111
const QQmlError& e = *it;
113
if (e.url().isLocalFile()) {
114
str << QDir::toNativeSeparators(e.url().toLocalFile());
116
str << e.url().toString();
119
str << ':' << e.line() << ',' << e.column();
121
str << ": " << e.description() << '\n';
124
str << " Working directory: "
125
<< QDir::toNativeSeparators(QDir::current().absolutePath()) << '\n';
126
if (QQmlEngine *engine = view->engine()) {
127
const QStringList import_paths = engine->importPathList();
128
str << " View: " << view->metaObject()->className() << ", import paths:\n";
129
for (QStringList::const_iterator it = import_paths.begin();
130
it != import_paths.end(); ++it) {
131
str << " '" << QDir::toNativeSeparators(*it) << "'\n";
133
const QStringList plugin_paths = engine->pluginPathList();
134
str << " Plugin paths:\n";
135
for (QStringList::const_iterator it = plugin_paths.begin();
136
it != plugin_paths.end(); ++it) {
137
str << " '" << QDir::toNativeSeparators(*it) << "'\n";
141
qWarning("%s", qPrintable(message));
143
results.fail(errors.at(0).description(),
144
errors.at(0).url(), errors.at(0).line());
145
results.finishTestData();
146
results.finishTestDataCleanup();
147
results.finishTestFunction();
148
results.setFunctionName(QString());
149
results.stopLogging();
33
152
static QString stripQuotes(const QString& in) {
34
153
if (in.length() >= 2 && in.startsWith("\"") && in.endsWith("\"")) {
35
154
return in.mid(1, in.length() - 2);
41
160
int main(int argc, char** argv) {
42
char** filtered_argv = new char*[argc + 1];
43
int filtered_argc = 1;
44
filtered_argv[0] = argv[0];
162
QStringList library_paths;
165
bool use_data_dir = false;
49
169
while (index < argc) {
50
170
char* arg = argv[index];
51
if (QLatin1String(arg) == QLatin1String("-name") && (index + 1) < argc) {
52
name = stripQuotes(QString::fromLatin1(argv[index + 1]));
171
if (QLatin1String(arg) == QLatin1String("-import") && (index + 1) < argc) {
172
imports.append(stripQuotes(QString::fromLatin1(argv[index + 1])));
174
} else if (QLatin1String(arg) == QLatin1String("-input") && (index + 1) < argc) {
175
if (!test_path.isEmpty()) {
176
qFatal("Can only specify -input once");
178
test_path = stripQuotes(QString::fromLatin1(argv[index + 1]));
180
} else if (QLatin1String(arg) == QLatin1String("-name") && (index + 1) < argc) {
181
if (!name.isEmpty()) {
182
qFatal("Can only specify -name once");
184
name = stripQuotes(QString::fromLatin1(argv[index + 1])).toLatin1();
186
} else if (QLatin1String(arg) == QLatin1String("-add-library-path") && (index + 1) < argc) {
187
library_paths.append(stripQuotes(QString::fromLatin1(argv[index + 1])));
189
} else if (QLatin1String(arg) == QLatin1String("-use-datadir-for-context")) {
192
} else if (QLatin1String(arg) == QLatin1String("-nss-db-path") && (index + 1) < argc) {
193
if (!oxideGetNSSDbPath().isEmpty()) {
194
qFatal("Can only specify -nss-db-path once");
196
if (!oxideSetNSSDbPath(stripQuotes(QString::fromLatin1(argv[index + 1])))) {
197
qFatal("Failed to set NSS DB path");
200
} else if (index != outargc) {
201
argv[outargc++] = argv[index++];
55
filtered_argv[filtered_argc] = arg;
61
filtered_argv[filtered_argc] = NULL;
208
argv[outargc] = NULL;
63
QGuiApplication app(filtered_argc, filtered_argv);
210
QGuiApplication app(outargc, argv);
65
212
#if defined(ENABLE_COMPOSITING)
66
213
QOpenGLContext context;
75
return quick_test_main(filtered_argc, filtered_argv,
76
name.toUtf8().constData(), NULL);
222
for (int i = 0; i < library_paths.size(); ++i) {
223
app.addLibraryPath(library_paths[i]);
226
QuickTestResult::setCurrentAppname(argv[0]);
227
QuickTestResult::setProgramName(name.constData());
228
QuickTestResult::parseArgs(outargc, argv);
230
if (test_path.isEmpty()) {
231
test_path = QDir::currentPath();
234
QString data_dir(qgetenv("OXIDE_RUNTESTS_TMPDIR"));
235
if (data_dir.isEmpty()) {
236
data_dir = QDir::currentPath();
241
QFileInfo test_path_info(test_path);
242
if (test_path_info.isFile()) {
243
if (!test_path.endsWith(".qml")) {
244
qFatal("Test file '%s' does not end with '.qml'", qPrintable(test_path));
246
files.append(test_path);
247
} else if (test_path_info.isDir()) {
248
Q_ASSERT(test_path_info.isDir());
249
const QStringList filters(QStringLiteral("tst_*.qml"));
250
QDirIterator iter(test_path, filters, QDir::Files,
251
QDirIterator::Subdirectories |
252
QDirIterator::FollowSymlinks);
253
while (iter.hasNext()) {
254
files.append(iter.next());
257
if (files.isEmpty()) {
258
qFatal("Directory '%s' does not contain any test files",
259
qPrintable(test_path));
262
qFatal("Test file '%s' does not exist", qPrintable(test_path));
265
qmlRegisterSingletonType<QTestRootObject>(
266
"Qt.test.qtestroot", 1, 0, "QTestRootObject", GetTestRootObject);
268
QEventLoop event_loop;
270
view.setFlags(Qt::Window | Qt::WindowSystemMenuHint |
271
Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint |
272
Qt::WindowCloseButtonHint);
274
QObject::connect(view.engine(), SIGNAL(quit()),
275
QTestRootObject::instance(), SLOT(quit()));
276
QObject::connect(view.engine(), SIGNAL(quit()),
277
&event_loop, SLOT(quit()));
279
for (QStringList::iterator it = imports.begin(); it != imports.end(); ++it) {
280
view.engine()->addImportPath(*it);
282
view.rootContext()->setContextProperty(
283
QStringLiteral("QMLTEST_USE_CONTEXT_DATADIR"),
286
for (QStringList::iterator it = files.begin(); it != files.end(); ++it) {
287
const QFileInfo fi(*it);
293
if (files.size() > 1) {
294
dir = data_dir + QDir::separator() + fi.baseName();
296
view.rootContext()->setContextProperty(
297
QStringLiteral("QMLTEST_DATADIR"),
298
QUrl::fromLocalFile(dir.absolutePath()));
300
view.setObjectName(fi.baseName());
301
view.setTitle(view.objectName());
303
QTestRootObject::instance()->init();
305
QString path = fi.absoluteFilePath();
306
view.setSource(QUrl::fromLocalFile(path));
308
if (QTest::printAvailableFunctions) {
312
if (view.status() == QQuickView::Error) {
313
HandleCompileErrors(fi, &view);
317
if (!QTestRootObject::instance()->has_quit_) {
318
view.setFramePosition(QPoint(50, 50));
319
if (view.size().isEmpty()) {
320
qWarning().nospace() << "Test '" << QDir::toNativeSeparators(path) <<
321
"' has invalid size " << view.size() <<
323
view.resize(200, 200);
326
view.requestActivate();
327
QTest::qWaitForWindowExposed(&view);
328
if (view.isExposed()) {
329
QTestRootObject::instance()->setWindowShown(true);
331
if (!QTestRootObject::instance()->has_quit_ &&
332
QTestRootObject::instance()->hasTestCase()) {
338
QuickTestResult::setProgramName(NULL);
339
return QuickTestResult::exitCode();