[gtest-runner-qt] r28 committed - Did some major refactoring. A lot of the logic for running tests now b...

7 views
Skip to first unread message

gtest-r...@googlecode.com

unread,
Aug 17, 2010, 7:55:39 PM8/17/10
to gtest-r...@googlegroups.com
Revision: 28
Author: sandychapman
Date: Tue Aug 17 16:53:58 2010
Log: Did some major refactoring. A lot of the logic for running tests now
belongs
int the TestTreeWidget. I've also implemented the code to enable refreshing
of test listings. It's not rigourously tested yet (none of it is really),
but it looks right when I step through with the debugger.

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

Reply all
Reply to author
Forward
0 new messages