Wednesday, November 28, 2007

Limiting the runtime of a program

I have some programs that run in a cron job at night and occasionally they hang and don't exit which means that anything scheduled after them doesn't run and the next day I have processes hanging around. To solve that I made a twelve line program that uses features in QProcess to automatically kill a process that is taking longer then five minutes. Here is the slightly bigger version that lets you specify the timeout if you want and has a help.
timelimit -l 60000 ./reallyLongrunningapp

#include QtCore
int main(int argc, char **argv)
{
if (argc < 2) {
QTextStream out(stderr);
out << "Usage: timelimit -l 1000 command [args]" << endl;
return -1;
}

QProcess process;
QStringList arguments;
for (int i = 2; i < argc; ++i)
arguments.append(argv[i]);
QString command = argv[1];
int timeLimit = 1000 * 60 * 5;
if (argc > 3 && command == "-l") {
bool ok;
int userLimit = arguments.value(0).toInt(&ok);
if (ok) {
timeLimit = userLimit;
command = argv[3];
arguments.pop_front();
arguments.pop_front();
}
}
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start(command, arguments);
process.waitForFinished(timelimit);
return process.exitCode();
}

Friday, November 09, 2007

QtScript profiling

Working on some QtScript code a little bit ago I wanted to profile the code. There didn't exists a profiler to I wrote one. It uses QScriptEngineAgent which is part of Qt 4.4.

qscriptprofiler runs with script files as arguments that are each executed in the order they are passed. When the script is finished it will generate a callgrind file that can be loaded in KCachegrind. At this time qscriptprofiler only uses gettimeofday() to determine how long something takes so there is the possibility that it isn't accurate if your system is under heavy load. Improving it to not use the system clock is left as an exercise to the user (it is good enough for what I have been using it for). The profiler can be used in other projects and a pri file is included.

You can get the source for qscriptprofiler on github.

Thursday, November 08, 2007

Auto test stub generator

When writing auto tests for Qt code one of the more annoying parts of the task is getting the initial test file up and running. The basic makeup of the test files are about the same so I wrote a little tool that helps by automatically creating stub code for the class you are writing a test for. Then it is just a task of going through the file filling it in with the actual tests.

Some features include:
- Creates a stub tests for each non private function in the class.
- Creates a subclass for the class to expose any protected functions for testing.
- Creates a basic sanity test that just calls each function.
- Creates a _data() function for each test.
- Populates the _data() functions with columns for each argument and the return value.
- Adds QFETCH stub code that matches the generated _data function as a place to start from.
- Adds the four init and cleanup functions with documentation so you don't have to look up what does what.
- Adds signal spys to each tests if the class contains any signals.

The source code can be found: QAutoTestGenerator

Here is an example class:

class Example : QObject
{
Q_OBJECT
signals:
void done();
public:
void launch(int value);
protected:
bool crashed();
};

And the generated stub:

#include <QtTest/QtTest>
#include <example.h>

class tst_Example : public QObject
{
Q_OBJECT

public slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();

private slots:
void example_data();
void example();
void launch_data();
void launch();
void crashed_data();
void crashed();
void done_data();
void done();
};

// Subclass that exposes the protected functions.
class SubExample : public Example
{
public:
bool call_crashed()
{ return SubExample::crashed(); }

void call_done()
{ return SubExample::done(); }
};

// This will be called before the first test function is executed.
// It is only called once.
void tst_Example::initTestCase()
{
}

// This will be called after the last test function is executed.
// It is only called once.
void tst_Example::cleanupTestCase()
{
}

// This will be called before each test function is executed.
void tst_Example::init()
{
}

// This will be called after every test function.
void tst_Example::cleanup()
{
}

void tst_Example::example_data()
{
}

void tst_Example::example()
{
SubExample example;
/*
example.isDone();
example.launch();
example.call_crashed();
example.call_done();
*/
QVERIFY(false); // remove after test is implemented
}

void tst_Example::launch_data()
{
QTest::addColumn<int>("value");
QTest::newRow("null") << 0;
}

// public void launch(int value)
void tst_Example::launch()
{
/*
QFETCH(int, value);

SubExample example;

QSignalSpy spy0(&example, SIGNAL(done()));
QCOMPARE(spy0.count(), 0);
example.launch();
*/
QVERIFY(false); // remove after test is implemented
}

void tst_Example::crashed_data()
{
/*
QTest::addColumn<bool>("crashed");
QTest::newRow("null") << 0;
*/
}

// protected bool crashed()
void tst_Example::crashed()
{
/*
QFETCH(bool, crashed);

SubExample example;

QSignalSpy spy0(&example, SIGNAL(done()));
QCOMPARE(spy0.count(), 0);
example.call_crashed();
*/
QVERIFY(false); // remove after test is implemented
}

void tst_Example::done_data()
{
QTest::addColumn<int>("foo");
QTest::newRow("null") << 0;
}

// protected void done()
void tst_Example::done()
{
/*
QFETCH(int, foo);

SubExample example;

QSignalSpy spy0(&example, SIGNAL(done()));
QCOMPARE(spy0.count(), 0);
example.call_done();
*/
QVERIFY(false); // remove after test is implemented
}

QTEST_MAIN(tst_Example)
#include "tst_example.moc"

Popular Posts