A Program Is Born: QCMake’s First Functional Test
Nov 2nd, 2007 by klimek
When you write a GUI that is just a thin layer for an existing business layer and you don’t see how to integrate test fixtures into this business layer, you’ll be down in the dirty functional testing work very quickly. This happened to me today when I tried to write my first test for a small Qt facade object for cmake.
I started the test very enthusiastically: To test cmake I create a directory, cd into that directory, create a CMakeLists.txt and let cmake create a CMakeCache.txt. In the end I know that cmake ran when CMakeCache.txt exists.
void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
}
I hit F5 and everything runs just fine. Once. The second time the directory ExecuteInCurrentDirectory already exists. Of course to have a nice and clean starting point the test must remove the test directory if it already exists. So I added:
void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(currentDirectory.rmdir(testDirectory.dirName()));
}
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
}
Green. Perfect. Now let’s create a CMakeLists.txt.
void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(currentDirectory.rmdir(testDirectory.dirName()));
}
QVERIFY(QDir::setCurrent(testDirectory.path()));
QFile cmakeLists("CMakeLists.txt");
QVERIFY(cmakeLists.open(QIODevice::ReadWrite));
}
Green again. Once. The test fails the second time it’s executed:
********* Start testing of QCMakeControlTest ********* Config: Using QTest library 4.3.2, Qt 4.3.2 PASS : QCMakeControlTest::initTestCase() FAIL! : QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory() 'currentDirectory.rmdir(testDirectory.dirName())' returned FALSE. () ..\..\..\..\..\Source\CMake\Source\QTDialog\qcmaketest\QCMakeControlTest.cpp(30) : failure location PASS : QCMakeControlTest::cleanupTestCase() Totals: 2 passed, 1 failed, 0 skipped
Yep, no problem, all I need to do is to rmdir recursively. Just a quick glance into the Qt docs. But I found nothing. Well, it’s not too hard to implement a recursive rm -rf, but still… I was so sure that this function would be hidden somewhere that I spent more time googling and doc-reading than implementing it when I finally realized that I was on my own. So in the end the test looked a little bloated:
#include "qcmaketest/QCMakeControlTest.h"
#include "qcmakeui/QCMakeControl.h"
bool removeRecursiveForced(QDir& directory, const QFileInfo& entry)
{
if(!entry.isDir())
{
return directory.remove(entry.fileName());
}
QDir directoryEntry(entry.filePath());
QList entries(directoryEntry.entryInfoList
(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
for(int entryIndex = 0; entryIndex < entries.count(); ++entryIndex)
{
if(!removeRecursiveForced(directoryEntry, entries.at(entryIndex)))
{
return false;
}
}
return directory.rmdir(entry.fileName());
}
void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(removeRecursiveForced(currentDirectory,
QFileInfo(currentDirectory, testDirectory.dirName())));
}
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
QFile cmakeLists("CMakeLists.txt");
QVERIFY(cmakeLists.open(QIODevice::ReadWrite));
QCMakeControl qCMakeControl;
qCMakeControl.configure();
QFile cmakeCache("CMakeCache.txt");
QVERIFY(cmakeCache.exists());
}
#include "QCMakeControlTest.moc"
At least I have an idea where this could lead me - a nice class to generate a clean cmake directory. But let's see whether I'll be right, perhaps YAGNI will finally get back at me. And if you know an easier way to delete a directory recursively with Qt, please leave a comment.
