Doc: Enabling Qt QML linking to Qt Quick.
[profile/ivi/qtdeclarative.git] / src / qmltest / quicktestresult.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "quicktestresult_p.h"
43 #include <QtTest/qtestcase.h>
44 #include <QtTest/qtestsystem.h>
45 #include <QtTest/private/qtestresult_p.h>
46 #include <QtTest/private/qtesttable_p.h>
47 #include <QtTest/private/qtestlog_p.h>
48 #include "qtestoptions_p.h"
49 #include <QtTest/qbenchmark.h>
50 #include <QtTest/private/qbenchmark_p.h>
51 #include <QtCore/qset.h>
52 #include <QtCore/qmap.h>
53 #include <QtCore/qbytearray.h>
54 #include <QtCore/qcoreapplication.h>
55 #include <QtCore/qdebug.h>
56 #include <QtCore/QUrl>
57 #include <QtCore/QDir>
58 #include <QtQuick/qquickwindow.h>
59 #include <QtGui/qvector3d.h>
60 #include <QtQml/private/qqmlglobal_p.h>
61
62 QT_BEGIN_NAMESPACE
63
64 static const char *globalProgramName = 0;
65 static bool loggingStarted = false;
66 static QBenchmarkGlobalData globalBenchmarkData;
67
68 extern bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000);
69
70 class Q_QUICK_TEST_EXPORT QuickTestImageObject : public QObject
71 {
72     Q_OBJECT
73 public:
74     QuickTestImageObject(const QImage& img, QObject *parent = 0)
75         : QObject(parent)
76         , m_image(img)
77     {
78     }
79
80     ~QuickTestImageObject() {}
81
82 public Q_SLOTS:
83     int red(int x, int y) const
84     {
85         return pixel(x, y).value<QColor>().red();
86     }
87
88     int green(int x, int y) const
89     {
90         return pixel(x, y).value<QColor>().green();
91     }
92
93     int blue(int x, int y) const
94     {
95         return pixel(x, y).value<QColor>().blue();
96     }
97
98     int alpha(int x, int y) const
99     {
100         return pixel(x, y).value<QColor>().alpha();
101     }
102
103     QVariant pixel(int x, int y) const
104     {
105         if (m_image.isNull()
106          || x >= m_image.width()
107          || y >= m_image.height()
108          || x < 0
109          || y < 0
110          || x * y >= m_image.width() * m_image.height())
111             return QVariant();
112
113         const QRgb* pixel = reinterpret_cast<const QRgb*>(m_image.constScanLine(y));
114         pixel += x;
115         return QColor::fromRgba(*pixel);
116     }
117 private:
118     QImage m_image;
119 };
120
121 class QuickTestResultPrivate
122 {
123 public:
124     QuickTestResultPrivate()
125         : table(0)
126         , benchmarkIter(0)
127         , benchmarkData(0)
128         , iterCount(0)
129     {
130     }
131     ~QuickTestResultPrivate()
132     {
133         delete table;
134         delete benchmarkIter;
135         delete benchmarkData;
136     }
137
138     QByteArray intern(const QString &str);
139
140     QString testCaseName;
141     QString functionName;
142     QSet<QByteArray> internedStrings;
143     QTestTable *table;
144     QTest::QBenchmarkIterationController *benchmarkIter;
145     QBenchmarkTestMethodData *benchmarkData;
146     int iterCount;
147     QList<QBenchmarkResult> results;
148 };
149
150 QByteArray QuickTestResultPrivate::intern(const QString &str)
151 {
152     QByteArray bstr = str.toUtf8();
153     return *(internedStrings.insert(bstr));
154 }
155
156 QuickTestResult::QuickTestResult(QObject *parent)
157     : QObject(parent), d_ptr(new QuickTestResultPrivate)
158 {
159     if (!QBenchmarkGlobalData::current)
160         QBenchmarkGlobalData::current = &globalBenchmarkData;
161 }
162
163 QuickTestResult::~QuickTestResult()
164 {
165 }
166
167 /*!
168     \qmlproperty string TestResult::testCaseName
169
170     This property defines the name of current TestCase element
171     that is running test cases.
172
173     \sa functionName
174 */
175 QString QuickTestResult::testCaseName() const
176 {
177     Q_D(const QuickTestResult);
178     return d->testCaseName;
179 }
180
181 void QuickTestResult::setTestCaseName(const QString &name)
182 {
183     Q_D(QuickTestResult);
184     d->testCaseName = name;
185     emit testCaseNameChanged();
186 }
187
188 /*!
189     \qmlproperty string TestResult::functionName
190
191     This property defines the name of current test function
192     within a TestCase element that is running.  If this string is
193     empty, then no function is currently running.
194
195     \sa testCaseName
196 */
197 QString QuickTestResult::functionName() const
198 {
199     Q_D(const QuickTestResult);
200     return d->functionName;
201 }
202
203 void QuickTestResult::setFunctionName(const QString &name)
204 {
205     Q_D(QuickTestResult);
206     if (!name.isEmpty()) {
207         if (d->testCaseName.isEmpty()) {
208             QTestResult::setCurrentTestFunction
209                 (d->intern(name).constData());
210         } else {
211             QString fullName = d->testCaseName + QLatin1String("::") + name;
212             QTestResult::setCurrentTestFunction
213                 (d->intern(fullName).constData());
214         }
215     } else {
216         QTestResult::setCurrentTestFunction(0);
217     }
218     d->functionName = name;
219     emit functionNameChanged();
220 }
221
222 /*!
223     \qmlproperty string TestResult::dataTag
224
225     This property defines the tag for the current row in a
226     data-driven test, or an empty string if not a data-driven test.
227 */
228 QString QuickTestResult::dataTag() const
229 {
230     const char *tag = QTestResult::currentDataTag();
231     if (tag)
232         return QString::fromUtf8(tag);
233     else
234         return QString();
235 }
236
237 void QuickTestResult::setDataTag(const QString &tag)
238 {
239     if (!tag.isEmpty()) {
240         QTestData *data = &(QTest::newRow(tag.toUtf8().constData()));
241         QTestResult::setCurrentTestData(data);
242         emit dataTagChanged();
243     } else {
244         QTestResult::setCurrentTestData(0);
245     }
246 }
247
248 /*!
249     \qmlproperty bool TestResult::failed
250
251     This property returns true if the current test function (or
252     current test data row for a data-driven test) has failed;
253     false otherwise.  The fail state is reset when functionName
254     is changed or finishTestDataCleanup() is called.
255
256     \sa skipped
257 */
258 bool QuickTestResult::isFailed() const
259 {
260     return QTestResult::currentTestFailed();
261 }
262
263 /*!
264     \qmlproperty bool TestResult::skipped
265
266     This property returns true if the current test function was
267     marked as skipped; false otherwise.
268
269     \sa failed
270 */
271 bool QuickTestResult::isSkipped() const
272 {
273     return QTestResult::skipCurrentTest();
274 }
275
276 void QuickTestResult::setSkipped(bool skip)
277 {
278     QTestResult::setSkipCurrentTest(skip);
279     emit skippedChanged();
280 }
281
282 /*!
283     \qmlproperty int TestResult::passCount
284
285     This property returns the number of tests that have passed.
286
287     \sa failCount, skipCount
288 */
289 int QuickTestResult::passCount() const
290 {
291     return QTestLog::passCount();
292 }
293
294 /*!
295     \qmlproperty int TestResult::failCount
296
297     This property returns the number of tests that have failed.
298
299     \sa passCount, skipCount
300 */
301 int QuickTestResult::failCount() const
302 {
303     return QTestLog::failCount();
304 }
305
306 /*!
307     \qmlproperty int TestResult::skipCount
308
309     This property returns the number of tests that have been skipped.
310
311     \sa passCount, failCount
312 */
313 int QuickTestResult::skipCount() const
314 {
315     return QTestLog::skipCount();
316 }
317
318 /*!
319     \qmlproperty list<string> TestResult::functionsToRun
320
321     This property returns the list of function names to be run.
322 */
323 QStringList QuickTestResult::functionsToRun() const
324 {
325     return QTest::testFunctions;
326 }
327
328 /*!
329     \qmlmethod TestResult::reset()
330
331     Resets all pass/fail/skip counters and prepare for testing.
332 */
333 void QuickTestResult::reset()
334 {
335     if (!globalProgramName)     // Only if run via qmlviewer.
336         QTestResult::reset();
337 }
338
339 /*!
340     \qmlmethod TestResult::startLogging()
341
342     Starts logging to the test output stream and writes the
343     test header.
344
345     \sa stopLogging()
346 */
347 void QuickTestResult::startLogging()
348 {
349     // The program name is used for logging headers and footers if it
350     // is set.  Otherwise the test case name is used.
351     if (loggingStarted)
352         return;
353     QTestLog::startLogging();
354     loggingStarted = true;
355 }
356
357 /*!
358     \qmlmethod TestResult::stopLogging()
359
360     Writes the test footer to the test output stream and then stops logging.
361
362     \sa startLogging()
363 */
364 void QuickTestResult::stopLogging()
365 {
366     Q_D(QuickTestResult);
367     if (globalProgramName)
368         return;     // Logging will be stopped by setProgramName(0).
369     QTestResult::setCurrentTestObject(d->intern(d->testCaseName).constData());
370     QTestLog::stopLogging();
371 }
372
373 void QuickTestResult::initTestTable()
374 {
375     Q_D(QuickTestResult);
376     delete d->table;
377     d->table = new QTestTable;
378     //qmltest does not really need the column for data driven test
379     //add this to avoid warnings.
380     d->table->addColumn(qMetaTypeId<QString>(), "qmltest_dummy_data_column");
381 }
382
383 void QuickTestResult::clearTestTable()
384 {
385     Q_D(QuickTestResult);
386     delete d->table;
387     d->table = 0;
388 }
389
390 void QuickTestResult::finishTestData()
391 {
392     QTestResult::finishedCurrentTestData();
393 }
394
395 void QuickTestResult::finishTestDataCleanup()
396 {
397     QTestResult::finishedCurrentTestDataCleanup();
398 }
399
400 void QuickTestResult::finishTestFunction()
401 {
402     QTestResult::finishedCurrentTestFunction();
403 }
404
405 static QString qtestFixUrl(const QUrl &location)
406 {
407     if (location.isLocalFile()) // Use QUrl's logic for Windows drive letters.
408         return QDir::toNativeSeparators(location.toLocalFile());
409     return location.toString();
410 }
411
412 void QuickTestResult::fail
413     (const QString &message, const QUrl &location, int line)
414 {
415     QTestResult::addFailure(message.toLatin1().constData(),
416                             qtestFixUrl(location).toLatin1().constData(), line);
417 }
418
419 bool QuickTestResult::verify
420     (bool success, const QString &message, const QUrl &location, int line)
421 {
422     if (!success && message.isEmpty()) {
423         return QTestResult::verify
424             (success, "verify()", "",
425              qtestFixUrl(location).toLatin1().constData(), line);
426     } else {
427         return QTestResult::verify
428             (success, message.toLatin1().constData(), "",
429              qtestFixUrl(location).toLatin1().constData(), line);
430     }
431 }
432
433 bool QuickTestResult::fuzzyCompare(const QVariant &actual, const QVariant &expected, qreal delta)
434 {
435     if (actual.type() == QVariant::Color || expected.type() == QVariant::Color) {
436         if (!actual.canConvert(QVariant::Color) || !expected.canConvert(QVariant::Color))
437             return false;
438
439         //fuzzy color comparison
440         QColor act;
441         QColor exp;
442         bool ok(false);
443
444         QVariant var = QQml_colorProvider()->colorFromString(actual.toString(), &ok);
445         if (!ok)
446             return false;
447         act = var.value<QColor>();
448
449         QQml_colorProvider()->colorFromString(expected.toString(), &ok);
450         if (!ok)
451             return false;
452         exp = var.value<QColor>();
453
454         return ( qAbs(act.red() - exp.red()) <= delta
455               && qAbs(act.green() - exp.green()) <= delta
456               && qAbs(act.blue() - exp.blue()) <= delta
457               && qAbs(act.alpha() - exp.alpha()) <= delta);
458     } else {
459         //number comparison
460         bool ok = true;
461         qreal act = actual.toFloat(&ok);
462         if (!ok)
463             return false;
464
465         qreal exp = expected.toFloat(&ok);
466         if (!ok)
467             return false;
468
469         return (qAbs(act - exp) <= delta);
470     }
471
472     return false;
473 }
474
475 void QuickTestResult::stringify(QQmlV8Function *args)
476 {
477     if (args->Length() < 1)
478         args->returnValue(v8::Null());
479
480     v8::Local<v8::Value> value = (*args)[0];
481
482     QString result;
483     QV8Engine *engine = args->engine();
484
485     //Check for Object Type
486     if (value->IsObject()
487     && !value->IsFunction()
488     && !value->IsArray()
489     && !value->IsDate()
490     && !value->IsRegExp()) {
491         QVariant v = engine->toVariant(value, QMetaType::UnknownType);
492         if (v.isValid()) {
493             switch (v.type()) {
494             case QVariant::Vector3D:
495             {
496                 QVector3D v3d = v.value<QVector3D>();
497                 result = QString::fromLatin1("Qt.vector3d(%1, %2, %3)").arg(v3d.x()).arg(v3d.y()).arg(v3d.z());
498                 break;
499             }
500             default:
501                 result = v.toString();
502             }
503
504         } else {
505             result = QLatin1String("Object");
506         }
507     } else {
508         v8::Local<v8::String> jsstr = value->ToString();
509         QString tmp = engine->toString(jsstr);
510         if (value->IsArray())
511             result.append(QString::fromLatin1("[%1]").arg(tmp));
512         else
513             result.append(tmp);
514     }
515
516     args->returnValue(args->engine()->toString(result));
517 }
518
519 bool QuickTestResult::compare
520     (bool success, const QString &message,
521      const QVariant &val1, const QVariant &val2,
522      const QUrl &location, int line)
523 {
524     return QTestResult::compare
525         (success, message.toLocal8Bit().constData(),
526          QTest::toString(val1.toString().toLatin1().constData()),
527          QTest::toString(val2.toString().toLatin1().constData()),
528          "", "",
529          qtestFixUrl(location).toLatin1().constData(), line);
530 }
531
532 void QuickTestResult::skip
533     (const QString &message, const QUrl &location, int line)
534 {
535     QTestResult::addSkip(message.toLatin1().constData(),
536                          qtestFixUrl(location).toLatin1().constData(), line);
537     QTestResult::setSkipCurrentTest(true);
538 }
539
540 bool QuickTestResult::expectFail
541     (const QString &tag, const QString &comment, const QUrl &location, int line)
542 {
543     return QTestResult::expectFail
544         (tag.toLatin1().constData(),
545          QTest::toString(comment.toLatin1().constData()),
546          QTest::Abort, qtestFixUrl(location).toLatin1().constData(), line);
547 }
548
549 bool QuickTestResult::expectFailContinue
550     (const QString &tag, const QString &comment, const QUrl &location, int line)
551 {
552     return QTestResult::expectFail
553         (tag.toLatin1().constData(),
554          QTest::toString(comment.toLatin1().constData()),
555          QTest::Continue, qtestFixUrl(location).toLatin1().constData(), line);
556 }
557
558 void QuickTestResult::warn(const QString &message, const QUrl &location, int line)
559 {
560     QTestLog::warn(message.toLatin1().constData(), qtestFixUrl(location).toLatin1().constData(), line);
561 }
562
563 void QuickTestResult::ignoreWarning(const QString &message)
564 {
565     QTestLog::ignoreMessage(QtWarningMsg, message.toLatin1().constData());
566 }
567
568 void QuickTestResult::wait(int ms)
569 {
570     QTest::qWait(ms);
571 }
572
573 void QuickTestResult::sleep(int ms)
574 {
575     QTest::qSleep(ms);
576 }
577
578 bool QuickTestResult::waitForRendering(QQuickItem *item, int timeout)
579 {
580     Q_ASSERT(item);
581
582     return qWaitForSignal(item->window(), SIGNAL(frameSwapped()), timeout);
583 }
584
585 void QuickTestResult::startMeasurement()
586 {
587     Q_D(QuickTestResult);
588     delete d->benchmarkData;
589     d->benchmarkData = new QBenchmarkTestMethodData();
590     QBenchmarkTestMethodData::current = d->benchmarkData;
591     d->iterCount = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
592     d->results.clear();
593 }
594
595 void QuickTestResult::beginDataRun()
596 {
597     QBenchmarkTestMethodData::current->beginDataRun();
598 }
599
600 void QuickTestResult::endDataRun()
601 {
602     Q_D(QuickTestResult);
603     QBenchmarkTestMethodData::current->endDataRun();
604     if (d->iterCount > -1)  // iteration -1 is the warmup iteration.
605         d->results.append(QBenchmarkTestMethodData::current->result);
606
607     if (QBenchmarkGlobalData::current->verboseOutput) {
608         if (d->iterCount == -1) {
609             qDebug() << "warmup stage result      :" << QBenchmarkTestMethodData::current->result.value;
610         } else {
611             qDebug() << "accumulation stage result:" << QBenchmarkTestMethodData::current->result.value;
612         }
613     }
614 }
615
616 bool QuickTestResult::measurementAccepted()
617 {
618     return QBenchmarkTestMethodData::current->resultsAccepted();
619 }
620
621 static QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container)
622 {
623     const int count = container.count();
624     if (count == 0)
625         return QBenchmarkResult();
626
627     if (count == 1)
628         return container.at(0);
629
630     QList<QBenchmarkResult> containerCopy = container;
631     qSort(containerCopy);
632
633     const int middle = count / 2;
634
635     // ### handle even-sized containers here by doing an aritmetic mean of the two middle items.
636     return containerCopy.at(middle);
637 }
638
639 bool QuickTestResult::needsMoreMeasurements()
640 {
641     Q_D(QuickTestResult);
642     ++(d->iterCount);
643     if (d->iterCount < QBenchmarkGlobalData::current->adjustMedianIterationCount())
644         return true;
645     if (QBenchmarkTestMethodData::current->resultsAccepted())
646         QTestLog::addBenchmarkResult(qMedian(d->results));
647     return false;
648 }
649
650 void QuickTestResult::startBenchmark(RunMode runMode, const QString &tag)
651 {
652     QBenchmarkTestMethodData::current->result = QBenchmarkResult();
653     QBenchmarkTestMethodData::current->resultAccepted = false;
654     QBenchmarkGlobalData::current->context.tag = tag;
655     QBenchmarkGlobalData::current->context.slotName = functionName();
656
657     Q_D(QuickTestResult);
658     delete d->benchmarkIter;
659     d->benchmarkIter = new QTest::QBenchmarkIterationController
660         (QTest::QBenchmarkIterationController::RunMode(runMode));
661 }
662
663 bool QuickTestResult::isBenchmarkDone() const
664 {
665     Q_D(const QuickTestResult);
666     if (d->benchmarkIter)
667         return d->benchmarkIter->isDone();
668     else
669         return true;
670 }
671
672 void QuickTestResult::nextBenchmark()
673 {
674     Q_D(QuickTestResult);
675     if (d->benchmarkIter)
676         d->benchmarkIter->next();
677 }
678
679 void QuickTestResult::stopBenchmark()
680 {
681     Q_D(QuickTestResult);
682     delete d->benchmarkIter;
683     d->benchmarkIter = 0;
684 }
685
686 QObject *QuickTestResult::grabImage(QQuickItem *item)
687 {
688     if (item) {
689         QQuickWindow *window = item->window();
690         QImage grabbed = window->grabWindow();
691         QRectF rf(item->x(), item->y(), item->width(), item->height());
692         rf = rf.intersected(QRectF(0, 0, grabbed.width(), grabbed.height()));
693         return new QuickTestImageObject(grabbed.copy(rf.toAlignedRect()));
694     }
695     return 0;
696 }
697 namespace QTest {
698     void qtest_qParseArgs(int argc, char *argv[], bool qml);
699 };
700
701 void QuickTestResult::parseArgs(int argc, char *argv[])
702 {
703     if (!QBenchmarkGlobalData::current)
704         QBenchmarkGlobalData::current = &globalBenchmarkData;
705     QTest::qtest_qParseArgs(argc, argv, true);
706 }
707
708 void QuickTestResult::setProgramName(const char *name)
709 {
710     if (name) {
711         QTestResult::reset();
712     } else if (!name && loggingStarted) {
713         QTestResult::setCurrentTestObject(globalProgramName);
714         QTestLog::stopLogging();
715         QTestResult::setCurrentTestObject(0);
716     }
717     globalProgramName = name;
718     QTestResult::setCurrentTestObject(globalProgramName);
719 }
720
721 void QuickTestResult::setCurrentAppname(const char *appname)
722 {
723     QTestResult::setCurrentAppname(appname);
724 }
725
726 int QuickTestResult::exitCode()
727 {
728 #if defined(QTEST_NOEXITCODE)
729     return 0;
730 #else
731     // make sure our exit code is never going above 127
732     // since that could wrap and indicate 0 test fails
733     return qMin(QTestLog::failCount(), 127);
734 #endif
735 }
736
737 #include "quicktestresult.moc"
738
739 QT_END_NAMESPACE