summaryrefslogtreecommitdiffstats
path: root/kunittest/runnergui.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kunittest/runnergui.cpp')
-rw-r--r--kunittest/runnergui.cpp433
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"