/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of QtUiTest. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "qscriptengine.h" #include "qscriptvalue.h" #include "qscriptvalueiterator.h" #include "qscriptcontext.h" #include "qscriptcontextinfo.h" #include "qtscript_bindings.h" QStringList builtinFiles; void QtScript::addInternalFile(QString const &filename) { builtinFiles << filename; } static QString readFile(const QString &filename) { QFile file(filename); if (!file.open(QFile::ReadOnly)) return QString(); QTextStream stream(&file); return stream.readAll(); } static void appendCString(QVector *v, const char *s) { char c; do { c = *(s++); *v << c; } while (c != '\0'); } void QtScript::getLocation(QScriptContext *ctx, QString *fileName, int *lineNumber) { Q_ASSERT(ctx); if (fileName) *fileName = QString(); if (lineNumber) *lineNumber = 0; while (ctx) { QScriptContextInfo ctxInfo(ctx); QString fn = ctxInfo.fileName(); if (!fn.isEmpty() && !builtinFiles.contains(fn)) { if (fileName) *fileName = fn; if (lineNumber) *lineNumber = ctxInfo.lineNumber(); return; } ctx = ctx->parentContext(); } } QMetaObject QtScriptTest::staticMetaObject; Q_GLOBAL_STATIC(QVector, qt_meta_data_QtScriptTest) Q_GLOBAL_STATIC(QVector, qt_meta_stringdata_QtScriptTest) const QMetaObject *QtScriptTest::metaObject() const { return &staticMetaObject; } void *QtScriptTest::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_QtScriptTest()->constData())) return static_cast(const_cast(this)); return QObject::qt_metacast(_clname); } int QtScriptTest::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { const QMetaObject *mo = metaObject(); QMetaMethod method = mo->method(mo->methodOffset() + _id); QByteArray sig = method.signature(); QString name = sig.left(sig.lastIndexOf('(')); QScriptValue testcase = m_engine->globalObject().property("testcase"); QScriptValue val = testcase.property(name); if (name.endsWith("_data")) { QTest::addColumn("dummy"); QScriptValueIterator it(val); while (it.hasNext()) { it.next(); QString tag = it.name(); QTest::newRow(tag.toLatin1()); } } else { QScriptValue args; QScriptValue data = testcase.property(name + "_data"); if (data.isObject()) { QString tag = QTest::currentDataTag(); QScriptValue v = data.property(tag); if (v.isValid()) { if (v.isArray()) { args = v; } else { args = m_engine->newArray(); args.setProperty(0, v); } } } if (!args.isArray()) args = m_engine->newArray(); QScriptValue ret; if (name == "init") { ret = m_engine->evaluate("init_global();"); } else if (name == "initTestCase") { ret = m_engine->evaluate("initTestCase_global();"); } else if (name != "cleanup" && name != "cleanupTestCase") { m_engine->evaluate("qtuitest_pre_test_function();"); } if (!ret.isError() || ret.property("name").toString() == "QTestFailure") { ret = val.call(m_engine->globalObject(), args); QTest::compare_helper( true, "expectFail() not followed by a test statement", m_testFilePath.toAscii(), property("expectFailLineNumber").toInt() ); } if (name != "init" && name != "initTestCase" && name != "cleanup" && name != "cleanupTestCase") { m_engine->evaluate("qtuitest_post_test_function();"); } if (name == "cleanup") { m_engine->evaluate("cleanup_global();"); } else if (name == "cleanupTestCase") { m_engine->evaluate("cleanupTestCase_global();"); } if (ret.isError() && (ret.property("name").toString() != "QTestFailure")) { QString backtrace = m_engine->uncaughtExceptionBacktrace().join("\n"); QString message = ret.toString() // xxx makes the failure message look cluttered; correct fix // xxx is to implement proper generic saving of backtrace info // xxx in test results // + "\n" + backtrace ; QString fileName = "unknown"; int lineNumber = -1; QRegExp locationRe("@([^:]+):([0-9]+)"); if (-1 != locationRe.indexIn(backtrace)) { fileName = locationRe.cap(1); lineNumber = locationRe.cap(2).toInt(); } QTest::qFail(qPrintable(message), qPrintable(fileName), lineNumber); } } _id = -1; } return _id; } QtScriptTest::QtScriptTest(QString const &testFilePath, QString const &scriptData, QScriptEngine *engine) : QObject(), m_testFilePath(testFilePath), m_engine(engine), m_status(StatusNotStarted) { if (m_testFilePath.isEmpty()) m_testFilePath = qgetenv("Q_TEST_FILE"); if (m_testFilePath.isEmpty()) m_testFilePath="testcase.js"; // m_engine->importExtension("qt.core"); QScriptValue qtestObject = m_engine->newObject(); qtestObject.setProperty("SkipSingle", QScriptValue(m_engine, QTest::SkipSingle)); qtestObject.setProperty("SkipAll", QScriptValue(m_engine, QTest::SkipAll)); qtestObject.setProperty("Abort", QScriptValue(m_engine, QTest::Abort)); qtestObject.setProperty("Continue", QScriptValue(m_engine, QTest::Continue)); m_engine->globalObject().setProperty("QTest", qtestObject); m_engine->evaluate("function QTestFailure() { Error.apply(this, arguments); }" "QTestFailure.prototype = new Error();" "QTestFailure.prototype.name = 'QTestFailure';"); QStringList slotNames; QString script = scriptData; if (script.isEmpty()) script = readFile(m_testFilePath); if (!script.isEmpty()) { QScriptSyntaxCheckResult synChk = m_engine->checkSyntax(script); if (synChk.state() != QScriptSyntaxCheckResult::Valid) { QTestIDE::instance()->scriptSyntaxError(synChk.errorMessage(), m_testFilePath, synChk.errorLineNumber()); m_status = StatusSyntaxError; return; } QScriptValue ret = m_engine->evaluate(script, m_testFilePath); QString error; if (m_engine->hasUncaughtException()) { error = m_engine->uncaughtException().toString(); } else if (ret.isError()) { error = ret.toString(); } if (!error.isEmpty()) { QString backtrace = m_engine->uncaughtExceptionBacktrace().join("\n"); qWarning("%s\n%s", qPrintable(error), qPrintable(backtrace)); m_status = StatusException; return; } QScriptValue testcase = m_engine->globalObject().property("testcase"); QScriptValueIterator it(testcase); while (it.hasNext()) { it.next(); QScriptValue val = it.value(); if (val.isFunction() || (val.isObject() && it.name().endsWith("_data"))) slotNames << it.name(); } QStringList requiredSlots; requiredSlots << "init" << "cleanup" << "initTestCase" << "cleanupTestCase"; foreach (QString required, requiredSlots) { if (!slotNames.contains(required)) { m_engine->evaluate("testcase." + required + " = function() {}"); slotNames << required; } } } else { qWarning("*** Failed to read testcase!"); } QVector *data = qt_meta_data_QtScriptTest(); data->clear(); // content: *data << 1 // revision << 0 // classname << 0 << 0 // classinfo << slotNames.count() << 10 // methods << 0 << 0 // properties << 0 << 0 // enums/sets ; QString testcaseName = QFileInfo(m_testFilePath).baseName(); QVector *stringdata = qt_meta_stringdata_QtScriptTest(); stringdata->clear(); appendCString(stringdata, testcaseName.toLocal8Bit() ); int namelen = stringdata->size(); appendCString(stringdata, ""); // slots: signature, parameters, type, tag, flags foreach (QString slotName, slotNames) { QString slot = slotName + QLatin1String("()"); *data << stringdata->size() << namelen << namelen << namelen << 0x08; appendCString(stringdata, slot.toLatin1()); } *data << 0; // eod // initialize staticMetaObject staticMetaObject.d.superdata = &QObject::staticMetaObject; staticMetaObject.d.stringdata = stringdata->constData(); staticMetaObject.d.data = data->constData(); staticMetaObject.d.extradata = 0; m_status = StatusNormal; } QtScriptTest::~QtScriptTest() { } QtScriptTest::Status QtScriptTest::status() { return m_status; }