/** * Copyright (C) 2005 Jeroen Wijnhout * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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 TQString &name, const TQString &info) { Tester *tester = Runner::self()->registry().find(name.local8Bit()); if ( tester == 0L ) return false; tester->results()->addDebugInfo(info); return true; } bool addSlotDebugInfo(const TQString &name, const TQString &slt, const TQString &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(tester); sltester->results(slt.local8Bit())->addDebugInfo(info); return true; } private: RunnerGUI *m_rg; }; RunnerGUI::RunnerGUI(TQWidget *parent) : TQHBox(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()); TQStringList suites; for ( ; it.current(); ++it ) { addTester(it.currentKey(), it.current()); TQString 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(TQListView::Extended); m_testerWidget->resultList()->setRootIsDecorated(true); m_testerWidget->resultList()->setColumnAlignment(g_finishedColumn, TQt::AlignHCenter); m_testerWidget->resultList()->setColumnAlignment(g_skippedColumn, TQt::AlignHCenter); m_testerWidget->resultList()->setColumnAlignment(g_failedColumn, TQt::AlignHCenter); m_testerWidget->resultList()->setColumnAlignment(g_xfailedColumn, TQt::AlignHCenter); m_testerWidget->resultList()->setColumnAlignment(g_passedColumn, TQt::AlignHCenter); m_testerWidget->resultList()->setColumnAlignment(g_xpassedColumn, TQt::AlignHCenter); // set the text in the results label fillResultsLabel(); // init the progress bar configureProgressBar(Runner::self()->numberOfTestCases(), 0); connect(Runner::self(), TQT_SIGNAL(finished(const char *, Tester *)), this, TQT_SLOT(addTestResult(const char *, Tester *))); connect(m_testerWidget->resultList(), TQT_SIGNAL(clicked(TQListViewItem *)), this, TQT_SLOT(showDetails(TQListViewItem *))); connect(m_testerWidget, TQT_SIGNAL(run()), this, TQT_SLOT(runSuite())); connect(m_testerWidget->details(), TQT_SIGNAL(doubleClicked(int, int)), this, TQT_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( TQString("Test cases: %1 | Tests performed: %5, Skipped: %4 | Passed: %2, Failed: %3") .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(TQString("Test cases: %1").arg(Runner::self()->numberOfTestCases())); } void RunnerGUI::addTestResult(const char *name, Tester *test) { TQStringList scopes = TQStringList::split("::", name); TQString suite = scopes[0]; // find the suite item TQListViewItem *item = 0L; for ( uint i = 0; i < scopes.count(); ++i ) item = getItem(scopes[i], item); if ( test->inherits("KUnitTest::SlotTester") ) { SlotTester *sltest = static_cast(test); TestResultsListIteratorType it(sltest->resultsList()); TQListViewItem *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) { TQStringList scopes = TQStringList::split("::", name); TQString suite = scopes[0]; // find the suite item TQListViewItem *item = 0L; for ( uint i = 0; i < scopes.count(); ++i ) item = getItem(scopes[i], item); if ( test->inherits("KUnitTest::SlotTester") ) { TQStrList allSlots = test->metaObject()->slotNames(); for ( char *sl = allSlots.first(); sl; sl = allSlots.next() ) { if ( TQString(sl).startsWith("test") ) getItem(sl, item); } } } TQListViewItem *RunnerGUI::getItem(const TQString &name, TQListViewItem *item /*= 0L*/) { TQListViewItem *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 TQListViewItem(m_testerWidget->resultList()); else item = new TQListViewItem(parent); item->setText(g_nameColumn, name); } return item; } void RunnerGUI::reset() { TQListViewItemIterator it( m_testerWidget->resultList() ); while ( it.current() ) { TQListViewItem *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, TQPixmap()); ++it; } } void RunnerGUI::setSummary(TQListViewItem *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, TQString::number(val + res->testsFinished())); val = item->text(g_skippedColumn).toInt(&ok); if (!ok) val = 0; item->setText(g_skippedColumn, TQString::number(val + res->skipped())); val = item->text(g_passedColumn).toInt(&ok); if (!ok) val = 0; item->setText(g_passedColumn, TQString::number(val + res->passed())); val = item->text(g_failedColumn).toInt(&ok); if (!ok) val = 0; item->setText(g_failedColumn, TQString::number(val + res->errors())); val = item->text(g_xfailedColumn).toInt(&ok); if (!ok) val = 0; item->setText(g_xfailedColumn, TQString::number(val + res->xfails())); val = item->text(g_xpassedColumn).toInt(&ok); if (!ok) val = 0; item->setText(g_xpassedColumn, TQString::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); } TQString RunnerGUI::fullName(TQListViewItem *item) { TQString 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 ) { TQListViewItemIterator it( m_testerWidget->resultList() ); TQStringList prefixes; while ( it.current() ) { TQListViewItem *item = it.current(); if ( item->isSelected() ) { TQString 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 { TQString suite = m_testerWidget->selectCombo()->currentText(); TQStringList tests; RegistryIteratorType it(Runner::self()->registry()); for ( ; it.current(); ++it ) if ( TQString(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(TQListViewItem *item) { if ( item == 0L ) return; TQString 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(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 { TQTextEdit *te = m_testerWidget->details(); te->clear(); te->append("Errors:
"); appendList(te, res->errorList()); te->append("

Expected to fail:
"); appendList(te, res->xfailList()); te->append("

Unexpected Success:
"); appendList(te, res->xpassList()); te->append("

Success:
"); appendList(te, res->successList()); te->append("

Skipped:
"); appendList(te, res->skipList()); te->append("

Debug:
"); te->append(res->debugInfo()); te->scrollToAnchor("errors"); } } void RunnerGUI::appendList(TQTextEdit *te, const TQStringList &list) { for ( uint i = 0; i < list.count(); ++i ) te->append(list[i]); } void RunnerGUI::doubleClickedOnDetails(int para, int /*pos*/) { static TQRegExp reFileAndLine("^(.*)\\[([0-9]+)\\]:"); TQString 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(); TQByteArray data; TQDataStream arg(data, IO_WriteOnly); bool ok; arg << TQString(reFileAndLine.cap(1)) << (reFileAndLine.cap(2).toInt(&ok) - 1); client.send("tdevelop-*", "KDevPartController", "editDocument(TQString,int)", data); client.send("tdevelop-*", "MainWindow", "raise()", TQString("")); client.detach(); } } } #include "runnergui.moc"