/**************************************************************************** ** ** 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 the Qt Autotester project. ** ** $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 "mainwindow.h" #include "test.h" #include "qtdirectory.h" #include "logger.h" #include #include bool Test::runQMake = true; QString Test::m_globalArguments; /************************************************************************************************** **************************************************************************************************/ Test::Test(const QString &name, const QString &path, const QString &pro) : QObject(), testName(name), testPath(path), teststatus(Initial), p(0), testPro(pro), testIsAvailable(true) { } /************************************************************************************************** **************************************************************************************************/ Test::~Test() { if (p) delete p; } /************************************************************************************************** **************************************************************************************************/ QString Test::executable() const { QString exe = testPath; #ifdef Q_WS_WIN //prepend debug or release if needed if (!MainWindow::getCurrentProfile()->variant().isEmpty()) exe += QLatin1Char('/') + MainWindow::getCurrentProfile()->variant(); #endif exe += QLatin1String("/tst_") + testName; //append OS specific suffix #ifdef Q_WS_MAC QFileInfo f(MainWindow::getCurrentQtVersion()->testBuildDir() + exe); if (!f.exists()) exe += QLatin1String(".app"); #endif #ifdef Q_WS_WIN exe += QLatin1String(".exe"); #endif return exe; } /************************************************************************************************** **************************************************************************************************/ void Test::run() { Logger::getIt()->log(QString("Running auto-test %1").arg(testName)); //clear previous results r.pass = false; setStatus(Initial); int status = buildTest(); if (status != 0 || teststatus == Skipped) { emit statusChanged(teststatus); return; } runTest(); Logger::getIt()->log(" "); // make sure the test view is in a coherent state emit statusChanged(teststatus); } /************************************************************************************************** **************************************************************************************************/ void Test::cancelRun() { if (p && p->state() != QProcess::NotRunning) p->kill(); setStatus(Skipped); } /************************************************************************************************** **************************************************************************************************/ int Test::buildTest() { setStatus(Building); QString absoluteTestPath = MainWindow::getCurrentQtVersion()->testBuildDir() + testPath; QDir testDir(absoluteTestPath); if (!testDir.exists()) testDir.mkpath(absoluteTestPath); int ret = 0; if (runQMake) ret = runExecutable(absoluteTestPath, MainWindow::getCurrentQtVersion()->qmake(), MainWindow::getCurrentQtVersion()->binDir(), QStringList() << "-r" << MainWindow::getCurrentQtVersion()->testSrcDir() + testPro); #ifdef Q_WS_WIN if (ret == 0) { ret = runExecutable(absoluteTestPath, MainWindow::getCurrentQtVersion()->make(), MainWindow::getCurrentQtVersion()->makeDir(), QStringList() << MainWindow::getCurrentProfile()->variant()); } #else ret = runExecutable(absoluteTestPath, "/usr/bin/make", "", QStringList()); #endif if (ret != 0 && teststatus != Skipped) { setStatus(BuildError); } return ret; } /************************************************************************************************** **************************************************************************************************/ int Test::runTest() { setStatus(Running); int code = 0; if (!MainWindow::getCurrentQtVersion()->isCrossCompiled()) { //A desktop platform, run the test directly and capture it's stdout code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, MainWindow::getCurrentQtVersion()->testBuildDir() + executable(), MainWindow::getCurrentQtVersion()->libDir(), QStringList() << "-xml" << "-flush" << m_globalArguments.split(" ", QString::SkipEmptyParts), true); } else if (MainWindow::getCurrentQtVersion()->remoteOS() == "symbian") { QFile results; if (!MainWindow::getCurrentProfile()->variant().contains("winscw")) { //make sis package for phone testing #ifdef Q_WS_WIN code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, MainWindow::getCurrentQtVersion()->make(), MainWindow::getCurrentQtVersion()->makeDir() + ";" + MainWindow::getCurrentQtVersion()->binDir(), QStringList() << "sis"); #else code = runExecutable(testPath, "/usr/bin/make", "", QStringList() << "sis"); #endif //remote test using runonphone if(code == 0) { code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, "runonphone", MainWindow::getCurrentQtVersion()->libDir(), QStringList() << "--sis" << QDir::cleanPath(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath) + QString("/tst_" + testName + ".sis") << "--download" << "c:\\data\\testresults.xml" << QDir::cleanPath(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath) + QString("/testresults.xml") << QString("tst_") + testName + ".exe" << "-flush" << "-o" << "c:\\data\\testresults.xml" << "-xml"); } results.setFileName(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath + "/testresults.xml"); } else { //run emulator executable QDir epocroot(qgetenv("EPOCROOT")); QDir path = epocroot; path.cd(QLatin1String("epoc32/release/winscw/")); if(MainWindow::getCurrentProfile()->variant().contains("debug")) path.cd(QLatin1String("udeb/")); else path.cd(QLatin1String("urel/")); code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, path.absoluteFilePath(QString("tst_" + testName + ".exe")), MainWindow::getCurrentQtVersion()->libDir(), QStringList() << "--" << "-flush" << "-xml" << "-o" << "c:\\data\\testresults.xml"); results.setFileName(epocroot.absoluteFilePath(QLatin1String("epoc32/winscw/c/data/testresults.xml"))); } //read xml results file if(results.open(QIODevice::ReadOnly)) { QString tmp = results.readAll(); output += tmp; results.close(); results.remove(); Logger::getIt()->log(makePrintable(tmp)); } else { setStatus(RunError); return -1; } } //else if wince, do something with cetest if (code < 0) { if (teststatus == Skipped) { Logger::getIt()->log(QString("%1 has been skipped").arg(testName)); } else { setStatus(RunError); Logger::getIt()->log(QString("An error has occured while running %1").arg(testName)); } return -1; } processResult(output); if (r.pass) setStatus(TestPass); else setStatus(TestFail); return 0; } /************************************************************************************************** **************************************************************************************************/ int Test::runExecutable(const QString &workdir, const QString &executable, const QString &env, const QStringList &arguments, bool ignoreErrorChannel) { Logger::getIt()->log(QString("Starting \"%1\" %2").arg(executable).arg(arguments.join(" "))); delete p; p = new QProcess; connect(p, SIGNAL(readyRead()), this, SLOT(onProcessReadyRead())); if (!ignoreErrorChannel) p->setProcessChannelMode(QProcess::MergedChannels); QProcessEnvironment proEnv = QProcessEnvironment::systemEnvironment(); if (!env.isEmpty()) { #if defined(Q_WS_WIN) proEnv.insert("PATH", QDir::toNativeSeparators(env) + ";" + proEnv.value("PATH")); #elif defined(Q_WS_MAC) proEnv.insert("DYLD_LIBRARY_PATH", env); #else proEnv.insert("LD_LIBRARY_PATH", env); #endif } proEnv.insert("QTDIR", MainWindow::getCurrentQtVersion()->buildDir()); p->setProcessEnvironment(proEnv); if (!workdir.isEmpty()) p->setWorkingDirectory(workdir); if (arguments.isEmpty()) p->start(executable); else p->start(executable, arguments); if (p->waitForFinished(-1) == false) { Logger::getIt()->log(QString("\"%1\" exited with code %2").arg(executable).arg(p->exitCode())); return -1; } if (p->error() == QProcess::Crashed) { Logger::getIt()->log(QString("\"%1\" exited with code %2").arg(executable).arg(p->exitCode())); return -1; } int ret = p->exitCode(); Logger::getIt()->log(QString("\"%1\" exited with code %2").arg(executable).arg(ret)); return ret; } /************************************************************************************************** **************************************************************************************************/ void Test::setStatus(Test::TestStatus st) { if (st == teststatus) return; teststatus = st; emit statusChanged(teststatus); } /************************************************************************************************** **************************************************************************************************/ void Test::processResult(const QString &result) { r.pass = true; r.failures.clear(); r.messages.clear(); QString res = result; res.remove(0, result.indexOf("testSrcDir() + testPro; static QHash cache; if (!cache.contains(profile)) { QFile pro(profile); bool found = false; if (pro.open(QIODevice::ReadOnly | QIODevice::Text)) { while (!pro.atEnd()) { QString line = pro.readLine(); if (line.contains("parallel_test")) { found = true; break; } } } else { qWarning() << "Cannot open: " << profile; } cache.insert(profile, found); } return cache[profile]; } /************************************************************************************************** **************************************************************************************************/ QString Test::makePrintable(const QString &out) { static QString testName; static QString currentFunction; static QString file; static QString linenb; static QString desc; static QString data; static int passed; static int failed; static int skipped; if (out.isEmpty()) return ""; QString res; QString line_break; QTextStream dummy(&line_break); dummy << endl; QStringList list = out.split(line_break); bool inDescription = false; foreach (const QString &line, list) { QString trimmed = line.trimmed(); if (trimmed.isEmpty() || trimmed.startsWith("", ""); desc += "
   " + trimmed; } else { res += trimmed + "
"; } } else if (trimmed == "") { res += QString("Totals: %1 passed, %2 failed, %3 skipped
").arg(passed).arg(failed).arg(skipped); res += "********* Finished testing of " + testName + " *********
"; } else if (trimmed == "") { res += "
"; } else if (trimmed == "" || trimmed == "") { res += ": " + testName + "::" + currentFunction + "(" + data + ") " + desc + "
"; if (!file.isEmpty()) res += "   Loc: [" + file + "(" + linenb + ")]
"; } else if (trimmed == "") { inDescription = false; } else { QDomDocument doc; doc.setContent(trimmed); QDomElement docElem = doc.documentElement(); QString tagName = docElem.tagName(); if (tagName == "TestCase") { passed = 0; failed = 0; skipped = 0; testName = docElem.attribute("name"); res += "********* Start testing of " + testName + " *********
"; } else if (tagName == "Environment") { res += "Config: "; } else if (tagName == "QtVersion") { res += "Using Qt " + docElem.text() + ", "; } else if (tagName == "QTestVersion") { res += "QTest library " + docElem.text(); } else if (tagName == "TestFunction") { currentFunction = docElem.attribute("name"); desc = ""; data = ""; } else if (tagName == "Incident") { QString type = docElem.attribute("type"); file = docElem.attribute("file"); linenb = docElem.attribute("line"); if (type == "pass") { ++passed; res += "PASS   "; } else if (type == "xfail") { res += "XFAIL  "; } else if (type == "fail") { ++failed; res += "FAIL!  "; } if (trimmed.endsWith("/>")) res += ": " + testName + "::" + currentFunction + "()
"; } else if (tagName == "Message") { file = docElem.attribute("file"); linenb = docElem.attribute("line"); QString type = docElem.attribute("type"); if (type == "qwarn") { res += "QWARN  "; } else if (type == "qdebug") { res += "QDEBUG "; } else if (type == "warn") { res += "WARNING"; } else if (type == "qfatal") { res += "QFATAL "; } else if (type == "skip") { ++skipped; res += "SKIP   "; } if (trimmed.endsWith("/>")) res += ": " + testName + "::" + currentFunction + "()
"; } else if (tagName == "DataTag") { data = docElem.text(); } else if (tagName == "Description") { inDescription = true; desc = docElem.text(); if (desc.isEmpty()) { desc = trimmed; desc.replace("")) res.chop(4); return res; } /************************************************************************************************** **************************************************************************************************/ void Test::onProcessReadyRead() { QString read = p->readAll(); output += read; Logger::getIt()->log(makePrintable(read)); }