From def2b0c7930cd92b08351844a2e40a8e3ed94dae Mon Sep 17 00:00:00 2001 From: Martin Wolf Date: Mon, 17 May 2010 16:02:02 +0300 Subject: Added performance tests --- tests/test-dbus-qeventloop-runner.sh | 5 +- .../test-dbus-qeventloop/test-dbus-qeventloop.cpp | 2 +- .../dbus-spammer/dbus-services.cpp | 120 +++++++ .../test-performance/dbus-spammer/dbus-services.h | 74 ++++ .../test-performance/dbus-spammer/dbus-spammer.cpp | 238 +++++++++++++ .../test-performance/dbus-spammer/dbus-spammer.pro | 21 ++ tests/test-performance/template.html | 62 ++++ tests/test-performance/test-performance.pro | 15 + .../ResultsUpload.cpp | 39 +++ .../ResultsUpload.h | 26 ++ .../test-resourceqt-performance-collector.cpp | 153 +++++++++ .../test-resourceqt-performance-collector.pro | 21 ++ .../test-resourceqt-performance-runner.sh | 80 +++++ .../test-resourceqt-performance.ini | 12 + .../test-resourceqt-performance/client.cpp | 380 +++++++++++++++++++++ .../test-resourceqt-performance/client.h | 175 ++++++++++ .../test-resourceqt-performance.cpp | 116 +++++++ .../test-resourceqt-performance.pro | 24 ++ tests/test-performance/tests.xml | 37 ++ tests/tests.pro | 3 +- 20 files changed, 1597 insertions(+), 6 deletions(-) create mode 100644 tests/test-performance/dbus-spammer/dbus-services.cpp create mode 100644 tests/test-performance/dbus-spammer/dbus-services.h create mode 100644 tests/test-performance/dbus-spammer/dbus-spammer.cpp create mode 100644 tests/test-performance/dbus-spammer/dbus-spammer.pro create mode 100644 tests/test-performance/template.html create mode 100644 tests/test-performance/test-performance.pro create mode 100644 tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.cpp create mode 100644 tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.h create mode 100644 tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.cpp create mode 100644 tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.pro create mode 100755 tests/test-performance/test-resourceqt-performance-runner.sh create mode 100644 tests/test-performance/test-resourceqt-performance.ini create mode 100644 tests/test-performance/test-resourceqt-performance/client.cpp create mode 100644 tests/test-performance/test-resourceqt-performance/client.h create mode 100644 tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.cpp create mode 100644 tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.pro create mode 100644 tests/test-performance/tests.xml (limited to 'tests') diff --git a/tests/test-dbus-qeventloop-runner.sh b/tests/test-dbus-qeventloop-runner.sh index 75cab5e..adc6e12 100755 --- a/tests/test-dbus-qeventloop-runner.sh +++ b/tests/test-dbus-qeventloop-runner.sh @@ -1,10 +1,7 @@ #!/bin/sh # Source and export D-Bus session info -. /tmp/dbus-info -export DBUS_SESSION_BUS_ADDRESS -export DBUS_SESSION_BUS_PID -export DBUS_SESSION_BUS_WINDOWID +. /tmp/session_bus_address.user # Run pong server on system bus /usr/lib/libresourceqt-tests/test-dbus-pong & diff --git a/tests/test-dbus-qeventloop/test-dbus-qeventloop.cpp b/tests/test-dbus-qeventloop/test-dbus-qeventloop.cpp index 746a6ed..032156a 100644 --- a/tests/test-dbus-qeventloop/test-dbus-qeventloop.cpp +++ b/tests/test-dbus-qeventloop/test-dbus-qeventloop.cpp @@ -131,7 +131,7 @@ private slots: void initTestCase() { // First allocate and obtain systemBus = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - sessionBus = dbus_bus_get(DBUS_BUS_SESSION, NULL);; + sessionBus = dbus_bus_get(DBUS_BUS_SESSION, NULL); // Last check, if server is running pongServerRunningSystem = dbus_bus_name_has_owner(systemBus, "com.nokia.dbusqeventloop.test", NULL); pongServerRunningSession = dbus_bus_name_has_owner(sessionBus, "com.nokia.dbusqeventloop.test", NULL); diff --git a/tests/test-performance/dbus-spammer/dbus-services.cpp b/tests/test-performance/dbus-spammer/dbus-services.cpp new file mode 100644 index 0000000..d084ce0 --- /dev/null +++ b/tests/test-performance/dbus-spammer/dbus-services.cpp @@ -0,0 +1,120 @@ +#include "dbus-services.h" + +DbusSpammer::DbusSpammer(int id, QString service, int messageLen) : QThread() +{ + firstTime = true; +// qDebug("Hello world from DbusSpammer::DbusSpammer(%d, %s, %d)", id, qPrintable(service), messageLen); + + myBus = dbus_bus_get(DBUS_BUS_SESSION, NULL); + if( myBus == NULL ) + { + qDebug("Failed to access session bus!"); + return; + } + + threadID = id; + serviceName = service; + spamData.fill('A', messageLen); + pszSpamData = qPrintable(spamData); + + myMessage = dbus_message_new_method_call(qPrintable(service), "/", NULL, "ping"); + if( myMessage == NULL ) + { + qDebug("Failed to create dbus message! Out of memory?"); + return; + } + + dbus_message_append_args(myMessage, DBUS_TYPE_STRING, &pszSpamData, DBUS_TYPE_INVALID); + + timerID = startTimer(0); +} + +DbusSpammer::~DbusSpammer() +{ + killTimer(timerID); + + dbus_message_unref(myMessage); + dbus_connection_unref(myBus); +} + +void DbusSpammer::run() +{ +// sleep(1); +// printf("Thread #%d: Using service '%s' ...\n", threadID, qPrintable(serviceName)); + + exec(); +} + +void DbusSpammer::timerEvent(QTimerEvent*) +{ + killTimer(timerID); + + if( firstTime ) + { + dbus_connection_send(myBus, myMessage, NULL); + dbus_connection_flush(myBus); + } + else + { + DBusError error; + dbus_error_init(&error); + DBusMessage* reply = dbus_connection_send_with_reply_and_block(myBus, myMessage, 1000, &error); + + if( dbus_error_is_set(&error) ) + { + if( QString(error.name) != QString("org.freedesktop.DBus.Error.AccessDenied") ) + qDebug("Thread #%d dbus error %s:\n %s", threadID, error.name, error.message); + } + + if( reply ) + { + dbus_message_unref(reply); + } + } + + firstTime = false; + timerID = startTimer(0); +} + +DbusServer::DbusServer(int id) +{ + serviceID = id; + serviceName.sprintf("com.nokia.spam.dbus%d", id); +} + +DbusServer::~DbusServer() +{ + if( myBus ) + { + delete myBus; + myBus = NULL; + } +} + +void DbusServer::run() +{ + myBus = new QDBusConnection(QDBusConnection::sessionBus().sessionBus()); + if( !myBus || !myBus->isConnected() ) + { + qDebug("Connection error!"); + return; + } + + if( !myBus->registerService(serviceName) ) + { + qDebug("Service #%d: %s", serviceID, qPrintable(myBus->lastError().message())); + return; + } + + // Register all slots as dbus methods + myBus->registerObject("/", this, QDBusConnection::ExportScriptableSlots); + +// qDebug("DbusServer(%d) running!", serviceID); + exec(); +} + +QString DbusServer::ping(const QString &arg) +{ + // Just return back + return QString("%1").arg(arg); +} diff --git a/tests/test-performance/dbus-spammer/dbus-services.h b/tests/test-performance/dbus-spammer/dbus-services.h new file mode 100644 index 0000000..6924fdc --- /dev/null +++ b/tests/test-performance/dbus-spammer/dbus-services.h @@ -0,0 +1,74 @@ +#ifndef _DBUS_THREAD_ +#define _DBUS_THREAD_ + +#include +#include +#include +#include +#include + +typedef QList Threads; +typedef QList Servers; + +class Maintainer : public QObject +{ + Q_OBJECT + +public: + Maintainer(Threads* t); + ~Maintainer(); + +protected: + void timerEvent(QTimerEvent *e); + + int timerID; + Threads* threads; + bool allOnline; +}; + +class DbusSpammer : public QThread +{ +public: + DbusSpammer(int id, QString service, int messageLen); + ~DbusSpammer(); + + void run(); + + DBusConnection* myBus; + DBusMessage* myMessage; + + bool firstTime; + bool exitThread; + int threadID; + const char* pszSpamData; + QString spamData; + QString serviceName; + +protected: + void timerEvent(QTimerEvent *e); + + int timerID; +}; + +class DbusServer: public QThread +{ + Q_OBJECT + +public: + DbusServer(int id); + ~DbusServer(); + + const QString& getServiceName() { return serviceName; } + const int getServiceID() { return serviceID; } + void run(); + +private: + QDBusConnection* myBus; + QString serviceName; + int serviceID; + +public slots: + Q_SCRIPTABLE QString ping(const QString &arg); +}; + +#endif // _DBUS_THREAD_ diff --git a/tests/test-performance/dbus-spammer/dbus-spammer.cpp b/tests/test-performance/dbus-spammer/dbus-spammer.cpp new file mode 100644 index 0000000..2d84a60 --- /dev/null +++ b/tests/test-performance/dbus-spammer/dbus-spammer.cpp @@ -0,0 +1,238 @@ +#include +#include +#include "dbus-services.h" + +sig_atomic_t volatile g_exit = 0; + +Maintainer::Maintainer(Threads* t) : QObject() +{ + allOnline = false; + threads = t; + timerID = startTimer(0); +} + +Maintainer::~Maintainer() +{ + killTimer(timerID); +} + +void Maintainer::timerEvent(QTimerEvent*) +{ + if( !allOnline ) + { + int count = 0; + Threads::iterator th; + for( th = threads->begin(); th != threads->end(); ++th ) + { + DbusSpammer* t = dynamic_cast(*th); + if( t ) + { + if( t->firstTime ) + count++; + } + } + + allOnline = (count == 0) && (threads->count() > 0); + + if( allOnline ) + printf("All thread online and working!\n"); + } + + fflush(stdout); + fflush(stderr); + + if( g_exit ) + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit"); +} + +class CommandLineParser +{ +public: + CommandLineParser(int argc, char** argv) + { + threadCount = 10; + dataSize = 512; + + parseArguments(argc, argv); + } + +public: + int threadCount; + int dataSize; + +private: + void parseArguments(int argc, char** argv) + { + int option, tmp; + + while( (option = getopt(argc, argv, "ht:s:")) != -1 ) + { + switch( option ) + { + case 'h': + usage(0); + return; + case 't': + tmp = atoi(optarg); + if( tmp > 0 ) + { + threadCount = tmp; + printf("Thread count: %d\n", threadCount); + } + else + { + printf("Invalid thread count, using default value %d!\n", threadCount); + } + break; + case 's': + tmp = atoi(optarg); + if( tmp > 0 ) + { + dataSize = tmp; + printf("Data size: %d\n", dataSize); + } + else + { + printf("Invalid data size, using default value %d!\n", dataSize); + } + break; + default: + usage(-1); + return; + } + } + } + + void usage(int theExitCode) + { + printf("usage: dbus-spammer [-h] [-t thread_count] [-s data_size]\n"); + printf("default values:\n"); + printf(" thread_count: %d\n", threadCount); + printf(" data_size : %d\n", dataSize); + exit(theExitCode); + } +}; + +void signal_handler(int signo, siginfo_t *info, void *data) +{ + switch( signo ) + { + case SIGHUP: + case SIGTERM: + case SIGINT: + default: + puts("Caught exit signal!"); + g_exit = 1; + } + + (void)info; + (void)data; +} + +bool installSignals() +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_sigaction = signal_handler; + sa.sa_flags = SA_SIGINFO; + + if( sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0 || sigaction(SIGINT, &sa, NULL) < 0 ) + { + puts("Failed to install signal handlers"); + return false; + } + + return true; +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + CommandLineParser cmd(argc, argv); + + int count = cmd.threadCount; + + Threads threads; + Servers servers; + + if( !installSignals() ) + { + return -2; + } + + if( !QDBusConnection::sessionBus().isConnected() ) + { + puts("Cannot connect to the D-Bus session bus."); + return -1; + } + + Maintainer maintainer(&threads); + + printf("Starting pair: "); + for( int i = 0; i < count; i++ ) + { + printf("#%d", i); + fflush(stdout); + DbusServer* s = new DbusServer(i); + s->start(QThread::NormalPriority); + servers.push_back(s); + usleep(100000); + + QThread* t = new DbusSpammer(i, s->getServiceName(), cmd.dataSize); + threads.push_back(t); + t->start(QThread::NormalPriority); + } + + puts(""); + + app.exec(); + + Threads::iterator th; + printf("Cleaning thread: "); + for( th = threads.begin(); th != threads.end(); ++th ) + { + DbusSpammer* t = dynamic_cast(*th); + if( t ) + { + printf("#%d", t->threadID); + + t->quit(); + int cnt = 0; + while( t->isRunning() ) + { + fflush(stdout); + fflush(stderr); + if( ++cnt == 1000 ) + { + printf("[T!]"); + t->terminate(); + break; + } + usleep(1000); + } + delete t; + } + } + puts(" ... done!"); + + Servers::iterator sv; + printf("Cleaning service: "); + for( sv = servers.begin(); sv != servers.end(); ++sv ) + { + DbusServer* s = dynamic_cast(*sv); + if( s ) + { + printf("#%d", s->getServiceID()); + s->quit(); + while( !s->isFinished() ) + usleep(1000); + + delete s; + } + } + + puts(" ... done!"); + + return 0; +} diff --git a/tests/test-performance/dbus-spammer/dbus-spammer.pro b/tests/test-performance/dbus-spammer/dbus-spammer.pro new file mode 100644 index 0000000..752d740 --- /dev/null +++ b/tests/test-performance/dbus-spammer/dbus-spammer.pro @@ -0,0 +1,21 @@ +TEMPLATE = app +TARGET = dbus-spammer +MOC_DIR = .moc +OBJECTS_DIR = .obj +DEPENDPATH += . +QT = core dbus +CONFIG += console link_pkgconfig +CONFIG -= app_bundle +PKGCONFIG += dbus-1 + +QMAKE_CXXFLAGS += -Wall + +# Input +SOURCES += dbus-spammer.cpp dbus-services.cpp +HEADERS += dbus-services.h + +QMAKE_DISTCLEAN += -r .moc .obj + +# Install options +target.path = /usr/lib/libresourceqt-performance-tests/ +INSTALLS = target diff --git a/tests/test-performance/template.html b/tests/test-performance/template.html new file mode 100644 index 0000000..6134d68 --- /dev/null +++ b/tests/test-performance/template.html @@ -0,0 +1,62 @@ + + + + + + + +Performance testrun for libresourceqt + + + + +
+
+%%TESTRUNINFO%% +
+ More performance test results can be found at http://policy.research.nokia.com/performance/ +
+ + + \ No newline at end of file diff --git a/tests/test-performance/test-performance.pro b/tests/test-performance/test-performance.pro new file mode 100644 index 0000000..e4303e9 --- /dev/null +++ b/tests/test-performance/test-performance.pro @@ -0,0 +1,15 @@ +##################################################################### +# Tests projectfile +##################################################################### + +CONFIG += ordered +TEMPLATE = subdirs + +SUBDIRS = dbus-spammer test-resourceqt-performance test-resourceqt-performance-collector + +# Install options +testsxml.path = /usr/share/libresourceqt-performance-tests/ +testsxml.files = tests.xml +testrunner.path = /usr/lib/libresourceqt-performance-tests/ +testrunner.files = test-resourceqt-performance-runner.sh test-resourceqt-performance.ini template.html +INSTALLS = testsxml testrunner diff --git a/tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.cpp b/tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.cpp new file mode 100644 index 0000000..594b07f --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.cpp @@ -0,0 +1,39 @@ +/* + * ResultsUpload.cpp + * + * Created on: May 14, 2010 + * Author: wlk + */ + +#include "ResultsUpload.h" + +ResultsUpload::ResultsUpload(QString fileToUpload) +{ + networkManager = new QNetworkAccessManager(this); + uploadFile = new QFile(fileToUpload); + connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); + uploadFile->open(QIODevice::ReadOnly); + +/* QNetworkAccessManager man; + QFile* f = new QFile("/usr/lib/libresourceqt-tests/test-performance.ini"); + f->open(QIODevice::ReadOnly); + man.post(QNetworkRequest(QUrl("http://policy.research.nokia.com/performance/upload.php")), f);*/ + networkManager->post(QNetworkRequest(QUrl("http://policy.research.nokia.com/performance/upload.php")), uploadFile); +} + +ResultsUpload::~ResultsUpload() +{ + if( networkManager ) + delete networkManager; + + if( uploadFile ) + { + uploadFile->close(); + delete uploadFile; + } +} + +void ResultsUpload::replyFinished(QNetworkReply* reply) +{ + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit"); +} diff --git a/tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.h b/tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.h new file mode 100644 index 0000000..ebc6213 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance-collector/ResultsUpload.h @@ -0,0 +1,26 @@ +#ifndef _RESULTSUPLOAD_H_ +#define _RESULTSUPLOAD_H_ + +#include +#include +#include +#include +#include + +class ResultsUpload : public QObject +{ + Q_OBJECT + +public: + ResultsUpload(QString fileToUpload); + virtual ~ResultsUpload(); + +private slots: + void replyFinished(QNetworkReply* reply); + +private: + QNetworkAccessManager* networkManager; + QFile* uploadFile; +}; + +#endif // _RESULTSUPLOAD_H_ diff --git a/tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.cpp b/tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.cpp new file mode 100644 index 0000000..a2aee81 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include "ResultsUpload.h" +#include + +class Reporter +{ +public: + Reporter(bool create) + { + QSettings settings("/usr/lib/libresourceqt-performance-tests/test-resourceqt-performance.ini", QSettings::IniFormat); + + settings.beginGroup("main"); + reportAcquire = settings.value("acqreport", "/tmp/acquire.csv").toString(); + reportRelease = settings.value("relreport", "/tmp/release.csv").toString(); + report = settings.value("report", "/tmp/resourceqt-performance.html").toString(); + reportTemplate = settings.value("template", "/usr/lib/libresourceqt-tests/performance/template.html").toString(); + timeLimit = settings.value("limit", 80.0).toDouble(); + settings.endGroup(); + + if( create && QFile::exists(report) ) + { + QFile::remove(report); + } + + if( !QFile::exists(report) ) + { + QFile::copy(reportTemplate, report); + } + } + + void updateReport(bool finalize) + { + QFile f(report); + f.open(QIODevice::ReadOnly | QIODevice::Text); + QString data(f.readAll().data()); + f.close(); + + QString replace, testName("name information missing"), testDesc("test information missing"); + char* env = getenv("TEST_DESC"); + if( env ) + testDesc = env; + + env = getenv("TEST_NAME"); + if( env ) + testName = env; + + QString results; + QString resAcq, resRel, resHead; + + readReport(reportAcquire, "Acquire", resAcq); + readReport(reportRelease, "Release", resRel); + + resHead = "TypeCountLimit [ms]Average [ms]Minimum [ms]Maximum [ms]Total time [ms]\n"; + results.sprintf("%s%s%s
", qPrintable(resHead), qPrintable(resAcq), qPrintable(resRel)); + replace.sprintf("

Testcase '%s'

\n

%s

\n

Test results

\n%s\n


", qPrintable(testName), qPrintable(testDesc), qPrintable(results)); + if( !finalize ) + replace += "%%TESTRUNINFO%%"; + + data.replace("%%TESTRUNINFO%%", replace); + + QFile::remove(report); + f.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&f); + out << data; + f.close(); + } + +public: + QString report; + +private: + QString reportAcquire; + QString reportRelease; + QString reportTemplate; + double timeLimit; + + bool readReport(QString reportFile, QString name, QString& table) + { + QFile f(reportFile); + + if( !f.open(QIODevice::ReadOnly | QIODevice::Text) ) + { + table.sprintf("%sN/A%.2fN/AN/AN/AN/A\n", qPrintable(name), timeLimit); + return false; + } + + int tCount = 0; + double tMin = 0, tMax = 0, tAvg = 0, tTotal = 0; + + for( int i = 0; i < 5; i++ ) + { + char temp[1024]; + if( f.readLine(temp, 1024) ) + { + QString qtemp(temp); + QStringList l = qtemp.split(';'); + QString name = l[0]; + QString val = l[1]; + + if( name == "Cnt" ) + tCount = val.toInt(); + else if( name == "Tot") + tTotal = val.toDouble(); + else if( name == "Max" ) + tMax = val.toDouble(); + else if( name == "Min" ) + tMin = val.toDouble(); + else if( name == "Avg" ) + tAvg = val.toDouble(); + } + } + + QString color = "#00FF00"; + if( tAvg > timeLimit ) + color = "#FF0000"; + + table.sprintf("%s%d%.2f%.2f%.2f%.2f%.2f\n", qPrintable(name), tCount, timeLimit, qPrintable(color), tAvg, tMin, tMax, tTotal); + f.close(); + + return true; + } +}; + +int main(int argc, char** argv) +{ + bool createReport = false, finalizeReport = false; + + if( argc > 1 ) + { + QString param; + param = argv[1]; + if( param == "--create" ) + createReport = true; + else if( param == "--finalize" ) + finalizeReport = true; + } + + Reporter r(createReport); + r.updateReport(finalizeReport); + + if( finalizeReport ) + { + QCoreApplication app(argc, argv); + ResultsUpload u(r.report); + app.exec(); + } + + return 0; +} diff --git a/tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.pro b/tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.pro new file mode 100644 index 0000000..2cffc92 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance-collector/test-resourceqt-performance-collector.pro @@ -0,0 +1,21 @@ +TEMPLATE = app +TARGET = test-resourceqt-performance-collector +MOC_DIR = .moc +OBJECTS_DIR = .obj +DEPENDPATH += . +QT = core network +CONFIG += console +CONFIG -= app_bundle + +INCLUDEPATH += $${LIBRESOURCEINC} +QMAKE_CXXFLAGS += -Wall + +# Input +HEADERS = ResultsUpload.h +SOURCES += test-resourceqt-performance-collector.cpp ResultsUpload.cpp + +QMAKE_DISTCLEAN += -r .moc .obj + +# Install options +target.path = /usr/lib/libresourceqt-performance-tests/ +INSTALLS = target diff --git a/tests/test-performance/test-resourceqt-performance-runner.sh b/tests/test-performance/test-resourceqt-performance-runner.sh new file mode 100755 index 0000000..b255a9c --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance-runner.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +# Source and export D-Bus session info +. /tmp/session_bus_address.user + +# D-Bus spamming is default option +nospam="0" +spamparam="-t10 -s50" + +if [ $# -eq 0 ] +then + echo "Usage: test-resourceqt-performance-runner.sh test_case_nr" + exit 2 +fi + +case "$1" in + 0) + export TEST_DESC="Testcase without dbus spamming" + export TEST_NAME="no dbus load" + nospam="1" + collectparam="--create" + ;; + 1) + export TEST_DESC="Testcase with 5 dbus spamming threads, message size 50 bytes" + export TEST_NAME="small dbus load" + spamparam="-t5 -s50" + ;; + 2) + export TEST_DESC="Testcase with 10 dbus spamming threads, message size 50 bytes" + export TEST_NAME="medium dbus load" + spamparam="-t10 -s50" + ;; + 3) + export TEST_DESC="Testcase with 20 dbus spamming threads, message size 50 bytes" + export TEST_NAME="heavy dbus load" + spamparam="-t20 -s50" + ;; + 4) + export TEST_DESC="Testcase with 20 dbus spamming threads, message size 150 bytes" + export TEST_NAME="very heavy dbus load" + spamparam="-t20 -s150" + collectparam="--finalize" + ;; + *) + echo "Usage: test-resourceqt-performance-runner.sh test_case_nr" + exit 2 + ;; +esac + +# Run spam server +if [ "$nospam" != "1" ] +then + /usr/lib/libresourceqt-performance-tests/dbus-spammer $spamparam & + if [ $? -ne 0 ] + then + exit 3; + fi + sleep 5 +fi + +# Check the speed! +/usr/lib/libresourceqt-performance-tests/test-resourceqt-performance +status=$? + +# Kill spammer +if [ "$nospam" != "1" ] +then + killall dbus-spammer +fi + +# Collect the results +/usr/lib/libresourceqt-performance-tests/test-resourceqt-performance-collector $collectparam + +if [ "$status" -ne 0 ] +then + echo "Test failed with value $status" +fi + +# Return number of failed acquire/release cycles, nothing should be failing... +exit $status diff --git a/tests/test-performance/test-resourceqt-performance.ini b/tests/test-performance/test-resourceqt-performance.ini new file mode 100644 index 0000000..5039182 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance.ini @@ -0,0 +1,12 @@ +[main] +acqreport=/tmp/acquire.csv +relreport=/tmp/release.csv +report=/tmp/resourceqt-performance.html +template=/usr/lib/libresourceqt-performance-tests/template.html +limit=80.0 + +[config] +AllResources="AudioPlayback,VideoPlayback" +OptionalResources= +ApplicationClass=player +TestCount=100 diff --git a/tests/test-performance/test-resourceqt-performance/client.cpp b/tests/test-performance/test-resourceqt-performance/client.cpp new file mode 100644 index 0000000..e172d21 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance/client.cpp @@ -0,0 +1,380 @@ +#include +#include + +#include +#include +#include + +#include "client.h" + +using namespace ResourcePolicy; + +Client::Client(int testCnt, QString appClass, uint32_t all, uint32_t optional) : QObject() +{ + QSettings settings("/usr/lib/libresourceqt-performance-tests/test-resourceqt-performance.ini", QSettings::IniFormat); + + settings.beginGroup("main"); + reportAcquire = settings.value("acqreport", "/tmp/acquire.csv").toString(); + reportRelease = settings.value("relreport", "/tmp/release.csv").toString(); + settings.endGroup(); + + totalTestCount = testCnt; + applicationClass = appClass; + resourceSet = NULL; + testState = TestStart; + testCounter = 1; + + allResources = all; + optionalResources = optional; + firstTime = true; + + failed = 0; + + mainTimerID = startTimer(0); +} + +Client::~Client() +{ + killTimer(mainTimerID); + + if( resourceSet != NULL ) + { + delete resourceSet; + resourceSet = NULL; + } +} + +uint32_t Client::parseResourceList(QString resourceListStr) +{ + struct + { + uint32_t resourceType; + const char* resourceName; + } + resourceDef[] = + { + { RES_AUDIO_PLAYBACK, "AudioPlayback" }, + { RES_VIDEO_PLAYBACK, "VideoPlayback" }, + { RES_AUDIO_RECORDING, "AudioRecording" }, + { RES_VIDEO_RECORDING, "VideoRecording" }, + { RES_VIBRA, "Vibra" }, + { RES_LEDS, "Leds" }, + { RES_BACKLIGHT, "BackLight" }, + { RES_SYSTEM_BUTTON, "SystemButton" }, + { RES_LOCK_BUTTON, "LockButton" }, + { RES_SCALE_BUTTON, "ScaleButton" }, + { RES_SNAP_BUTTON, "SnapButton" }, + { RES_LENS_COVER, "LensCover" }, + { 0, NULL } + }; + + uint32_t resourceList = 0; + + if( resourceListStr.isEmpty() || resourceListStr.isNull() ) + { + return 0; + } + else + { + QStringList resList = resourceListStr.split(",", QString::SkipEmptyParts); + + for( int i = 0; i < resList.count(); i++ ) + { + int pos = 0; + while( resourceDef[pos].resourceName != NULL ) + { + if( resList[i] == resourceDef[pos].resourceName ) + break; + + pos++; + } + + if( !resourceDef[pos].resourceName ) + { + const char* res = qPrintable(resList[i]); + printf("Ignoring invalid resource name '%s'\n", res); + } + else + { + resourceList |= resourceDef[pos].resourceType; + } + } + } + + return resourceList; +} + +void Client::createSet(uint32_t list, uint32_t optional) +{ + uint32_t resources[] = + { + RES_AUDIO_PLAYBACK, RES_VIDEO_PLAYBACK, RES_AUDIO_RECORDING, RES_VIDEO_RECORDING, RES_VIBRA, RES_LEDS, RES_BACKLIGHT, RES_SYSTEM_BUTTON, + RES_LOCK_BUTTON, RES_SCALE_BUTTON, RES_SNAP_BUTTON, RES_LENS_COVER, 0 + }; + + int pos = 0; + while( resources[pos] ) + { + if( list & resources[pos] ) + { + Resource* resource = NULL; + ResourceType res = getResourceType(resources[pos]); + bool opt = (optional & resources[pos]) == resources[pos]; + + resource = allocateResource(res, opt); + if( resource ) + { + resourceSet->addResourceObject(resource); + } + } + + pos++; + } +} + +bool Client::initialize() +{ + if( resourceSet ) + { + return false; + } + + resourceSet = new ResourceSet(applicationClass); + if( resourceSet == NULL ) + { + return false; + } + resourceSet->setAlwaysReply(); + + createSet(allResources, optionalResources); + + if( !connect(resourceSet, SIGNAL(resourcesGranted(QList )), this, SLOT(resourceAcquiredHandler(QList))) ) + { + return false; + } + + if( !connect(resourceSet, SIGNAL(resourcesDenied()), this, SLOT(resourceDeniedHandler())) ) + { + return false; + } + + if( !connect(resourceSet, SIGNAL(lostResources()), this, SLOT(resourceLostHandler())) ) + { + return false; + } + + connect(resourceSet, SIGNAL(resourcesReleased()), this, SLOT(resourceReleasedHandler())); + + return true; +} + +bool Client::cleanup() +{ + if( !disconnect(resourceSet, SIGNAL(resourcesGranted(QList )), this, SLOT(resourceAcquiredHandler(QList))) ) + { + return false; + } + + if( !disconnect(resourceSet, SIGNAL(resourcesDenied()), this, SLOT(resourceDeniedHandler())) ) + { + return false; + } + + if( !disconnect(resourceSet, SIGNAL(lostResources()), this, SLOT(resourceLostHandler())) ) + { + return false; + } + + disconnect(resourceSet, SIGNAL(resourcesReleased()), this, SLOT(resourceReleasedHandler())); + + if( resourceSet != NULL ) + { + delete resourceSet; + resourceSet = NULL; + } + + return true; +} + +Resource* Client::allocateResource(ResourceType resource, bool optional) +{ + Resource* retValue = NULL; + + switch( resource ) + { + case AudioPlaybackType: + retValue = new AudioResource(); + break; + case VideoPlaybackType: + retValue = new VideoResource(); + break; + case AudioRecorderType: + retValue = new AudioRecorderResource(); + break; + case VideoRecorderType: + retValue = new VideoRecorderResource(); + break; + case VibraType: + retValue = new VibraResource(); + break; + case LedsType: + retValue = new LedsResource(); + break; + case BacklightType: + retValue = new BacklightResource(); + break; + case SystemButtonType: + retValue = new SystemButtonResource(); + break; + case LockButtonType: + retValue = new LockButtonResource(); + break; + case ScaleButtonType: + retValue = new ScaleButtonResource(); + break; + case SnapButtonType: + retValue = new SnapButtonResource(); + break; + case LensCoverType: + retValue = new LensCoverResource(); + break; + case NumberOfTypes: + return NULL; + } + + if( retValue ) + { + retValue->setOptional(optional); + } + else + { + printf("Unknown resource type - %d\n", resource); + } + + return retValue; +} + +ResourceType Client::getResourceType(uint32_t resource) +{ + switch( resource ) + { + case RES_AUDIO_PLAYBACK: + return AudioPlaybackType; + case RES_VIDEO_PLAYBACK: + return VideoPlaybackType; + case RES_AUDIO_RECORDING: + return AudioRecorderType; + case RES_VIDEO_RECORDING: + return VideoRecorderType; + case RES_VIBRA: + return VibraType; + case RES_LEDS: + return LedsType; + case RES_BACKLIGHT: + return BacklightType; + case RES_SYSTEM_BUTTON: + return SystemButtonType; + case RES_LOCK_BUTTON: + return LockButtonType; + case RES_SCALE_BUTTON: + return ScaleButtonType; + case RES_SNAP_BUTTON: + return SnapButtonType; + case RES_LENS_COVER: + return LensCoverType; + } + + return NumberOfTypes; +} + +void Client::resourceAcquiredHandler(const QList& /*grantedResList*/) +{ + timeStatAcq.markEnd(false); + testState = TestRelease; +} + +void Client::resourceDeniedHandler() +{ +/* if( timeStat.markEnd() ) + { + timeStat.report(); + }*/ +} + +void Client::resourceLostHandler() +{ +/* if( timeStat.markEnd() ) + { + timeStat.report(); + }*/ +} + +void Client::resourceReleasedHandler() +{ + timeStatRel.markEnd(false); + testState = TestFinished; +} + +void Client::timerEvent(QTimerEvent*) +{ + if( firstTime ) + { + firstTime = false; + timeStatTotal.markStart(); + initialize(); + } + + switch( testState ) + { + case TestFinished: + if( testCounter == totalTestCount ) + { + cleanup(); + timeStatTotal.markEnd(false); + puts("\n*** RESULTS ***"); + printf("Total failed: %d/%d\n", failed, totalTestCount); + printf("Total time : %.2f ms\n", timeStatTotal.caseTime); + printf("Average time: %.2f ms (%.2f ms)\n", timeStatTotal.caseTime / (double)totalTestCount, timeStatTotal.caseTime / (double)(totalTestCount*2)); + printf("Acquire:\n\tAverage: %.2f ms\n\tMin : %.2f ms\n\tMax : %.2f ms\n", timeStatAcq.totalTime / (double)timeStatAcq.caseCount, timeStatAcq.totalMin, timeStatAcq.totalMax); + printf("Release:\n\tAverage: %.2f ms\n\tMin : %.2f ms\n\tMax : %.2f ms\n", timeStatRel.totalTime / (double)timeStatRel.caseCount, timeStatRel.totalMin, timeStatRel.totalMax); + timeStatAcq.exportData(qPrintable(reportAcquire)); + timeStatRel.exportData(qPrintable(reportRelease)); + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit"); + } + else + { + testCounter++; + testState = TestStart; + } + break; + case TestWaiting: + break; + case TestStart: + testState = TestAcquire; + printf("\rRunning round %d of %d ... ", testCounter, totalTestCount); + fflush(stdout); + break; + case TestAcquire: + timeStatAcq.markStart(); + if( !resourceSet || !resourceSet->acquire() ) + { + timeStatAcq.markEnd(true); + testState = TestFinished; + failed++; + } + else + testState = TestWaiting; + break; + case TestRelease: + timeStatRel.markStart(); + if( !resourceSet || !resourceSet->release() ) + { + timeStatRel.markEnd(true); + testState = TestFinished; + failed++; + } + else + testState = TestWaiting; + break; + } +} diff --git a/tests/test-performance/test-resourceqt-performance/client.h b/tests/test-performance/test-resourceqt-performance/client.h new file mode 100644 index 0000000..c7ecb30 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance/client.h @@ -0,0 +1,175 @@ +#ifndef _CLIENT_H_ +#define _CLIENT_H_ + +#include +#include + +#include +#include + +#define RES_AUDIO_PLAYBACK (1<<0) +#define RES_VIDEO_PLAYBACK (1<<1) +#define RES_AUDIO_RECORDING (1<<2) +#define RES_VIDEO_RECORDING (1<<3) +#define RES_VIBRA (1<<4) +#define RES_LEDS (1<<5) +#define RES_BACKLIGHT (1<<6) +#define RES_SYSTEM_BUTTON (1<<7) +#define RES_LOCK_BUTTON (1<<8) +#define RES_SCALE_BUTTON (1<<9) +#define RES_SNAP_BUTTON (1<<10) +#define RES_LENS_COVER (1<<11) + +class TimeStat +{ +public: + double totalTime; + double caseTime; + double totalMax; + double totalMin; + int caseCount; + + TimeStat() + { + running = false; + totalTime = 0; + caseTime = 0; + caseCount = 0; + totalMax = 0; + totalMin = 9999999999.; + }; + + inline void markStart() + { + if( running ) + return; + + running = true; + gettimeofday(&start, NULL); + } + + inline void markEnd(bool failed) + { + if( !running ) + return; + + gettimeofday(&end, NULL); + + running = false; + + timevalSub(&end, &start, &end); + caseTime = end.tv_sec * 1000.0 + end.tv_usec / 1000.0; + + if( !failed ) + { + totalMax = (totalMax < caseTime) ? caseTime : totalMax; + totalMin = (totalMin > caseTime) ? caseTime : totalMin; + totalTime += caseTime; + data.push_back(caseTime); + caseCount++; + } + } + + void exportData(const char* name) + { + FILE* f = fopen(name, "wt"); + + fprintf(f, "Cnt;%d\n", caseCount); + fprintf(f, "Tot;%.4f\n", totalTime); + fprintf(f, "Max;%.4f\n", totalMax); + fprintf(f, "Min;%.4f\n", totalMin); + fprintf(f, "Avg;%.4f\n", totalTime / (double)caseCount); + int pos = 0; + DataList::iterator it; + for( it = data.begin(); it != data.end(); ++it ) + { + fprintf(f, "%d;%.4f\n", ++pos, *it); + } + fclose(f); + } + +private: + typedef QList DataList; + + DataList data; + bool running; + struct timeval start; + struct timeval end; + + + void timevalSub(struct timeval *a, struct timeval *b, struct timeval *diff) + { + diff->tv_sec = a->tv_sec - b->tv_sec; + if( a->tv_usec < b->tv_usec ) + { + diff->tv_sec--; + diff->tv_usec = 1000000 - b->tv_usec + a->tv_usec; + } + else + diff->tv_usec = a->tv_usec - b->tv_usec; + } +}; + +class Client : public QObject +{ + Q_OBJECT + +public: + static uint32_t parseResourceList(QString resourceListStr); + +public: + Client(int testCnt, QString appClass, uint32_t all, uint32_t optional); + ~Client(); + + bool initialize(); + bool cleanup(); + int getFailed() { return failed; } + + double getAcquireAverage() { return timeStatAcq.totalTime / (double)timeStatAcq.caseCount; } + double getReleaseAverage() { return timeStatRel.totalTime / (double)timeStatRel.caseCount; } + +private slots: + void resourceAcquiredHandler(const QList& grantedResList); + void resourceDeniedHandler(); + void resourceLostHandler(); + void resourceReleasedHandler(); + +protected: + void timerEvent(QTimerEvent *e); + +private: + enum TestState + { + TestFinished = 0, + TestStart, + TestAcquire, + TestRelease, + TestWaiting + }; + + QString reportAcquire; + QString reportRelease; + TestState testState; + int testCounter; + int mainTimerID; + QString applicationClass; + TimeStat timeStatAcq; + TimeStat timeStatRel; + TimeStat timeStatTotal; + bool firstTime; + + uint32_t allResources; + uint32_t optionalResources; + + int failed; + int totalTestCount; + + ResourcePolicy::ResourceSet* resourceSet; + + ResourcePolicy::Resource* allocateResource(ResourcePolicy::ResourceType resource, bool optional); + ResourcePolicy::ResourceType getResourceType(uint32_t resource); + + void createSet(uint32_t list, uint32_t optional); +}; + +#endif diff --git a/tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.cpp b/tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.cpp new file mode 100644 index 0000000..69648b0 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include "client.h" + +class ConfigParser +{ +public: + ConfigParser() + { + exitCode = 0; + exitFlag = false; + + resourcesAll = 0; + resourcesOptional = 0; + repeatCount = 100; + + parseConfig(); + } + +public: + bool exitFlag; + int exitCode; + + int repeatCount; + uint32_t resourcesAll; + uint32_t resourcesOptional; + double timeLimit; + QString applicationClass; + +private: + void parseConfig() + { + QSettings settings("/usr/lib/libresourceqt-performance-tests/test-resourceqt-performance.ini", QSettings::IniFormat); + + settings.beginGroup("config"); + QString resAll = settings.value("AllResources", "AudioPlayback,VideoPlayback").toString(); + QString resOpt = settings.value("OptionalResources", "").toString(); + QString appClass = settings.value("ApplicationClass", "player").toString().toLower(); + repeatCount = settings.value("TestCount", 100).toInt(); + settings.endGroup(); + + settings.beginGroup("main"); + timeLimit = settings.value("limit", 80.0).toDouble(); + settings.endGroup(); + + resourcesAll = Client::parseResourceList(resAll); + resourcesOptional = Client::parseResourceList(resOpt); + applicationClass = parseClassString(qPrintable(appClass)); + + if( applicationClass.isEmpty() ) + { + printf("Wrong application class in configuration file! Using default ...\n"); + applicationClass = "player"; + } + + if( !resourcesAll ) + { + resAll = "AudioPlayback,VideoPlayback"; + resourcesAll = Client::parseResourceList(resAll); + + printf("No resources found in configuration file! Using defaults ...\n"); + } + + if( (resourcesAll | resourcesOptional) != resourcesAll ) + { + printf("Optional resources are not subset of all resources!\n"); + exitFlag = true; + exitCode = 1; + } + } + + const char* parseClassString(const char *str) + { + if( strcmp(str, "call") && strcmp(str, "camera") && strcmp(str, "ringtone") && strcmp(str, "alarm") && strcmp(str, "navigator") && strcmp(str, "game") + && strcmp(str, "player") && strcmp(str, "event") && strcmp(str, "background") ) + { + printf("Invalid class '%s'", str); + return NULL; + } + + return str; + } +}; + +int main(int argc, char *argv[]) +{ + ConfigParser parser; + + if( parser.exitFlag ) + return parser.exitCode; + + QCoreApplication app(argc, argv); + Client client(parser.repeatCount, parser.applicationClass, parser.resourcesAll, parser.resourcesOptional); + app.exec(); + + int retval = client.getFailed(); + if( retval > 0 ) + printf("One or more test iterations failed!\n"); + + int oldval = retval; + retval += client.getAcquireAverage() > parser.timeLimit ? 100 : 0; + if( retval > oldval ) + printf("Acquire average time is higher than limit (%.2f/%.2f ms) !\n", client.getAcquireAverage(), parser.timeLimit); + + oldval = retval; + retval += client.getReleaseAverage() > parser.timeLimit ? 100 : 0; + if( retval > oldval ) + printf("Release average time is higher than limit (%.2f/%.2f ms) !\n", client.getReleaseAverage(), parser.timeLimit); + + return retval; +} diff --git a/tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.pro b/tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.pro new file mode 100644 index 0000000..01c9573 --- /dev/null +++ b/tests/test-performance/test-resourceqt-performance/test-resourceqt-performance.pro @@ -0,0 +1,24 @@ +include(../../../common.pri) + +TEMPLATE = app +TARGET = test-resourceqt-performance +MOC_DIR = .moc +OBJECTS_DIR = .obj +DEPENDPATH += . +QT = core +CONFIG += console +CONFIG -= app_bundle + +INCLUDEPATH += $${LIBRESOURCEINC} +QMAKE_CXXFLAGS += -Wall +LIBS += -L$${LIBRESOURCEQT}/build -lresourceqt -L$${LIBDBUSQEVENTLOOP}/build -ldbus-qeventloop + +# Input +HEADERS = client.h +SOURCES += test-resourceqt-performance.cpp client.cpp + +QMAKE_DISTCLEAN += -r .moc .obj + +# Install options +target.path = /usr/lib/libresourceqt-performance-tests/ +INSTALLS = target diff --git a/tests/test-performance/tests.xml b/tests/test-performance/tests.xml new file mode 100644 index 0000000..852e2eb --- /dev/null +++ b/tests/test-performance/tests.xml @@ -0,0 +1,37 @@ + + + + + + + /usr/lib/libresourceqt-performance-tests/test-resourceqt-performance-runner.sh 0 + + + + /usr/lib/libresourceqt-performance-tests/test-resourceqt-performance-runner.sh 1 + + + + /usr/lib/libresourceqt-performance-tests/test-resourceqt-performance-runner.sh 2 + + + + /usr/lib/libresourceqt-performance-tests/test-resourceqt-performance-runner.sh 3 + + + + /usr/lib/libresourceqt-performance-tests/test-resourceqt-performance-runner.sh 4 + + + + /tmp/resourceqt-performance.html + + + + false + true + + + + + diff --git a/tests/tests.pro b/tests/tests.pro index 3d0658c..3622290 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -9,7 +9,8 @@ SUBDIRS = test-dbus-qeventloop \ test-dbus-pong \ test-resource \ test-resource-set \ - test-resource-engine + test-resource-engine \ + test-performance # Install options testsxml.path = /usr/share/libresourceqt-tests/ -- cgit v1.2.3