http://code.google.com/p/gtest-runner-qt/source/detail?r=28
Modified:
/trunk/include/GTest.h
/trunk/include/GTestExecutable.h
/trunk/include/GTestResults.h
/trunk/include/GTestRunner.h
/trunk/include/GTestSuite.h
/trunk/include/TestTreeWidget.h
/trunk/include/TestTreeWidgetItem.h
/trunk/src/GTest.cpp
/trunk/src/GTestExecutable.cpp
/trunk/src/GTestParser.cpp
/trunk/src/GTestRunner.cpp
/trunk/src/GTestSuite.cpp
/trunk/src/TestTreeWidget.cpp
/trunk/src/TestTreeWidgetItem.cpp
=======================================
--- /trunk/include/GTest.h Thu Aug 12 16:12:42 2010
+++ /trunk/include/GTest.h Tue Aug 17 16:53:58 2010
@@ -21,7 +21,8 @@
#include <QObject>
#include <QString>
-#include "GTestResults.h"
+class GTestResults;
+class GTestSuite;
/*! \brief This class logically represents a single unit test.
*
@@ -38,7 +39,8 @@
Q_OBJECT
protected:
- QString name; /**< Represents the name of the test.
+ //QString name;
+ /**< Represents the name of the test.
* In the case of the GTestExecutable class, this holds
* the executable's file path.
*/
@@ -49,12 +51,12 @@
signals:
void requestingRun(QString testName, QString caseName = QString()); //!<
Sends a request to the parent GTest
- void testResultsReady(); //!< Sends a notification that the test results
have been received and are ready to be read.
+ void testResultsReady();//!< Sends a notification that the test results
have been received and are ready to be read.
public:
GTest(QObject* parent = 0, QString name = QString());
+ GTest(GTestSuite* parent = 0, QString name = QString());
virtual ~GTest();
- QString getName() const;
const GTestResults* getTestResults() const;
virtual void receiveTestResults(GTestResults* testResults);
@@ -74,15 +76,7 @@
* run. In which case, this will not simply add the test to the
queue,
* but may need some mechanism to run immediately. This is still in
planning.
*/
-inline void GTest::run() { emit requestingRun(name); }
-
-/*! \brief Retrieve the name of the test.
- *
- * \return For a GTest, this returns the test name.
- * \return For a GTestSuite, it returns the test case name.
- * \return For a GTestExecutable, it returns the file's path.
- */
-inline QString GTest::getName() const { return name; }
+inline void GTest::run() { emit requestingRun(objectName()); }
/*! \brief Retrieve the results of the test.
*
=======================================
--- /trunk/include/GTestExecutable.h Thu Aug 12 16:12:42 2010
+++ /trunk/include/GTestExecutable.h Tue Aug 17 16:53:58 2010
@@ -22,6 +22,7 @@
#include <QProcess>
#include <QMetaType>
#include <QMutex>
+#include <QSet>
#include "GTestExecutableResults.h"
#include "GTestSuite.h"
@@ -61,7 +62,8 @@
QProcess *gtest; //!< The process thread for running tests.
QBuffer standardOutput; //!< The buffer to hold the stdout text.
QBuffer standardError; //!< The buffer to hold the stderr text.
- QStringList listing; //!< The list of unit test suites provided.
+ QSet<QString> listingSet;//!< The set of unit test suites provided.
+ QSet<QString> oldListingSet;//!< The set of elements prior to an update.
QStringList testsToRun; //!< The list of tests to run.
bool runOnSignal; /*!< A flag to indicate whether a process should be
* run when the runTest slot is called */
@@ -95,7 +97,8 @@
QString getExecutablePath() const;
int getExitCode() const;
QProcess::ExitStatus getExitStatus() const;
- QStringList getListing() const;
+ const QSet<QString>& getListing() const;
+ const QSet<QString>& getOldListing() const;
STATE getState();
void setRunFlag(bool runOnSignal);
@@ -111,11 +114,17 @@
/*! \brief Retrieves the listing (not necessarily populated).
*
- * The caller should ensure that produce listing has been called first and
+ * The caller should ensure that produceListing() has been called first and
* that the QProcess has successfully completed.
* \see GTestExecutable::produceListing()
*/
-inline QStringList GTestExecutable::getListing() const { return listing; }
+inline const QSet<QString>& GTestExecutable::getListing() const { return
listingSet; }
+
+/*! \brief Retrieves the listing prior to an update (not necessarily
populated).
+ *
+ * If nothing has changed between sets, oldListing should be equal to
listing.
+ */
+inline const QSet<QString>& GTestExecutable::getOldListing() const {
return oldListingSet; }
/*! \brief Retrieves the executable path (i.e. name) of the executable file
*
@@ -125,7 +134,7 @@
* where 'filepath' is the full path and 'name' is simply the filename.
* \todo TODO::create a new instance variable 'filePath'.
*/
-inline QString GTestExecutable::getExecutablePath() const { return name; }
+inline QString GTestExecutable::getExecutablePath() const { return
objectName(); }
/*! \brief Retrieves the error produced from running the gtest executable.
*
@@ -156,7 +165,7 @@
* \param executablePath The fully qualified path to a gtest executable.
* \todo TODO::set the return value of this function to return STATE
*/
-inline void GTestExecutable::setExecutablePath(QString executablePath) {
this->name = executablePath; }
+inline void GTestExecutable::setExecutablePath(QString executablePath) {
setObjectName(executablePath); }
/*! \brief Sets the flag that determines whether this GTestExecutable
* responds to the runTest() slot.
=======================================
--- /trunk/include/GTestResults.h Thu Aug 12 16:12:42 2010
+++ /trunk/include/GTestResults.h Tue Aug 17 16:53:58 2010
@@ -33,7 +33,7 @@
//! The run status of the GTest.
enum STATUS {
RUN, //!< The test was run.
- //! \todo TODO::Investigate other run status of a GTest.
+ //! \todo Investigate other run status of a GTest.
UNDEFINED //!< Currently not sure what the other possibilities are.
};
=======================================
--- /trunk/include/GTestRunner.h Thu Aug 12 16:12:42 2010
+++ /trunk/include/GTestRunner.h Tue Aug 17 16:53:58 2010
@@ -53,10 +53,7 @@
public slots:
void addTests();
- void updateAllListings();
void runTests();
- void updateListing(GTestExecutable* gtest);
- void setTestResult(GTest* gtest);
void treeItemClicked(QTreeWidgetItem* item, int column);
private:
@@ -69,14 +66,12 @@
QToolBar testTreeTools; //!< The tool bar for the test tree.
TestTreeWidget testTree;//!< The test tree.
- QList<GTestExecutable*> gtestList; //!< A list of all loaded gtest
executables. \todo TODO::change list to a hash.
-
void setup();
void setupMenus();
void setupToolBars();
void setupLayout();
- void invokeListingRetrieval(GTestExecutable* gtest);
+ void invokeListingRetrieval(QSharedPointer<GTestExecutable> gtest);
};
=======================================
--- /trunk/include/GTestSuite.h Sun Aug 8 10:59:31 2010
+++ /trunk/include/GTestSuite.h Tue Aug 17 16:53:58 2010
@@ -47,6 +47,7 @@
public:
GTestSuite(QObject* parent = 0, QString name = QString());
+ GTestSuite(GTestSuite* parent = 0, QString name = QString());
virtual ~GTestSuite();
void addTest(GTest* test);
void removeTest(GTest* test);
=======================================
--- /trunk/include/TestTreeWidget.h Thu Aug 12 16:12:42 2010
+++ /trunk/include/TestTreeWidget.h Tue Aug 17 16:53:58 2010
@@ -17,12 +17,20 @@
#ifndef TESTTREEWIDGET_H_
#define TESTTREEWIDGET_H_
+#include <QHash>
+#include <QList>
+#include <QObject>
+#include <QSharedPointer>
+#include <QSignalMapper>
+#include <QString>
#include <QTreeWidget>
#include <QTreeWidgetItem>
-
-#include "GTest.h"
-#include "GTestResults.h"
-#include "TestTreeWidgetItem.h"
+#include <QWidget>
+
+#include "GTestExecutable.h"
+
+class GTest;
+class TestTreeWidgetItem;
/*! \brief An extension of the QTreeWidget to provide custom functionality.
*
@@ -34,14 +42,34 @@
Q_OBJECT
+private:
+ QHash<QString, QSharedPointer<GTestExecutable> > gtestHash; //!< A hash
of all loaded gtest executables.
+ /*!< \todo Investigate whether the signal mapper is needed.
+ * A (gtest signal-> tree item signal-> tree widget slot) might work fine.
+ */
+ QSignalMapper signalMap; //!< Maps signals from GTests to TreeWidgets.
+
+ template <class T, class U>
+ TestTreeWidgetItem* createNewTreeItem(T* parent, U* test);
+
+signals:
+ void resettingRunStates();
+ void runningTests();
+
public slots:
void populateTestResult(QObject* item);
+ void updateListing(GTestExecutable* gtest);
+ void updateAllListings();
public:
TestTreeWidget(QWidget* parent = 0);
virtual ~TestTreeWidget();
+ void insertExecutable(QSharedPointer<GTestExecutable> executable);
+ QList<QSharedPointer<GTestExecutable> > getExecutableList();
+
};
+inline QList<QSharedPointer<GTestExecutable> >
TestTreeWidget::getExecutableList() { return gtestHash.values(); }
#endif /* TESTTREEWIDGET_H_ */
=======================================
--- /trunk/include/TestTreeWidgetItem.h Thu Aug 12 16:12:42 2010
+++ /trunk/include/TestTreeWidgetItem.h Tue Aug 17 16:53:58 2010
@@ -18,8 +18,12 @@
#define TESTTREEWIDGETITEM_H_
#include <QObject>
+#include <QStringList>
#include <QTreeWidgetItem>
+class TestTreeWidget;
+class TestTreeWidgetItem;
+
#include "GTestResults.h"
/*! \brief Extends the QTreeWidgetItem to make a QObject derived version.
@@ -31,8 +35,8 @@
Q_OBJECT
public:
- TestTreeWidgetItem(QTreeWidget* parent, const QStringList& strings, int
type = Type);
- TestTreeWidgetItem(QTreeWidgetItem* parent, const QStringList& strings,
int type = Type);
+ TestTreeWidgetItem(TestTreeWidget* parent, const QStringList& strings,
int type = Type);
+ TestTreeWidgetItem(TestTreeWidgetItem* parent, const QStringList&
strings, int type = Type);
virtual ~TestTreeWidgetItem();
};
=======================================
--- /trunk/src/GTest.cpp Sun Aug 8 10:59:31 2010
+++ /trunk/src/GTest.cpp Tue Aug 17 16:53:58 2010
@@ -15,6 +15,8 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * */
#include "GTest.h"
+#include "GTestResults.h"
+#include "GTestSuite.h"
/*! \brief Constructor
*
@@ -23,8 +25,26 @@
* the value \em testName in \code GTEST(testCase, testName) \endcode
*/
GTest::GTest(QObject* parent, QString name)
-: QObject(parent), name(name), testResults(0)
-{}
+: QObject(parent), testResults(0)
+{
+ setObjectName(name);
+}
+
+/*! \brief Constructor
+ *
+ * This is the same as the constructor with the QObject* argument,
+ * except that addTest is called on the parent.
+ * \param parent A pointer to the parent test.
+ * \param name The name of the unit test. If called directly, this should
be
+ * the value \em testName in \code GTEST(testCase, testName) \endcode
+ */
+GTest::GTest(GTestSuite* parent, QString name)
+: QObject(parent), testResults(0)
+{
+ setObjectName(name);
+ if(parent)
+ parent->addTest(this);
+}
/*! \bried Destructor
*
=======================================
--- /trunk/src/GTestExecutable.cpp Thu Aug 12 16:12:42 2010
+++ /trunk/src/GTestExecutable.cpp Tue Aug 17 16:53:58 2010
@@ -30,7 +30,7 @@
GTestExecutable::GTestExecutable(QObject* parent, QString filePath)
: GTestSuite(parent, filePath), state(VALID), processLock(),
outputLock(), gtest(0), standardOutput(), standardError(),
- listing(), testsToRun(), runOnSignal(false)
+ listingSet(), oldListingSet(), testsToRun(), runOnSignal(false)
{
getState();
}
@@ -53,13 +53,14 @@
* GTestExecutable objects while the QProcess retrieves the result.
* A caller can determine when the listing is ready by connecting a slot to
* the listingReady() slot.
- *
- * \todo TODO::check the state variable before running.
*/
void GTestExecutable::produceListing() {
//We lock so that any attempt to try to produce a listing
//or run a test will block until we're done with what we're
//doing here.
+ if(getState() != VALID)
+ return; //! \todo throw error here?
+ //! \todo Check if the gtest file has changed, and preempt here if
unchanged.
processLock.lock();
gtest = new QProcess();
QObject::connect(gtest, SIGNAL(readyReadStandardOutput()),
@@ -68,7 +69,7 @@
this, SLOT(executableFinished(int, QProcess::ExitStatus)));
QObject::connect(gtest, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(parseListing(int, QProcess::ExitStatus)));
- gtest->start(name, QStringList() << "--gtest_list_tests");
+ gtest->start(objectName(), QStringList() << "--gtest_list_tests");
//unlock the processLock in the parseListing slot
}
@@ -82,21 +83,72 @@
* \todo TODO::Have this function create the GTest / GTestSuite tree
instead of the test runner
*/
void GTestExecutable::parseListing(int /*exitCode*/, QProcess::ExitStatus
exitStatus) {
+ //Check status
if(exitStatus != QProcess::NormalExit) {
QMessageBox::warning((QWidget*)this->parent(),"Error Retrieving Listing",
"The Google test executable exited abnormally.");
processLock.unlock();
- }
+ return;
+ }
+ //Status is good, set up some vars and let's start parsing
+ oldListingSet = listingSet; //copy old set, to find elements to remove
+ QString name;
+ QString line;
+ GTestSuite* testSuite;
+ GTest* test;
standardOutput.open(QBuffer::ReadOnly);
- QByteArray temp;
while(standardOutput.canReadLine()) {
- temp = standardOutput.readLine();
- temp.resize(temp.size()-1);
- qDebug() << temp;
- listing << temp;
- }
+ line = standardOutput.readLine();
+ line.resize(line.size()-1); //remove \n
+ if(line.endsWith('.')) { //this means its a test suite name
+ name = line.left(line.size()-1); //get the name without the '.'
+ testSuite = findChild<GTestSuite*>(name);
+ if(!testSuite) //if it doesn't already exist, make one
+ testSuite = new GTestSuite(this, name);
+ listingSet << name; //add it to our new listing set
+ }
+ else {
+ //We should always run into 'testsuitename.'
+ //before we hit a unit test name.
+ Q_ASSERT(testSuite != 0);
+ name = line.right(line.size()-2); //test name is prepended with 2 spaces
+ test = testSuite->findChild<GTest*>(name);
+ if(!test) //if it doesn't already exist, make one
+ test = new GTest(testSuite, name);
+ //add it to our new listingSet in form of 'testsuitename.testname"
+ listingSet << testSuite->objectName().append('.').append(name);
+ }
+ }
+ //diff our old set with our new set to find removed tests.
+ QSet<QString> diffedListing = oldListingSet - listingSet;
+ QStringList deletedSuites;
+ QSet<QString>::iterator it = diffedListing.begin();
+ while(it != diffedListing.end()) {
+ int indexOfDot = it->indexOf('.');
+ if(indexOfDot == -1) { //not in form of testsuitename.testname
+ //must be an entire suite to be removed.
+ testSuite = findChild<GTestSuite*>(*it);
+ Q_ASSERT(testSuite != 0);
+ deletedSuites << testSuite->objectName();
+ testSuite->deleteLater(); //deletes children and cleans up signals/slots
+ continue;
+ }
+ //its of the form "testsuitename.testname"
+ //check if we've deleted the suite already
+ name = it->left(indexOfDot);
+ if(deletedSuites.contains(name))
+ continue; // already handled the deletion
+ testSuite = findChild<GTestSuite*>(name);
+ Q_ASSERT(testSuite != 0);
+ test = findChild<GTest*>(it->right(it->length()-indexOfDot));
+ Q_ASSERT(test != 0);
+ test->deleteLater();
+ ++it;
+ }
+
standardOutput.close();
processLock.unlock();
+ //! \todo Only emit listingReady if it differs from the last listing.
emit listingReady(this);
}
@@ -108,6 +160,9 @@
* \todo TODO::Check the state before running the test.
*/
void GTestExecutable::runTest() {
+ //Check our state, and whether we're listening to run signals.
+ if(this->state != VALID || !this->runOnSignal)
+ return;
//We lock so that any attempt to try to produce a listing
//or run a test will block until we're done with what we're
//doing here.
@@ -117,7 +172,7 @@
this, SLOT(standardOutputAvailable()));
QObject::connect(gtest, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(parseTestResults(int, QProcess::ExitStatus)));
- gtest->start(name, QStringList()
<< "--gtest_output=xml:./test_detail_1337.xml");
+ gtest->start(objectName(), QStringList()
<< "--gtest_output=xml:./test_detail_1337.xml");
//unlock the processLock in the parseTestResults slot
}
@@ -139,7 +194,7 @@
QList<GTest*>::iterator it = this->runList.begin();
GTestResults* testSuiteResults;
while(it != this->runList.end()) {
- testSuiteResults = testResults->getTestResults((*it)->getName());
+ testSuiteResults = testResults->getTestResults((*it)->objectName());
(*it)->receiveTestResults(testSuiteResults);
++it;
}
@@ -157,12 +212,10 @@
* \see GTestExecutable::readExecutableOutput()
*/
void GTestExecutable::standardOutputAvailable() {
- qDebug() << "std output available...";
outputLock.lock();
gtest->setReadChannel(QProcess::StandardOutput);
readExecutableOutput(standardOutput);
outputLock.unlock();
- qDebug() << "List finished populating";
}
/*! \brief Slot that is called when stderr data is available from the
process.
@@ -174,12 +227,10 @@
* \see GTestExecutable::readExecutableOutput()
*/
void GTestExecutable::standardErrorAvailable() {
- qDebug() << "std error available...";
outputLock.lock();
gtest->setReadChannel(QProcess::StandardError);
readExecutableOutput(standardError);
outputLock.unlock();
- qDebug() << "List finished populating";
}
/*! \brief Reads the current readChannel data into the appropriate QBuffer.
@@ -200,11 +251,9 @@
standardChannel.open(QBuffer::WriteOnly);
while(!gtest->atEnd()) {
while(gtest->canReadLine()) {
- qDebug() << "reading a line...";
lineLength = gtest->readLine(buffer, BUF_SIZE);
if(lineLength > 0 && lineLength <= BUF_SIZE) {
standardChannel.write(buffer);
- qDebug() << buffer << "added to buffer.";
}
}
}
@@ -218,7 +267,6 @@
* the exit statuses which can be subsequently checked by the test runner.
*/
void GTestExecutable::executableFinished(int exitCode,
QProcess::ExitStatus exitStatus) {
- qDebug() << "exe finished";
QObject::disconnect(gtest, SIGNAL(readyReadStandardOutput()),
this, SLOT(standardOutputAvailable()));
QObject::disconnect(gtest, SIGNAL(finished(int, QProcess::ExitStatus)),
@@ -240,7 +288,7 @@
*/
GTestExecutable::STATE GTestExecutable::getState() {
state = VALID;
- QFile file(name);
+ QFile file(objectName());
if(!file.exists()) {
state = FILE_NOT_FOUND;
}
=======================================
--- /trunk/src/GTestParser.cpp Thu Aug 12 16:12:42 2010
+++ /trunk/src/GTestParser.cpp Tue Aug 17 16:53:58 2010
@@ -65,12 +65,9 @@
GTestExecutableResults* testExeResults;
GTestSuiteResults* testSuiteResults;
GTestResults* testResults;
+ //! \bug \todo Fix the bug in here causing "premature end-of-file" error.
while(!xmlStream.atEnd()) {
- qDebug() << xmlStream.text();
- while(!xmlStream.readNextStartElement() && !xmlStream.hasError()
&& !xmlStream.atEnd()) {
- qDebug() << xmlStream.text();
- }
- qDebug() << xmlStream.text();
+ while(!xmlStream.readNextStartElement() && !xmlStream.hasError()
&& !xmlStream.atEnd()) {}
attributes = xmlStream.attributes();
if(xmlStream.name() == "testcase") {
testResults = new GTestResults(attributes.value("name").toString());
=======================================
--- /trunk/src/GTestRunner.cpp Thu Aug 12 16:12:42 2010
+++ /trunk/src/GTestRunner.cpp Tue Aug 17 16:53:58 2010
@@ -22,6 +22,7 @@
#include <QFileDialog>
#include <QGroupBox>
#include <QMessageBox>
+#include <QSharedPointer>
#include <QSignalMapper>
#include <QTreeWidgetItem>
#include <QTreeWidgetItemIterator>
@@ -34,10 +35,9 @@
* <a
href="http://doc.qt.nokia.com/4.6/qt.html#WindowType-enum">Qt::WFlags
Reference</a>
*/
GTestRunner::GTestRunner(QWidget *parent, Qt::WFlags flags)
- : QMainWindow(parent, flags),
+ : QMainWindow(parent, flags), menuBar(0),
fileMenu(tr("&File")), helpMenu(tr("&Help")), statusBar(this),
- centralWidget(this), testTreeTools(this), testTree(this),
- gtestList()
+ centralWidget(this), testTreeTools(this), testTree(this)
{
resize(500, 800);
setup();
@@ -47,7 +47,9 @@
*
*/
GTestRunner::~GTestRunner()
-{}
+{
+ menuBar->deleteLater();
+}
/*! \brief Sets up the application GUI.
*
@@ -67,6 +69,10 @@
QObject::connect(&testTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
this, SLOT(treeItemClicked(QTreeWidgetItem*, int)));
+ QObject::connect(this, SIGNAL(aboutToRunTests()),
+ &testTree, SIGNAL(resettingRunStates()));
+ QObject::connect(this, SIGNAL(runningTests()),
+ &testTree, SIGNAL(runningTests()));
setupLayout();
}
@@ -110,6 +116,7 @@
QAction* stopTestsAct = new QAction(tr("Stop"), this);
QAction* addTestsAct = new QAction(tr("Add"), this);
QAction* removeTestsAct = new QAction(tr("Remove"), this);
+ QAction* refreshTestListAct = new QAction(tr("Refresh"), this);
QObject::connect(runTestsAct, SIGNAL(triggered()),
this, SLOT(runTests()));
@@ -117,10 +124,14 @@
QObject::connect(addTestsAct, SIGNAL(triggered()),
this, SLOT(addTests()));
+ QObject::connect(refreshTestListAct, SIGNAL(triggered()),
+ &testTree, SLOT(updateAllListings()));
+
testTreeTools.addAction(runTestsAct);
testTreeTools.addAction(stopTestsAct);
testTreeTools.addAction(addTestsAct);
testTreeTools.addAction(removeTestsAct);
+ testTreeTools.addAction(refreshTestListAct);
}
/*! \brief Sets up the application's layout.
@@ -151,7 +162,7 @@
void GTestRunner::addTests() {
bool addResolved = false; //flag to see if we've got a good test.
QString filepath;
- GTestExecutable* newTest = new GTestExecutable(this);
+ QSharedPointer<GTestExecutable> newTest =
QSharedPointer<GTestExecutable>(new GTestExecutable(this));
while(!addResolved) {
filepath = QFileDialog::getOpenFileName(this, tr("Select Google Test
Executable"));
qDebug() << "File path received:" << filepath;
@@ -202,117 +213,12 @@
break;
}
case GTestExecutable::VALID: {
+ testTree.insertExecutable(newTest);
addResolved = true;
break;
}
}
}
-
- //We've got a good test, so let's have it send up a listing.
- invokeListingRetrieval(newTest);
-}
-
-/*! \brief Sets up and requests a listing from a GTestExecutable.
- *
- * This creates a signal / slot mapping so that the runner is informed
- * when the listing is ready.
- */
-void GTestRunner::invokeListingRetrieval(GTestExecutable* gtest) {
- //Have the executable inform us when the listing is ready.
- QObject::connect(gtest, SIGNAL(listingReady(GTestExecutable*)),
- this, SLOT(updateListing(GTestExecutable*)));
- gtest->produceListing();
-}
-
-/*! \brief Updates the tree with a listing provided by 'gtest'.
- *
- * This function takes the 'gtest' and retrieves its listing. It then
- * takes the listing and populates the main test tree with the tests
- * it provides.
- * \todo TODO::Update a listing that already exists.
- * \todo TODO::Refactor this method. It's a bit too long.
- */
-void GTestRunner::updateListing(GTestExecutable* gtest) {
- qDebug() << "Updating listing";
- const int exitCode = gtest->getExitCode();
- qDebug() << "got exit code:" << exitCode;
- QString exePath = gtest->getExecutablePath();
- qDebug() << "got exe path:" << exePath;
- if(exitCode != 0) {
- QMessageBox::critical(this, "Error Retrieving Test Listing",
- QString("Exit code").append(exitCode).append("was returned from the
Google Test executable at").append(exePath).append(". Are you sure this is
a valid Google Test unit test executable?"));
- //TODO: perform switch statement of process error.
- return;
- }
- QStringList testList = gtest->getListing();
- qDebug() << "Retrieved listing. Size ="<<testList.size();
- QStringList::iterator it = testList.begin();
-
-
- TestTreeWidgetItem* testContainer = new
TestTreeWidgetItem(&testTree,QStringList() << exePath);
- testContainer->setFlags(Qt::ItemIsTristate | Qt::ItemIsSelectable |
Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
- testContainer->setCheckState(0, Qt::Checked);
- QVariant var;
- var.setValue<GTestExecutable*>(gtest);
- testContainer->setData(0,Qt::UserRole,var);
- QObject::connect(this, SIGNAL(aboutToRunTests()),
- gtest, SLOT(resetRunState()));
- QObject::connect(this, SIGNAL(runningTests()),
- gtest, SLOT(runTest()));
- QSignalMapper* signalMap = new QSignalMapper(&testTree);
- signalMap->setMapping(gtest, testContainer);
- QObject::connect(gtest, SIGNAL(testResultsReady()),
- signalMap, SLOT(map()));
-
- TestTreeWidgetItem* topLevelItem = 0;
- TestTreeWidgetItem* newItem = 0;
- GTestSuite* suite = 0;
- GTest* test = 0;
- while(it != testList.end()) {
- qDebug() << *it;
- if(it->endsWith(".")) {
- //drop the '.' and make it a data item
- QString suiteName = it->left(it->size()-1);
- topLevelItem = new TestTreeWidgetItem(testContainer,
QStringList()<<suiteName);
- topLevelItem->setFlags(Qt::ItemIsTristate | Qt::ItemIsSelectable |
Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
- topLevelItem->setCheckState(0, Qt::Checked);
- suite = new GTestSuite(gtest, suiteName);
- var.setValue<GTestSuite*>(suite);
- topLevelItem->setData(0,Qt::UserRole,var);
- gtest->addTest(suite);
-
- signalMap->setMapping(suite, topLevelItem);
- QObject::connect(suite, SIGNAL(testResultsReady()),
- signalMap, SLOT(map()));
- }
- else {
- //drop the spaces and make it a data item
- QString testName = it->right(it->size()-2);
- newItem = new TestTreeWidgetItem(topLevelItem, QStringList()<<testName);
- newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable |
Qt::ItemIsEnabled);
- newItem->setCheckState(0,Qt::Checked);
- test = new GTest(suite, testName);
- var.setValue<GTest*>(test);
- newItem->setData(0,Qt::UserRole,var);
- suite->addTest(test);
-
- signalMap->setMapping(test, newItem);
- QObject::connect(test, SIGNAL(testResultsReady()),
- signalMap, SLOT(map()));
- }
- ++it;
- }
- QObject::connect(signalMap, SIGNAL(mapped(QObject*)),
- &testTree, SLOT(populateTestResult(QObject*)));
-}
-
-/*! \brief Updates all the listings for every GTestExecutable.
- *
- * \todo TODO::Launch this function from a 'refresh' button.
- * \todo TODO::Fill out this method.
- */
-void GTestRunner::updateAllListings() {
-
}
/*! \brief Runs all tests that are checked.
@@ -322,8 +228,10 @@
emit aboutToRunTests();
QTreeWidgetItemIterator it(&testTree,
QTreeWidgetItemIterator::NoChildren);
while(*it) {
- if((*it)->checkState(0) != Qt::Checked)
+ if((*it)->checkState(0) != Qt::Checked) {
+ it++;
continue;
+ }
(*it)->data(0,Qt::UserRole).value<GTest*>()->run();
it++;
}
@@ -333,8 +241,6 @@
continue;
GTestExecutable* gtest =
topLevelItem->data(0,Qt::UserRole).value<GTestExecutable*>();
if(gtest != 0 && gtest->getState() == GTestExecutable::VALID) {
- //QObject::connect(gtest, SIGNAL(testResultsReady(GTest*)),
- // this, SLOT(fillTestResults(GTestExecutable*)));
gtest->setRunFlag(true);
}
else
@@ -343,13 +249,6 @@
}
emit runningTests();
}
-
-/*! \brief Sets the test result of the given gtest.
- *
- * \todo TODO::Check to see if this function is needed/used.
- */
-void GTestRunner::setTestResult(GTest* /*gtest*/)
-{}
/*! \brief Slot to handle maintaining valid checkbox states.
*
=======================================
--- /trunk/src/GTestSuite.cpp Thu Aug 12 16:12:42 2010
+++ /trunk/src/GTestSuite.cpp Tue Aug 17 16:53:58 2010
@@ -26,6 +26,21 @@
GTestSuite::GTestSuite(QObject* parent, QString name)
: GTest(parent, name), runList()
{}
+
+/*! \brief Constructor
+ *
+ * This is the same as the constructor with the QObject* argument,
+ * except that addTest is called on the parent.
+ * \param parent A pointer to the parent test.
+ * \param name The name of the unit test. If called directly, this should
be
+ * the value \em testName in \code GTEST(testCase, testName) \endcode
+ */
+GTestSuite::GTestSuite(GTestSuite* parent, QString name)
+: GTest(parent, name), runList()
+{
+ if(parent)
+ parent->addTest(this);
+}
/*! \brief Destructor
*/
@@ -48,7 +63,7 @@
if(!runList.contains(test))
runList.append(test);
if(testCase.isEmpty())
- emit requestingRun(testName, name);
+ emit requestingRun(testName, objectName());
}
/*! \brief Receives the test results from the parent GTestExecutable
object.
@@ -63,7 +78,7 @@
QList<GTest*>::iterator it = runList.begin();
GTestResults* testResults;
while(it != runList.end()) {
- testResults = testSuiteResults->getTestResults((*it)->getName());
+ testResults = testSuiteResults->getTestResults((*it)->objectName());
(*it)->receiveTestResults(testResults);
++it;
}
=======================================
--- /trunk/src/TestTreeWidget.cpp Thu Aug 12 16:12:42 2010
+++ /trunk/src/TestTreeWidget.cpp Tue Aug 17 16:53:58 2010
@@ -14,17 +14,30 @@
* Boston, MA 02111-1307 USA
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * */
+#include <QDebug>
+#include <QIcon>
+#include <QMessageBox>
+#include <QVariant>
+
#include "TestTreeWidget.h"
+#include "TestTreeWidgetItem.h"
+#include "GTest.h"
#include "GTestSuite.h"
#include "GTestExecutable.h"
+#include "GTestResults.h"
/*! \brief Constructor
*
* \param parent The parent QWidget.
*/
TestTreeWidget::TestTreeWidget(QWidget* parent)
-: QTreeWidget(parent)
-{}
+: QTreeWidget(parent), signalMap(this)
+{
+ QObject::connect(&signalMap, SIGNAL(mapped(QObject*)),
+ this, SLOT(populateTestResult(QObject*)));
+ this->setColumnCount(2);
+ this->setHeaderLabels(QStringList()<<tr("Tests")<<tr(" "));
+}
/*! \brief Destructor
*
@@ -61,11 +74,143 @@
if(testResults == 0)
return;
- if(testResults->getFailureCount() == 0)
- treeItem->setBackgroundColor(0,Qt::green);
- else
- treeItem->setBackgroundColor(0,Qt::red);
-}
+ if(testResults->getFailureCount() == 0) {
+ treeItem->setBackgroundColor(0,QColor(0xAB,0xFF,0xBB,0xFF));
+ }
+ else {
+ treeItem->setBackgroundColor(0,QColor(0xFF,0x88,0x88,0xFF));
+ }
+}
+
+/* \brief Creates a tree item and sets up some slot/signals.
+ *
+ * This function is a help to the updateListing method which enables
+ * simplified creation of new tree items. It is templated as I didn't
+ * feel like writing three copies of the same code (thus making the
+ * function useless). It simply sets up some common states among tree
+ * items and some signal/slots between the tree item and its corresponding
+ * test item.
+ */
+template <class T, class U>
+TestTreeWidgetItem* TestTreeWidget::createNewTreeItem(T* parent, U* test) {
+ TestTreeWidgetItem* newTreeItem = new TestTreeWidgetItem(parent,
QStringList() << test->objectName());
+ newTreeItem->setObjectName(test->objectName());
+ newTreeItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable |
Qt::ItemIsEnabled);
+ newTreeItem->setCheckState(0, Qt::Checked);
+ QVariant var;
+ var.setValue<U*>(test);
+
+ newTreeItem->setData(0,Qt::UserRole,var);
+ signalMap.setMapping(test, newTreeItem); /* E.g. if gtest sends a signal
through
+ * the mapper, it will include exeTreeItem
+ * as an argument.
+ */
+ //Connect some signals and slots
+ QObject::connect(test, SIGNAL(destroyed()),
+ newTreeItem, SLOT(deleteLater()));
+ QObject::connect(test, SIGNAL(testResultsReady()),
+ &signalMap, SLOT(map()));
+ return newTreeItem;
+}
+
+/*! \brief Updates the tree with a listing provided by 'gtest'.
+ *
+ * This function takes the 'gtest' and retrieves its listing. It then
+ * takes the listing and populates the main test tree with the tests
+ * it provides.
+ * \todo TODO::Update a listing that already exists.
+ * \todo TODO::Refactor this method. It's a bit too long.
+ */
+void TestTreeWidget::updateListing(GTestExecutable* gtest) {
+ const int exitCode = gtest->getExitCode();
+ QString exePath = gtest->getExecutablePath();
+ if(exitCode != 0) {
+ QMessageBox::critical(this, "Error Retrieving Test Listing",
+ QString("Exit code").append(exitCode).append("was returned from the
Google Test executable at").append(exePath).append(". Are you sure this is
a valid Google Test unit test executable?"));
+ //! \todo Perform switch statement of process error.
+ return;
+ }
+
+ TestTreeWidgetItem* suiteTreeItem = 0;
+ TestTreeWidgetItem* testTreeItem = 0;
+ qDebug() << "attempting to find child with name"<<gtest->objectName();
+ TestTreeWidgetItem* exeTreeItem =
this->findChild<TestTreeWidgetItem*>(gtest->objectName());
+
+ if(!exeTreeItem) {
+ //If we haven't added the test yet, make one now.
+ qDebug() << "creating new test exe tree item.";
+ exeTreeItem =
createNewTreeItem<TestTreeWidget,GTestExecutable>(this,gtest);
+ qDebug() << "item created with name"<<exeTreeItem->objectName();
+ exeTreeItem->setFlags(exeTreeItem->flags() | Qt::ItemIsTristate);
+ QObject::connect(this, SIGNAL(runningTests()),
+ gtest, SLOT(runTest()));
+ QObject::connect(this, SIGNAL(resettingRunStates()),
+ gtest, SLOT(resetRunState()));
+ }
+
+ //Get which of the tests are new. This should be all of them the first
+ //time through for this test.
+ QSet<QString> newTests = gtest->getListing() - gtest->getOldListing();
+
+ //Iterate through all the suites.
+ QList<GTestSuite*> suiteList = gtest->findChildren<GTestSuite*>();
+ QList<GTestSuite*>::iterator suiteIter = suiteList.begin();
+ while(suiteIter != suiteList.end()) {
+ if(!newTests.contains((*suiteIter)->objectName())) {
+ suiteIter++;
+ continue; //already have this added
+ }
+
+ //Create a new GTestSuite tree item.
+ suiteTreeItem =
createNewTreeItem<TestTreeWidgetItem,GTestSuite>(exeTreeItem, *suiteIter);
+ suiteTreeItem->setFlags(suiteTreeItem->flags() | Qt::ItemIsTristate);
+
+ //Iterate through all the tests of this suite.
+ QList<GTest*> testList = (*suiteIter)->findChildren<GTest*>();
+ QList<GTest*>::iterator testIter = testList.begin();
+ while(testIter != testList.end()) {
+
if(!newTests.contains((*suiteIter)->objectName().append('.').append((*testIter)->objectName())))
{
+ testIter++;
+ continue; //already have this added
+ }
+
+ //Create a new GTest tree item.
+ testTreeItem = createNewTreeItem<TestTreeWidgetItem,
GTest>(suiteTreeItem, *testIter);
+ ++testIter;
+ }
+ ++suiteIter;
+ }
+}
+
+/*! \brief Updates all the listings for every GTestExecutable.
+ *
+ * This function iterates through the collection of GTestExecutables
+ * and invokes their listing retrieval mechanism.
+ */
+void TestTreeWidget::updateAllListings() {
+ QHash<QString, QSharedPointer<GTestExecutable> >::iterator it =
gtestHash.begin();
+ while(it != gtestHash.end()) {
+ (*it)->produceListing();
+ ++it;
+ }
+}
+
+/*! \brief Inserts a new executable into the tree.
+ *
+ * This inserts a new GTestExecutable into the tree and calls the method
+ * to begin retrieving the test listing.
+ * \see TestTreeWidget::updateListing()
+ */
+void TestTreeWidget::insertExecutable(QSharedPointer<GTestExecutable>
executable) {
+ QObject::connect(executable.data(),
SIGNAL(listingReady(GTestExecutable*)),
+ this, SLOT(updateListing(GTestExecutable*)));
+ //We insert it so that it doesn't auto-delete from the shared ptr.
+ //Will probably be useful later on when we want to save settings.
+ gtestHash.insert(executable->objectName(), executable);
+ //We've got test, so let's have it send up a listing.
+ executable->produceListing();
+}
+
=======================================
--- /trunk/src/TestTreeWidgetItem.cpp Thu Aug 12 16:12:42 2010
+++ /trunk/src/TestTreeWidgetItem.cpp Tue Aug 17 16:53:58 2010
@@ -15,27 +15,28 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * */
#include "TestTreeWidgetItem.h"
+#include "TestTreeWidget.h"
/*! \brief Constructor
*
* <a href="http://doc.qt.nokia.com/4.6/qtreewidgetitem.html#type">See
QTreeWidgetItem</a>
- * \param parent, The tree widget to which the object belongs.
+ * \param parent The tree widget to which the object belongs.
* \param strings Initial strings to be inserted as columns of this
element.
* \param type A type used to identify what kind of data is attached to
this item.
*/
-TestTreeWidgetItem::TestTreeWidgetItem(QTreeWidget* parent, const
QStringList& strings, int type)
-: QObject(), QTreeWidgetItem(parent, strings, type)
+TestTreeWidgetItem::TestTreeWidgetItem(TestTreeWidget* parent, const
QStringList& strings, int type)
+: QObject(parent), QTreeWidgetItem(parent, strings, type)
{}
/*! \brief Constructor
*
* <a href="http://doc.qt.nokia.com/4.6/qtreewidgetitem.html#type">See
QTreeWidgetItem</a>
- * \param parent, The tree widget item to which the object belongs.
+ * \param parent The tree widget item to which the object belongs.
* \param strings Initial strings to be inserted as columns of this
element.
* \param type A type used to identify what kind of data is attached to
this item.
*/
-TestTreeWidgetItem::TestTreeWidgetItem(QTreeWidgetItem* parent, const
QStringList& strings, int type)
-: QObject(), QTreeWidgetItem(parent, strings, type)
+TestTreeWidgetItem::TestTreeWidgetItem(TestTreeWidgetItem* parent, const
QStringList& strings, int type)
+: QObject(parent), QTreeWidgetItem(parent, strings, type)
{}
/*! \brief Destructor