diff options
Diffstat (limited to 'kunittest/runnergui.cpp')
-rw-r--r-- | kunittest/runnergui.cpp | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/kunittest/runnergui.cpp b/kunittest/runnergui.cpp new file mode 100644 index 00000000..ef5afe98 --- /dev/null +++ b/kunittest/runnergui.cpp @@ -0,0 +1,433 @@ +/** + * Copyright (C) 2005 Jeroen Wijnhout <[email protected]> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <qmetaobject.h> +#include <qregexp.h> +#include <qpushbutton.h> +#include <qtextedit.h> +#include <qlabel.h> +#include <qprogressbar.h> +#include <qcombobox.h> + +#include <dcopclient.h> +#include <dcopobject.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kmessagebox.h> + +#include <kunittest/tester.h> + +#include "dcopinterface.h" +#include "runnergui.h" +#include "testerwidget.h" + +namespace KUnitTest +{ + const int g_nameColumn = 0; + const int g_finishedColumn = 1; + const int g_skippedColumn = 2; + const int g_failedColumn = 3; + const int g_xfailedColumn = 4; + const int g_passedColumn = 5; + const int g_xpassedColumn = 6; + + /*! The DCOP implementation for the RunnerGUI. + */ + class RunnerGUIDCOPImpl : virtual public DCOPInterface + { + public: + RunnerGUIDCOPImpl(RunnerGUI *rg) : m_rg(rg) + { + // set the DCOP object id + setObjId("Runner"); + } + + /*! This DCOP method adds debug info to a given test case. + * @param name The name of the test. + * @param info The debug info. + */ + bool addDebugInfo(const QString &name, const QString &info) + { + Tester *tester = Runner::self()->registry().find(name.local8Bit()); + if ( tester == 0L ) return false; + + tester->results()->addDebugInfo(info); + + return true; + } + + bool addSlotDebugInfo(const QString &name, const QString &slt, const QString &info) + { + Tester *tester = Runner::self()->registry().find(name.local8Bit()); + + if ( tester == 0L ) return false; + if ( ! tester->inherits("KUnitTest::SlotTester") ) return false; + + SlotTester *sltester = static_cast<SlotTester*>(tester); + sltester->results(slt.local8Bit())->addDebugInfo(info); + + return true; + } + + private: + RunnerGUI *m_rg; + }; + + RunnerGUI::RunnerGUI(QWidget *parent) : QHBox(parent) + { + m_dcop = new RunnerGUIDCOPImpl(this); + + m_testerWidget = new TesterWidget(this); + setGeometry(0, 0, 700, 500); + + // file the combo box + m_testerWidget->selectCombo()->insertItem("All suites/modules . . ."); + m_testerWidget->selectCombo()->insertItem("Selected tests . . ."); + + RegistryIteratorType it(Runner::self()->registry()); + QStringList suites; + for ( ; it.current(); ++it ) + { + addTester(it.currentKey(), it.current()); + + QString test = it.currentKey(); + int index = test.find("::"); + if ( index != -1 ) test = test.left(index); + + if ( suites.contains(test) == 0 ) + suites.append(test); + } + + for ( uint i = 0; i < suites.count(); ++i ) + m_testerWidget->selectCombo()->insertItem(suites[i]); + + // configure the resultslist + m_testerWidget->resultList()->setAllColumnsShowFocus(true); + m_testerWidget->resultList()->setSelectionMode(QListView::Extended); + m_testerWidget->resultList()->setRootIsDecorated(true); + m_testerWidget->resultList()->setColumnAlignment(g_finishedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_skippedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_failedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_xfailedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_passedColumn, Qt::AlignHCenter); + m_testerWidget->resultList()->setColumnAlignment(g_xpassedColumn, Qt::AlignHCenter); + + // set the text in the results label + fillResultsLabel(); + + // init the progress bar + configureProgressBar(Runner::self()->numberOfTestCases(), 0); + + connect(Runner::self(), SIGNAL(finished(const char *, Tester *)), this, SLOT(addTestResult(const char *, Tester *))); + connect(m_testerWidget->resultList(), SIGNAL(clicked(QListViewItem *)), this, SLOT(showDetails(QListViewItem *))); + connect(m_testerWidget, SIGNAL(run()), this, SLOT(runSuite())); + connect(m_testerWidget->details(), SIGNAL(doubleClicked(int, int)), this, SLOT(doubleClickedOnDetails(int, int))); + } + + RunnerGUI::~RunnerGUI() + { + delete m_dcop; + } + + void RunnerGUI::configureProgressBar(int steps, int progress) + { + m_testerWidget->progressBar()->setTotalSteps(steps); + m_testerWidget->progressBar()->setProgress(progress); + } + + void RunnerGUI::fillResultsLabel() + { + if ( Runner::self()->numberOfTests() > 0 ) + m_testerWidget->resultsLabel()->setText( + QString("Test cases: %1 | Tests performed: %5, Skipped: <font color=\"#f7a300\">%4</font> | Passed: <font color=\"#009900\">%2</font>, Failed: <font color=\"#990000\">%3</font>") + .arg(Runner::self()->numberOfTestCases()) + .arg(Runner::self()->numberOfPassedTests()) + .arg(Runner::self()->numberOfFailedTests()) + .arg(Runner::self()->numberOfSkippedTests()) + .arg(Runner::self()->numberOfTests()) ); + else + m_testerWidget->resultsLabel()->setText(QString("Test cases: %1").arg(Runner::self()->numberOfTestCases())); + } + + void RunnerGUI::addTestResult(const char *name, Tester *test) + { + QStringList scopes = QStringList::split("::", name); + QString suite = scopes[0]; + + // find the suite item + QListViewItem *item = 0L; + for ( uint i = 0; i < scopes.count(); ++i ) + item = getItem(scopes[i], item); + + if ( test->inherits("KUnitTest::SlotTester") ) + { + SlotTester *sltest = static_cast<SlotTester*>(test); + TestResultsListIteratorType it(sltest->resultsList()); + QListViewItem *slotItem = 0L; + for ( ; it.current(); ++it) + { + slotItem = getItem(it.currentKey(), item); + setSummary(slotItem, it.current()); + } + } + else + setSummary(item, test->results()); + + fillResultsLabel(); + m_testerWidget->progressBar()->setProgress(m_testerWidget->progressBar()->progress() + 1); + } + + void RunnerGUI::addTester(const char *name, Tester *test) + { + QStringList scopes = QStringList::split("::", name); + QString suite = scopes[0]; + + // find the suite item + QListViewItem *item = 0L; + for ( uint i = 0; i < scopes.count(); ++i ) + item = getItem(scopes[i], item); + + if ( test->inherits("KUnitTest::SlotTester") ) + { + QStrList allSlots = test->metaObject()->slotNames(); + for ( char *sl = allSlots.first(); sl; sl = allSlots.next() ) + { + if ( QString(sl).startsWith("test") ) + getItem(sl, item); + } + } + } + + QListViewItem *RunnerGUI::getItem(const QString &name, QListViewItem *item /*= 0L*/) + { + QListViewItem *parent = item; + + if ( item == 0L ) item = m_testerWidget->resultList()->firstChild(); + else item = item->firstChild(); + + while ( item && (item->text(g_nameColumn) != name) ) + item = item->nextSibling(); + + // item not found, create it + if ( item == 0L ) + { + if ( parent == 0L ) + item = new QListViewItem(m_testerWidget->resultList()); + else + item = new QListViewItem(parent); + + item->setText(g_nameColumn, name); + } + + return item; + } + + void RunnerGUI::reset() + { + QListViewItemIterator it( m_testerWidget->resultList() ); + while ( it.current() ) + { + QListViewItem *item = it.current(); + item->setText(g_finishedColumn, "0"); + item->setText(g_skippedColumn, "0"); + item->setText(g_failedColumn, "0"); + item->setText(g_xfailedColumn, "0"); + item->setText(g_passedColumn, "0"); + item->setText(g_xpassedColumn, "0"); + item->setPixmap(g_nameColumn, QPixmap()); + ++it; + } + } + + void RunnerGUI::setSummary(QListViewItem *item, TestResults *res) + { + if ( item == 0L ) return; + + bool ok; + + int val = item->text(g_finishedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_finishedColumn, QString::number(val + res->testsFinished())); + + val = item->text(g_skippedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_skippedColumn, QString::number(val + res->skipped())); + + val = item->text(g_passedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_passedColumn, QString::number(val + res->passed())); + + val = item->text(g_failedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_failedColumn, QString::number(val + res->errors())); + + val = item->text(g_xfailedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_xfailedColumn, QString::number(val + res->xfails())); + + val = item->text(g_xpassedColumn).toInt(&ok); if (!ok) val = 0; + item->setText(g_xpassedColumn, QString::number(val + res->xpasses())); + + bool passed = (item->text(g_failedColumn).toInt(&ok) + item->text(g_xfailedColumn).toInt(&ok)) == 0; + item->setPixmap(g_nameColumn, passed ? SmallIcon("button_ok") : SmallIcon("button_cancel") ); + + setSummary(item->parent(), res); + } + + QString RunnerGUI::fullName(QListViewItem *item) + { + QString name = item->text(g_nameColumn); + while ( (item = item->parent()) != 0L ) + name = item->text(g_nameColumn) + "::" + name; + + return name; + } + + void RunnerGUI::runSuite() + { + Runner::self()->reset(); + reset(); + + if ( m_testerWidget->selectCombo()->currentItem() == 0 ) + { + configureProgressBar(Runner::self()->numberOfTestCases(), 0); + Runner::self()->runTests(); + } + else if ( m_testerWidget->selectCombo()->currentItem() == 1 ) + { + QListViewItemIterator it( m_testerWidget->resultList() ); + QStringList prefixes; + while ( it.current() ) + { + QListViewItem *item = it.current(); + if ( item->isSelected() ) + { + QString prefix = fullName(item); + if ( prefix.endsWith("()") ) + { + int index = prefix.findRev("::"); + prefix = prefix.left(index); + } + prefixes << prefix; + } + + ++it; + } + + configureProgressBar(prefixes.count(), 0); + for ( uint i = 0; i < prefixes.count(); ++i ) + Runner::self()->runMatchingTests(prefixes[i]); + } + else + { + QString suite = m_testerWidget->selectCombo()->currentText(); + QStringList tests; + RegistryIteratorType it(Runner::self()->registry()); + for ( ; it.current(); ++it ) + if ( QString(it.currentKey()).startsWith(suite) ) + tests.append(it.currentKey()); + + configureProgressBar(tests.count(), 0); + + for ( uint i = 0; i < tests.count(); ++i ) + Runner::self()->runTest(tests[i].local8Bit()); + } + + showDetails(m_testerWidget->resultList()->currentItem()); + } + + void RunnerGUI::showDetails(QListViewItem *item) + { + if ( item == 0L ) return; + + QString name = fullName(item); + if ( name.endsWith("()") ) name = fullName(item->parent()); + + Tester *tester = Runner::self()->registry().find(name.local8Bit()); + + if ( tester == 0L ) return; + + TestResults *res = 0L; + if ( tester->inherits("KUnitTest::SlotTester") ) + res = static_cast<SlotTester*>(tester)->results(item->text(g_nameColumn).local8Bit()); + else + res = tester->results(); + + if ( tester == 0L ) + m_testerWidget->details()->setText("No test found with name: " + fullName(item)); + else + { + QTextEdit *te = m_testerWidget->details(); + + te->clear(); + + te->append("<qt><a name=\"errors\"><font color=\"#990000\">Errors</font></a>:<br></qt>"); + appendList(te, res->errorList()); + + te->append("<qt><br><hr><font color=\"#c2c939\">Expected to fail</font>:<br></qt>"); + appendList(te, res->xfailList()); + + te->append("<qt><br><hr><font color=\"#BF00B5\">Unexpected Success</font>:<br></qt>"); + appendList(te, res->xpassList()); + + te->append("<qt><br><hr><font color=\"#009900\">Success</font>:<br></qt>"); + appendList(te, res->successList()); + + te->append("<qt><br><hr><font color=\"#F7A300\">Skipped</font>:<br></qt>"); + appendList(te, res->skipList()); + + te->append("<qt><br><hr><font color=\"#000099\">Debug</font>:<br></qt>"); + + te->append(res->debugInfo()); + + te->scrollToAnchor("errors"); + } + } + + void RunnerGUI::appendList(QTextEdit *te, const QStringList &list) + { + for ( uint i = 0; i < list.count(); ++i ) + te->append(list[i]); + } + + void RunnerGUI::doubleClickedOnDetails(int para, int /*pos*/) + { + static QRegExp reFileAndLine("^(.*)\\[([0-9]+)\\]:"); + + QString line = m_testerWidget->details()->text(para); + m_testerWidget->details()->setSelection(para, 0, para, line.length()-1); + + if ( reFileAndLine.search(line) != -1 ) + { + DCOPClient client; + client.attach(); + QByteArray data; + QDataStream arg(data, IO_WriteOnly); + bool ok; + arg << QString(reFileAndLine.cap(1)) << (reFileAndLine.cap(2).toInt(&ok) - 1); + client.send("kdevelop-*", "KDevPartController", "editDocument(QString,int)", data); + client.send("kdevelop-*", "MainWindow", "raise()", ""); + + client.detach(); + } + } +} + +#include "runnergui.moc" |