diff options
author | Rohan McGovern <rohan.mcgovern@nokia.com> | 2009-03-06 14:53:16 +1000 |
---|---|---|
committer | Rohan McGovern <rohan.mcgovern@nokia.com> | 2009-03-06 14:53:16 +1000 |
commit | 81ecf6284389848672c990255c395ad88bd8417c (patch) | |
tree | aecde23b72682cd838af5aa32e2fb66f8ea5e4d3 /benchmarks | |
parent | 16fd6dae77b47d8be1cf20423e2a1a75581aa278 (diff) |
Separate tst_messageserver benchmark from rest of tests, for Pulse and because
the test takes much longer to run than the rest.
Diffstat (limited to 'benchmarks')
-rw-r--r-- | benchmarks/benchmarks.pro | 6 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/3rdparty/cycle_p.h | 474 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/benchmarkcontext.cpp | 83 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/benchmarkcontext.h | 28 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/qscopedconnection.cpp | 30 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/qscopedconnection.h | 30 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/testfsusage.cpp | 29 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/testfsusage.h | 23 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/testmalloc.cpp | 328 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/testmalloc.h | 26 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/tst_messageserver.cpp | 668 | ||||
-rw-r--r-- | benchmarks/tst_messageserver/tst_messageserver.pro | 49 |
12 files changed, 1774 insertions, 0 deletions
diff --git a/benchmarks/benchmarks.pro b/benchmarks/benchmarks.pro new file mode 100644 index 00000000..c974b3f2 --- /dev/null +++ b/benchmarks/benchmarks.pro @@ -0,0 +1,6 @@ +TEMPLATE = subdirs +SUBDIRS = \ + tst_messageserver \ + +CONFIG += unittest + diff --git a/benchmarks/tst_messageserver/3rdparty/cycle_p.h b/benchmarks/tst_messageserver/3rdparty/cycle_p.h new file mode 100644 index 00000000..7aaa2b48 --- /dev/null +++ b/benchmarks/tst_messageserver/3rdparty/cycle_p.h @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2003, 2006 Matteo Frigo + * Copyright (c) 2003, 2006 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* $Id: cycle.h,v 1.52 2006-02-08 02:36:47 athena Exp $ */ + +/* machine-dependent cycle counters code. Needs to be inlined. */ + +/***************************************************************************/ +/* To use the cycle counters in your code, simply #include "cycle.h" (this + file), and then use the functions/macros: + + CycleCounterTicks getticks(void); + + CycleCounterTicks is an opaque typedef defined below, representing the current time. + You extract the elapsed time between two calls to gettick() via: + + double elapsed(CycleCounterTicks t1, CycleCounterTicks t0); + + which returns a double-precision variable in arbitrary units. You + are not expected to convert this into human units like seconds; it + is intended only for *comparisons* of time intervals. + + (In order to use some of the OS-dependent timer routines like + Solaris' gethrtime, you need to paste the autoconf snippet below + into your configure.ac file and #include "config.h" before cycle.h, + or define the relevant macros manually if you are not using autoconf.) +*/ + +/***************************************************************************/ +/* This file uses macros like HAVE_GETHRTIME that are assumed to be + defined according to whether the corresponding function/type/header + is available on your system. The necessary macros are most + conveniently defined if you are using GNU autoconf, via the tests: + + dnl --------------------------------------------------------------------- + + AC_C_INLINE + AC_HEADER_TIME + AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) + + AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t is defined in <sys/time.h>])],,[#if HAVE_SYS_TIME_H +#include <sys/time.h> +#endif]) + + AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime mach_absolute_time]) + + dnl Cray UNICOS _rtc() (real-time clock) intrinsic + AC_MSG_CHECKING([for _rtc intrinsic]) + rtc_ok=yes + AC_TRY_LINK([#ifdef HAVE_INTRINSICS_H +#include <intrinsics.h> +#endif], [_rtc()], [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])], [rtc_ok=no]) + AC_MSG_RESULT($rtc_ok) + + dnl --------------------------------------------------------------------- +*/ + +/***************************************************************************/ + +#ifndef QBENCHLIB_CYCLE_H +#define QBENCHLIB_CYCLE_H + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#define INLINE_ELAPSED(INL) static INL double elapsed(CycleCounterTicks t1, CycleCounterTicks t0) \ +{ \ + return (double)(t1 - t0); \ +} + +/*----------------------------------------------------------------*/ +/* Solaris */ +#if defined(HAVE_GETHRTIME) && defined(HAVE_HRTIME_T) && !defined(HAVE_TICK_COUNTER) +typedef hrtime_t CycleCounterTicks; + +#define getticks gethrtime + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* AIX v. 4+ routines to read the real-time clock or time-base register */ +#if defined(HAVE_READ_REAL_TIME) && defined(HAVE_TIME_BASE_TO_TIME) && !defined(HAVE_TICK_COUNTER) +typedef timebasestruct_t CycleCounterTicks; + +static inline CycleCounterTicks getticks(void) +{ + CycleCounterTicks t; + read_real_time(&t, TIMEBASE_SZ); + return t; +} + +static inline double elapsed(CycleCounterTicks t1, CycleCounterTicks t0) /* time in nanoseconds */ +{ + time_base_to_time(&t1, TIMEBASE_SZ); + time_base_to_time(&t0, TIMEBASE_SZ); + return ((t1.tb_high - t0.tb_high) * 1e9 + (t1.tb_low - t0.tb_low)); +} + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PowerPC ``cycle'' counter using the time base register. + */ +#if ((defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))) || (defined(__MWERKS__) && defined(macintosh))) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + unsigned int tbl, tbu0, tbu1; + + do { + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); + __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); + } while (tbu0 != tbu1); + + return (((unsigned long long)tbu0) << 32) | tbl; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, + from Carbon, requires no additional libraries to be linked). */ +#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER) +#include <mach/mach_time.h> +typedef uint64_t CycleCounterTicks; +#define getticks mach_absolute_time +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * Pentium cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + CycleCounterTicks ret; + + __asm__ __volatile__("rdtsc": "=A" (ret)); + /* no input, nothing else clobbered */ + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/* Visual C++ -- thanks to Morten Nissov for his help with this */ +#if _MSC_VER >= 1200 && _M_IX86 >= 500 && !defined(HAVE_TICK_COUNTER) +#include <windows.h> +typedef LARGE_INTEGER CycleCounterTicks; +#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */ + +static __inline CycleCounterTicks getticks(void) +{ + CycleCounterTicks retval; + + __asm { + RDTSC + mov retval.HighPart, edx + mov retval.LowPart, eax + } + return retval; +} + +static __inline double elapsed(CycleCounterTicks t1, CycleCounterTicks t0) +{ + return (double)(t1.QuadPart - t0.QuadPart); +} + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/*----------------------------------------------------------------*/ +/* + * X86-64 cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC)) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + unsigned a, d; + asm volatile("rdtsc" : "=a" (a), "=d" (d)); + return ((CycleCounterTicks)a) | (((CycleCounterTicks)d) << 32); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* PGI compiler, courtesy Cristiano Calonaci, Andrea Tarsi, & Roberto Gori. + NOTE: this code will fail to link unless you use the -Masmkeyword compiler + option (grrr). */ +#if defined(__PGI) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long CycleCounterTicks; +static CycleCounterTicks getticks(void) +{ + asm(" rdtsc; shl $0x20,%rdx; mov %eax,%eax; or %rdx,%rax; "); +} +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/* Visual C++ */ +#if _MSC_VER >= 1400 && (defined(_M_AMD64) || defined(_M_X64)) && !defined(HAVE_TICK_COUNTER) +#include <intrin.h> + +typedef unsigned __int64 CycleCounterTicks; + +#define getticks __rdtsc + +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * IA64 cycle counter + */ + +/* intel's icc/ecc compiler */ +#if (defined(__EDG_VERSION) || defined(__ECC)) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long CycleCounterTicks; +#include <ia64intrin.h> + +static __inline__ CycleCounterTicks getticks(void) +{ + return __getReg(_IA64_REG_AR_ITC); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* gcc */ +#if defined(__GNUC__) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + CycleCounterTicks ret; + + __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* HP/UX IA64 compiler, courtesy Teresa L. Johnson: */ +#if defined(__hpux) && defined(__ia64) && !defined(HAVE_TICK_COUNTER) +#include <machine/sys/inline.h> +typedef unsigned long CycleCounterTicks; + +static inline CycleCounterTicks getticks(void) +{ + CycleCounterTicks ret; + + ret = _Asm_mov_from_ar (_AREG_ITC); + return ret; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/* Microsoft Visual C++ */ +#if defined(_MSC_VER) && defined(_M_IA64) && !defined(HAVE_TICK_COUNTER) +typedef unsigned __int64 CycleCounterTicks; + +# ifdef __cplusplus +extern "C" +# endif +ticks __getReg(int whichReg); +#pragma intrinsic(__getReg) + +static __inline CycleCounterTicks getticks(void) +{ + volatile CycleCounterTicks temp; + temp = __getReg(3116); + return temp; +} + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PA-RISC cycle counter + */ +#if defined(__hppa__) || defined(__hppa) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long CycleCounterTicks; + +# ifdef __GNUC__ +static __inline__ CycleCounterTicks getticks(void) +{ + CycleCounterTicks ret; + + __asm__ __volatile__("mfctl 16, %0": "=r" (ret)); + /* no input, nothing else clobbered */ + return ret; +} +# else +# include <machine/inline.h> +static inline unsigned long getticks(void) +{ + register CycleCounterTicks ret; + _MFCTL(16, ret); + return ret; +} +# endif + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* S390, courtesy of James Treacy */ +#if defined(__GNUC__) && defined(__s390__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + CycleCounterTicks cycles; + __asm__("stck 0(%0)" : : "a" (&(cycles)) : "memory", "cc"); + return cycles; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__alpha__) && !defined(HAVE_TICK_COUNTER) +/* + * The 32-bit cycle counter on alpha overflows pretty quickly, + * unfortunately. A 1GHz machine overflows in 4 seconds. + */ +typedef unsigned int CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + unsigned long cc; + __asm__ __volatile__ ("rpcc %0" : "=r"(cc)); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__sparc_v9__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long CycleCounterTicks; + +static __inline__ CycleCounterTicks getticks(void) +{ + CycleCounterTicks ret; + __asm__ __volatile__("rd %%tick, %0" : "=r" (ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if (defined(__DECC) || defined(__DECCXX)) && defined(__alpha) && defined(HAVE_C_ASM_H) && !defined(HAVE_TICK_COUNTER) +# include <c_asm.h> +typedef unsigned int CycleCounterTicks; + +static __inline CycleCounterTicks getticks(void) +{ + unsigned long cc; + cc = asm("rpcc %v0"); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +/* SGI/Irix */ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && !defined(HAVE_TICK_COUNTER) +typedef struct timespec CycleCounterTicks; + +static inline CycleCounterTicks getticks(void) +{ + struct timespec t; + clock_gettime(CLOCK_SGI_CYCLE, &t); + return t; +} + +static inline double elapsed(CycleCounterTicks t1, CycleCounterTicks t0) +{ + return (double)(t1.tv_sec - t0.tv_sec) * 1.0E9 + + (double)(t1.tv_nsec - t0.tv_nsec); +} +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* Cray UNICOS _rtc() intrinsic function */ +#if defined(HAVE__RTC) && !defined(HAVE_TICK_COUNTER) +#ifdef HAVE_INTRINSICS_H +# include <intrinsics.h> +#endif + +typedef long long CycleCounterTicks; + +#define getticks _rtc + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +#endif // QBENCHLIB_CYCLE_H diff --git a/benchmarks/tst_messageserver/benchmarkcontext.cpp b/benchmarks/tst_messageserver/benchmarkcontext.cpp new file mode 100644 index 00000000..d9779d2f --- /dev/null +++ b/benchmarks/tst_messageserver/benchmarkcontext.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "benchmarkcontext.h" +#include "testfsusage.h" +#include "testmalloc.h" +#include "3rdparty/cycle_p.h" +#include <valgrind/valgrind.h> + +#include <QDebug> +#include <QDir> +#include <QTest> + +class BenchmarkContextPrivate +{ +public: + bool xml; + qint64 homeUsage; + QTime time; +#ifdef HAVE_TICK_COUNTER + CycleCounterTicks ticks; +#endif +}; + +BenchmarkContext::BenchmarkContext(bool xml) + : d(new BenchmarkContextPrivate) +{ + d->xml = xml; + d->homeUsage = TestFsUsage::usage(QDir::homePath()); + +#ifdef HAVE_TICK_COUNTER + d->ticks = getticks(); +#endif + + d->time.start(); + + TestMalloc::resetNow(); + TestMalloc::resetPeak(); +} + +BenchmarkContext::~BenchmarkContext() +{ + if (!QTest::currentTestFailed()) { + qint64 newHomeUsage = TestFsUsage::usage(QDir::homePath()); +#ifdef HAVE_TICK_COUNTER + CycleCounterTicks newTicks = getticks(); +#endif + + int heapUsageTotal = TestMalloc::peakTotal()/1024; + int heapUsageUsable = TestMalloc::peakUsable()/1024; + int ms = d->time.elapsed(); + quint64 cycles = quint64(elapsed(newTicks,d->ticks)); + qint64 diskUsage = (newHomeUsage - d->homeUsage) / 1024; + if (d->xml) { + if (!RUNNING_ON_VALGRIND) { + fprintf(stdout, "<BenchmarkResult metric=\"heap_usage\" tag=\"%s_\" value=\"%d\" iterations=\"1\"/>\n", QTest::currentDataTag(), heapUsageTotal); + } + fprintf(stdout, "<BenchmarkResult metric=\"disk_usage\" tag=\"%s_\" value=\"%lld\" iterations=\"1\"/>\n", QTest::currentDataTag(), diskUsage); + fprintf(stdout, "<BenchmarkResult metric=\"cycles\" tag=\"%s_\" value=\"%llu\" iterations=\"1\"/>\n", QTest::currentDataTag(), cycles); + fprintf(stdout, "<BenchmarkResult metric=\"walltime\" tag=\"%s_\" value=\"%d\" iterations=\"1\"/>\n", QTest::currentDataTag(), ms); + fflush(stdout); + } + else { + if (!RUNNING_ON_VALGRIND) { + qWarning() << "Peak heap usage (kB):" << heapUsageTotal << "total (" << heapUsageUsable << "usable )"; + } + qWarning() << "Change in homedir disk usage:" << diskUsage << "kB"; + qWarning("Cycles: %llu", cycles); + qWarning() << "Execution time:" << ms << "ms"; + } + } + + delete d; + d = 0; +} + diff --git a/benchmarks/tst_messageserver/benchmarkcontext.h b/benchmarks/tst_messageserver/benchmarkcontext.h new file mode 100644 index 00000000..12c8f53c --- /dev/null +++ b/benchmarks/tst_messageserver/benchmarkcontext.h @@ -0,0 +1,28 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef BENCHMARKCONTEXT_H +#define BENCHMARKCONTEXT_H + +#include <QtGlobal> + +class BenchmarkContextPrivate; +class BenchmarkContext +{ +public: + BenchmarkContext(bool xml = false); + ~BenchmarkContext(); + +private: + BenchmarkContextPrivate* d; +}; + +#endif + diff --git a/benchmarks/tst_messageserver/qscopedconnection.cpp b/benchmarks/tst_messageserver/qscopedconnection.cpp new file mode 100644 index 00000000..2fd2db04 --- /dev/null +++ b/benchmarks/tst_messageserver/qscopedconnection.cpp @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qscopedconnection.h" + +QScopedConnection::QScopedConnection( + QObject* sender, char const* signal, + QObject* receiver, char const* slot) + : m_sender(sender) + , m_signal(signal) + , m_receiver(receiver) + , m_slot(slot) +{ + if (!QObject::connect(sender, signal, receiver, slot)) + Q_ASSERT(0); +} + +QScopedConnection::~QScopedConnection() +{ + if (m_sender && m_receiver) + QObject::disconnect(m_sender, m_signal, m_receiver, m_slot); +} + diff --git a/benchmarks/tst_messageserver/qscopedconnection.h b/benchmarks/tst_messageserver/qscopedconnection.h new file mode 100644 index 00000000..b28c7f14 --- /dev/null +++ b/benchmarks/tst_messageserver/qscopedconnection.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCOPEDCONNECTION_H +#define QSCOPEDCONNECTION_H + +#include <QObject> +#include <QPointer> + +class QScopedConnection +{ +public: + QScopedConnection(QObject*, char const*, QObject*, char const*); + ~QScopedConnection(); +private: + QPointer<QObject> m_sender; + QByteArray m_signal; + QPointer<QObject> m_receiver; + QByteArray m_slot; +}; + +#endif + diff --git a/benchmarks/tst_messageserver/testfsusage.cpp b/benchmarks/tst_messageserver/testfsusage.cpp new file mode 100644 index 00000000..63f76b92 --- /dev/null +++ b/benchmarks/tst_messageserver/testfsusage.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "testfsusage.h" + +#include <QDir> +#include <QFileInfo> + +qint64 TestFsUsage::usage(QString const& path) +{ + qint64 ret = 0; + QFileInfo fi(path); + ret += fi.size(); + if (!fi.isSymLink() && fi.isDir()) { + QDir dir(fi.absoluteFilePath()); + foreach (QString const& name, dir.entryList(QDir::NoDotAndDotDot|QDir::AllEntries)) { + ret += usage(path + "/" + name); + } + } + return ret; +} + diff --git a/benchmarks/tst_messageserver/testfsusage.h b/benchmarks/tst_messageserver/testfsusage.h new file mode 100644 index 00000000..47537f28 --- /dev/null +++ b/benchmarks/tst_messageserver/testfsusage.h @@ -0,0 +1,23 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTFSUSAGE_H +#define TESTFSUSAGE_H + +#include <QString> + +class TestFsUsage +{ +public: + static qint64 usage(QString const&); +}; + +#endif + diff --git a/benchmarks/tst_messageserver/testmalloc.cpp b/benchmarks/tst_messageserver/testmalloc.cpp new file mode 100644 index 00000000..e63ab330 --- /dev/null +++ b/benchmarks/tst_messageserver/testmalloc.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "testmalloc.h" + +#include <QHash> +#include <QMutex> +#include <QMutexLocker> + +#include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> + +/* + Define this to have testmalloc do more extensive selftests to help verify + that all allocations are going through our overridden allocator. + This has a negative impact on performance. +*/ +//#define TEST_TESTMALLOC + +/* Additional amount of memory allocated by malloc for each chunk */ +#define CHUNK_OVERHEAD (2*sizeof(size_t)) + +struct TestMallocPrivate +{ + static void init(); + void selftest(); + + TestMallocPrivate() + : valid(false) + , peak_usable(0) + , peak_total(0) + , now_usable(0) + , now_overhead(0) + { + instance = this; + } + + ~TestMallocPrivate() + { + instance = 0; + } + + void updatePeak(); + + static TestMallocPrivate* instance; + +#ifdef TEST_TESTMALLOC + QAtomicInt inTestMalloc; + static void afterMorecore(); +#endif + + bool valid; + QAtomicInt peak_usable; + QAtomicInt peak_total; + QAtomicInt now_usable; + QAtomicInt now_overhead; +}; + +#define D (TestMallocPrivate::instance) + +TestMallocPrivate* TestMallocPrivate::instance = 0; + +/* + libc versions of functions. These are aliases for the libc malloc which we can + use to avoid using dlsym to look up malloc. That is troublesome because dlsym + itself will call malloc. +*/ +extern "C" void* __libc_malloc(size_t); +extern "C" void* __libc_calloc(size_t,size_t); +extern "C" void* __libc_realloc(void*,size_t); +extern "C" void* __libc_memalign(size_t,size_t); +extern "C" void __libc_free(void*); + +int TestMalloc::peakUsable() +{ + if (!D) TestMallocPrivate::init(); + if (D->valid) + return D->peak_usable; + else + return -1; +} + +int TestMalloc::peakTotal() +{ + if (!D) TestMallocPrivate::init(); + if (D->valid) + return D->peak_total; + else + return -1; +} + +int TestMalloc::nowUsable() +{ + if (!D) TestMallocPrivate::init(); + if (D->valid) + return D->now_usable; + else + return -1; +} + +int TestMalloc::nowTotal() +{ + if (!D) TestMallocPrivate::init(); + if (D->valid) + return D->now_usable + D->now_overhead; + else + return -1; +} + +void TestMalloc::resetPeak() +{ + if (!D) TestMallocPrivate::init(); + D->peak_usable.fetchAndStoreOrdered(D->now_usable); + D->peak_total.fetchAndStoreOrdered(D->now_usable + D->now_overhead); +} + +void TestMalloc::resetNow() +{ + if (!D) TestMallocPrivate::init(); + D->now_usable = 0; + D->now_overhead = 0; +} + +void (*__malloc_initialize_hook) (void) = TestMallocPrivate::init; + +void TestMallocPrivate::init() +{ + /* + When using glibc malloc, this function will be called before any heap allocation. + When using other malloc and when running under valgrind, we might get called after + some heap allocation. + */ + struct mallinfo info = mallinfo(); + static TestMallocPrivate testmalloc; + testmalloc.now_usable = info.uordblks; + testmalloc.now_overhead = 0; /* cannot get this figure, but should be close to 0. */ + TestMalloc::resetPeak(); + testmalloc.selftest(); + + /* Turn off mmap so that all blocks have a fixed overhead. */ + mallopt(M_MMAP_MAX, 0); + +#ifdef TEST_TESTMALLOC + __after_morecore_hook = &TestMallocPrivate::afterMorecore; + mallopt(M_TRIM_THRESHOLD, 0); + mallopt(M_TOP_PAD, 0); +#endif +} + +#ifdef TEST_TESTMALLOC +void TestMallocPrivate::afterMorecore() +{ + TestMallocPrivate* d = TestMallocPrivate::instance; + if (d && (0 == (int)(d->inTestMalloc))) { + fprintf(stderr, "Some memory allocation failed to go through hooks!\n"); + fflush(stderr); + abort(); + } +} +#endif + +void TestMallocPrivate::selftest() +{ + int before = this->now_usable; + int during; + int after; + char* array = 0; + { + QByteArray ba; + ba.resize(512); + array = new char[512]; + + during = this->now_usable; + } + delete [] array; + after = this->now_usable; + + if (!(during >= before+1024)) { + qWarning("Heap usage measurement fail: heap before byte array was %d, during was %d (expected at least %d). Heap usage will not be measured.", before, during, before + 1024); + return; + } + + /* + After and before ideally would be the same, but in practice memory may have been allocated + or freed, e.g. by the dynamic linker looking up QByteArray symbols. + */ + if (qAbs(after - before) >= 128) { + qWarning("Heap usage measurement fail: heap before byte array was %d, after was %d (expected after to be approximately equal to before). Heap usage will not be measured.", before, after); + return; + } + + valid = true; +} + +void TestMallocPrivate::updatePeak() +{ + if (now_usable > peak_usable) { + peak_usable.fetchAndStoreOrdered(now_usable); + } + if (now_usable + now_overhead > peak_total) { + peak_total.fetchAndStoreOrdered(now_usable + now_overhead); + } +} + +#ifdef TEST_TESTMALLOC +#define REF \ + bool didref = false; \ + do { if (D) { \ + didref = true; \ + D->inTestMalloc.ref(); \ + } } while(0) +#define DEREF if (didref) D->inTestMalloc.deref() +#else +#define REF do {} while(0) +#define DEREF do {} while(0) +#endif + +extern "C" void* malloc(size_t size) +{ + REF; + void* out = __libc_malloc(size); + DEREF; + if (out && D) { + D->now_usable.fetchAndAddOrdered(malloc_usable_size(out)); + D->now_overhead.fetchAndAddOrdered(CHUNK_OVERHEAD); + D->updatePeak(); + } + return out; +} + +extern "C" void* calloc(size_t nmemb, size_t size) +{ + REF; + void* out = __libc_calloc(nmemb, size); + DEREF; + if (out && D) { + D->now_usable.fetchAndAddOrdered(malloc_usable_size(out)); + D->now_overhead.fetchAndAddOrdered(CHUNK_OVERHEAD); + D->updatePeak(); + } + return out; +} + +extern "C" void* realloc(void* in, size_t size) +{ + size_t oldsize = (D && in) ? malloc_usable_size(in) : 0; + + REF; + void* out = __libc_realloc(in,size); + DEREF; + + if (D) { + D->now_usable.fetchAndAddOrdered(malloc_usable_size(out) - oldsize); + /* Overhead is affected only if old size was 0 */ + if (!oldsize) D->now_overhead.fetchAndAddOrdered(CHUNK_OVERHEAD); + D->updatePeak(); + } + return out; +} + +extern "C" void* memalign(size_t alignment, size_t size) +{ + REF; + void* out = __libc_memalign(alignment, size); + DEREF; + if (out && D) { + D->now_usable.fetchAndAddOrdered(malloc_usable_size(out)); + D->now_overhead.fetchAndAddOrdered(CHUNK_OVERHEAD); + D->updatePeak(); + } + return out; +} + +extern "C" void free(void* in) +{ + size_t oldsize = (D && in) ? malloc_usable_size(in) : 0; + REF; + __libc_free(in); + if (D && oldsize) { + D->now_usable.fetchAndAddOrdered(-oldsize); + D->now_overhead.fetchAndAddOrdered(-CHUNK_OVERHEAD); + D->updatePeak(); + } + DEREF; +} + +/* Force new/delete to go through malloc so we can profile them too. */ +void* operator new[](size_t size) +{ + return ::malloc(size); +} + +void* operator new(size_t size) +{ + return ::malloc(size); +} + +void operator delete[](void* p) +{ + ::free(p); +} + +void operator delete[](void* p, size_t /*size*/) +{ + ::free(p); +} + +void operator delete(void* p) +{ + ::free(p); +} + +void operator delete(void* p, size_t /*size*/) +{ + ::free(p); +} + diff --git a/benchmarks/tst_messageserver/testmalloc.h b/benchmarks/tst_messageserver/testmalloc.h new file mode 100644 index 00000000..a8eca9ef --- /dev/null +++ b/benchmarks/tst_messageserver/testmalloc.h @@ -0,0 +1,26 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTMALLOC_H +#define TESTMALLOC_H + +class TestMalloc +{ +public: + static int peakUsable(); + static int peakTotal(); + static int nowUsable(); + static int nowTotal(); + static void resetPeak(); + static void resetNow(); +}; + +#endif + diff --git a/benchmarks/tst_messageserver/tst_messageserver.cpp b/benchmarks/tst_messageserver/tst_messageserver.cpp new file mode 100644 index 00000000..53d10156 --- /dev/null +++ b/benchmarks/tst_messageserver/tst_messageserver.cpp @@ -0,0 +1,668 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QProcess> +#include <QTemporaryFile> +#include <QTest> +#include <QDir> +#include <QTimer> +#include <errno.h> +#include <qmailnamespace.h> +#include <stdio.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <imapconfiguration.h> +#include <qmailserviceaction.h> +#include <qmailstore.h> +#include <messageserver.h> +#include "benchmarkcontext.h" +#include "qscopedconnection.h" +#include <valgrind/callgrind.h> +#include <valgrind/valgrind.h> + +/* + This file is $TROLLTECH_INTERNAL$ + It contains information about internal mail accounts. +*/ + +class tst_MessageServer; +typedef void (tst_MessageServer::*TestFunction)(); + +typedef QList<QByteArray> TestMail; +typedef QList<TestMail> TestMailList; +Q_DECLARE_METATYPE(TestMailList); + +class tst_MessageServer : public QObject +{ + Q_OBJECT +public: + bool verbose; +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void completeRetrievalImap(); + void completeRetrievalImap_impl(); + void completeRetrievalImap_data(); + + void removeMessages(); + void removeMessages_impl(); + void removeMessages_data(); + + void replaceMessages(); + void replaceMessages_impl(); + void replaceMessages_data(); + +protected slots: + void onActivityChanged(QMailServiceAction::Activity); + void onProgressChanged(uint,uint); + +private: + void compareMessages(QMailMessageIdList const&, TestMailList const&); + void waitForActivity(QMailServiceAction*, QMailServiceAction::Activity, int); + void addAccount(QMailAccount*, QString const&, QString const&, QString const&, QString const&, int); + void removePath(QString const&); + void runInChildProcess(TestFunction); + + QEventLoop* m_loop; + QTimer* m_timer; + QMailServiceAction::Activity m_expectedState; + QString m_imapServer; + bool m_xml; +}; + +void tst_MessageServer::initTestCase() +{ + QProcess proc; + proc.start("hostname -d"); + QVERIFY(proc.waitForStarted()); + QVERIFY(proc.waitForFinished()); + QByteArray out = proc.readAll(); + if (out.contains("nokia")) + m_imapServer = "mail-nokia.trolltech.com.au"; + else + m_imapServer = "mail.trolltech.com.au"; + + m_xml = false; + foreach (QString const& arg, QCoreApplication::arguments()) { + if (arg == QLatin1String("-xml") || arg == QLatin1String("-lightxml")) { + m_xml = true; + } + } +} + +void tst_MessageServer::cleanupTestCase() +{ +} + +void tst_MessageServer::init() +{ + removePath(QMail::dataPath()); +} + +void tst_MessageServer::cleanup() +{ init(); } + +void tst_MessageServer::removePath(QString const& path) +{ + QFileInfo fi(path); + if (!fi.exists()) return; + + if (fi.isDir() && !fi.isSymLink()) { + QDir dir(path); + foreach (QString const& name, dir.entryList(QDir::NoDotAndDotDot|QDir::AllEntries|QDir::Hidden)) { + removePath(path + "/" + name); + } + } + + QDir parent = fi.dir(); + + bool ok; + if (fi.isDir() && !fi.isSymLink()) { + ok = parent.rmdir(fi.fileName()); + } + else { + ok = parent.remove(fi.fileName()); + } + if (!ok) { + qFatal("Could not delete %s", qPrintable(path)); + } +} + +void tst_MessageServer::completeRetrievalImap() +{ + runInChildProcess(&tst_MessageServer::completeRetrievalImap_impl); +} + +void tst_MessageServer::runInChildProcess(TestFunction fn) +{ + if (RUNNING_ON_VALGRIND) { + qWarning( + "Test is being run under valgrind. Testfunctions will not be run in child processes.\n" + "Run only one testfunction per test run for best results.\n" + ); + (this->*fn)(); + return; + } + + /* + Run the test in a separate process. + This is done so that subsequent tests are not affected by any in-process + caching of data or left over data from a previous run. + */ + pid_t pid = ::fork(); + if (-1 == pid) { + qFatal("fork: %s", strerror(errno)); + } + if (0 != pid) { + int status; + if (pid != waitpid(pid, &status, 0)) + qFatal("waitpid: %s", strerror(pid)); + if (!WIFEXITED(status)) { + if (WIFSIGNALED(status)) { + status = WTERMSIG(status); + QFAIL(qPrintable(QString("Child terminated by signal %1").arg(status))); + } + QFAIL(qPrintable(QString("Child exited for unknown reason with status %1").arg(status))); + } + + status = WEXITSTATUS(status); + if (status != 0) { + QFAIL(qPrintable(QString("Child exited with exit code %1").arg(status))); + } + return; + } + + /* We get these messages from not using a QtopiaApplication */ + for (int i = 0; i < 2; ++i) { + QTest::ignoreMessage(QtWarningMsg, "Object::connect: No such signal QApplication::appMessage(QString,QByteArray)"); + QTest::ignoreMessage(QtWarningMsg, "Object::connect: (sender name: 'tst_messageserver')"); + } + + (this->*fn)(); + + int exitcode = 0; + if (QTest::currentTestFailed()) + exitcode = 1; + + fflush(stdout); + fflush(stderr); + _exit(exitcode); +} + +/* Test full retrieval of all messages from a specific account */ +void tst_MessageServer::completeRetrievalImap_impl() +{ + static const char service[] = "imap4"; + + QFETCH(QString, user); + QFETCH(QString, password); + QFETCH(QString, server); + QFETCH(int, port); + QFETCH(TestMailList,mails); + + QMailMessageIdList fetched; + /* Valgrind slows things down quite a lot. */ + static const int MAXTIME = + RUNNING_ON_VALGRIND ? qMax(60000, 2000*mails.count()) : + 60000; + + QMailStore* ms = 0; + { + BenchmarkContext ctx(m_xml); + + new MessageServer; + ms = QMailStore::instance(); + + QMailAccount account; + addAccount(&account, service, user, password, server, port); + if (QTest::currentTestFailed()) return; + + /* Get message count for this account */ + QMailRetrievalAction retrieve; + + retrieve.synchronize(account.id()); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + + /* Ensure we have all the messages we expect */ + QCOMPARE(ms->countMessages(), mails.count()); + + /* OK, now download the entire message bodies. */ + fetched = ms->queryMessages(); + QCOMPARE(fetched.count(), mails.count()); + retrieve.retrieveMessages(fetched, QMailRetrievalAction::Content); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + } + + compareMessages(fetched, mails); +} + +void tst_MessageServer::waitForActivity(QMailServiceAction* action, QMailServiceAction::Activity state, int timeout) +{ + QScopedConnection c1(action, SIGNAL(activityChanged(QMailServiceAction::Activity)), this, SLOT(onActivityChanged(QMailServiceAction::Activity))); + QScopedConnection c2(action, SIGNAL(progressChanged(uint,uint)), this, SLOT(onProgressChanged(uint,uint))); + + QEventLoop loop; + QTimer timer; + QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.setInterval(timeout); + timer.setSingleShot(true); + + m_expectedState = state; + + timer.start(); + + m_loop = &loop; + m_timer = &timer; + int code = loop.exec(); + bool timed_out = m_loop; + m_timer = 0; + m_loop = 0; + + QCOMPARE(code, 0); + + QVERIFY2(!timed_out, qPrintable(QString("%1 timed out").arg(QString::fromLatin1(action->metaObject()->className())))); +} + +void tst_MessageServer::addAccount(QMailAccount* account, QString const& service, QString const& user, QString const& password, QString const& server, int port) +{ + if (service != QLatin1String("imap4")) { + QFAIL(qPrintable(QString("Unknown service type %1").arg(service))); + } + + account->setMessageType(QMailMessageMetaData::Email); + account->setStatus(QMailAccount::CanRetrieve, true); + account->setStatus(QMailAccount::MessageSource, true); + account->setStatus(QMailAccount::Enabled, true); + + QMailAccountConfiguration config; + config.addServiceConfiguration(service); + + ImapConfigurationEditor imap(&config); + imap.setVersion(100); + imap.setType(QMailServiceConfiguration::Source); + imap.setMailUserName(user); + imap.setMailPassword(password); + imap.setMailServer(server); + imap.setMailPort(port); + imap.setAutoDownload(false); + imap.setDeleteMail(false); + imap.setMaxMailSize(0); + + QVERIFY(QMailStore::instance()->addAccount(account, &config)); +} + +void tst_MessageServer::compareMessages(QMailMessageIdList const& actual, TestMailList const& expected) +{ + /* + Go through the fetched messages and make sure they are what we expect. + Note that this should be outside of BenchmarkContext sections so we don't count the + memory/time used to do this. + */ + QMailStore* ms = QMailStore::instance(); + for (int i = 0; i < actual.count(); ++i) { + QByteArray act = ms->message(actual.at(i)).toRfc2822(); + QVERIFY(act.size() > 0); + foreach (QByteArray const& exp, expected.at(i)) { + QVERIFY2(act.contains(exp), qPrintable(QString("Message was expected to contain this string, but didn't: %1\nMessage: %2").arg(QString::fromLatin1(exp)).arg(QString::fromLatin1(act)))); + } +#ifdef LEARN + if (!exp.at(i).count()) { + foreach (QByteArray const& line, actual.split('\n')) + qDebug() << line.constData(); + } +#endif + } +} + +void tst_MessageServer::completeRetrievalImap_data() +{ + QTest::addColumn<QString> ("user"); + QTest::addColumn<QString> ("password"); + QTest::addColumn<QString> ("server"); + QTest::addColumn<int> ("port"); + QTest::addColumn<TestMailList>("mails"); + + /* + Note - this testdata is deliberately _not_ strictly in order from smallest + to largest, because if it were, resource leaks between tests might be hidden. + */ + + TestMailList list; + + list.clear(); + for (int i = 0; i < 200; ++i) + list << TestMail(); + QTest::newRow("small_messages--200") + << QString::fromLatin1("mailtst31") + << QString::fromLatin1("testme31") + << m_imapServer + << 143 + << list + ; + + list.clear(); + for (int i = 0; i < 1000; ++i) + list << TestMail(); + QTest::newRow("small_messages--1000") + << QString::fromLatin1("mailtst33") + << QString::fromLatin1("testme33") + << m_imapServer + << 143 + << list + ; + list.clear(); + for (int i = 0; i < 100; ++i) + list << TestMail(); + QTest::newRow("small_messages--100") + << QString::fromLatin1("mailtst30") + << QString::fromLatin1("testme30") + << m_imapServer + << 143 + << list + ; + + QTest::newRow("big_messages") + << QString::fromLatin1("mailtst37") + << QString::fromLatin1("testme37") + << m_imapServer + << 143 + << (TestMailList() + << (TestMail() + << QByteArray("Subject: 4MB+ test file") + << QByteArray("o+ZmfhB18O/FYfHMEspiVR3/nRPYBXfnCyLURiIRyM0Lx7bXk9MtpRPEnL01xiAkeBobLd/e2ZKb") + << QByteArray("YMSd7zfs2xNPsCNwj76/73/Vx9SSJ//1RIyxewgR//4u5IpwoSEWMp5+") + ) + << (TestMail() + << QByteArray("Subject: 20,000 Leagues Under the Sea") + << QByteArray("I went to the central staircase which opened on to the platform,") + << QByteArray("from Ceylon to Sydney, touching at King George's Point and Melbourne.") + << QByteArray("274 2 occured occurred") + ) + << (TestMail() + << QByteArray("Subject: Fwd:3: Message with multiple attachments") + << QByteArray("SUsTXEe2aBGuTYOxhAH0og014xzV2R147zXEdIH1robUCKcaYTgmZ96lMWSGXN6sq+KmpMKIAx8V") + << QByteArray("Content-Type: image/png; name=snapshot2.png") + << QByteArray("RsySH338IAAGE+jk306+9ONT2nhj5rNnp44+f/S1s2888fijVl/5zE/PVO+pnvq7U0f+6ggrHH56") + << QByteArray("Content-Type: image/jpeg; name=snapshot11.jpg") + << QByteArray("CUAAAAAAPWYXQrHZ0+CZDhdCsdnT4JlGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + ) + ) + ; + + list.clear(); + for (int i = 0; i < 500; ++i) + list << TestMail(); + QTest::newRow("small_messages--500") + << QString::fromLatin1("mailtst32") + << QString::fromLatin1("testme32") + << m_imapServer + << 143 + << list + ; + + list.clear(); + for (int i = 0; i < 5000; ++i) + list << TestMail(); + QTest::newRow("small_messages--5000") + << QString::fromLatin1("mailtst35") + << QString::fromLatin1("testme35") + << m_imapServer + << 143 + << list + ; + + list.clear(); + for (int i = 0; i < 2000; ++i) + list << TestMail(); + QTest::newRow("small_messages--2000") + << QString::fromLatin1("mailtst34") + << QString::fromLatin1("testme34") + << m_imapServer + << 143 + << list + ; + + list.clear(); + for (int i = 0; i < 10000; ++i) + list << TestMail(); + QTest::newRow("small_messages--10000") + << QString::fromLatin1("mailtst36") + << QString::fromLatin1("testme36") + << m_imapServer + << 143 + << list + ; +} + +void tst_MessageServer::removeMessages() +{ runInChildProcess(&tst_MessageServer::removeMessages_impl); } + +void tst_MessageServer::removeMessages_impl() +{ + static const char service[] = "imap4"; + + QFETCH(QString, user); + QFETCH(QString, password); + QFETCH(QString, server); + QFETCH(int, port); + QFETCH(TestMailList,mails); + + QMailMessageIdList fetched; + /* Valgrind slows things down quite a lot. */ + static const int MAXTIME = + RUNNING_ON_VALGRIND ? qMax(60000, 2000*mails.count()) : + 60000; + + new MessageServer; + QMailStore* ms = QMailStore::instance(); + + QMailAccount account; + addAccount(&account, service, user, password, server, port); + if (QTest::currentTestFailed()) return; + + /* Get message count for this account */ + QMailRetrievalAction retrieve; + + retrieve.synchronize(account.id()); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + + /* Ensure we have all the messages we expect */ + QCOMPARE(ms->countMessages(), mails.count()); + + /* OK, now download the entire message bodies. */ + fetched = ms->queryMessages(); + QCOMPARE(fetched.count(), mails.count()); + retrieve.retrieveMessages(fetched, QMailRetrievalAction::Content); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + + compareMessages(fetched, mails); + if (QTest::currentTestFailed()) return; + + { + BenchmarkContext ctx(m_xml); + QVERIFY(ms->removeMessages(QMailMessageKey(), QMailStore::NoRemovalRecord)); + } + + QCOMPARE(ms->queryMessages().count(), 0); +} + +void tst_MessageServer::removeMessages_data() +{ completeRetrievalImap_data(); } + +void tst_MessageServer::replaceMessages() +{ runInChildProcess(&tst_MessageServer::replaceMessages_impl); } + +/* + Tests that downloading, deleting and redownloading the same mails does + not leak filesystem resources. + This test ensures the sqlite database is correctly reusing the space + freed when removing mails. +*/ +void tst_MessageServer::replaceMessages_impl() +{ + static const char service[] = "imap4"; + + QFETCH(QString, user); + QFETCH(QString, password); + QFETCH(QString, server); + QFETCH(int, port); + QFETCH(TestMailList,mails); + + QMailMessageIdList fetched; + /* Valgrind slows things down quite a lot. */ + static const int MAXTIME = + RUNNING_ON_VALGRIND ? qMax(60000, 2000*mails.count()) : + 60000; + + new MessageServer; + QMailStore* ms = QMailStore::instance(); + + QMailAccount account; + addAccount(&account, service, user, password, server, port); + if (QTest::currentTestFailed()) return; + + /* Get message count for this account */ + QMailRetrievalAction retrieve; + + retrieve.synchronize(account.id()); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + + /* Ensure we have all the messages we expect */ + QCOMPARE(ms->countMessages(), mails.count()); + + /* OK, now download the entire message bodies. */ + fetched = ms->queryMessages(); + QCOMPARE(fetched.count(), mails.count()); + retrieve.retrieveMessages(fetched, QMailRetrievalAction::Content); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + + compareMessages(fetched, mails); + if (QTest::currentTestFailed()) return; + + { + /* Remove the messages. */ + BenchmarkContext ctx(m_xml); + QVERIFY(ms->removeMessages(QMailMessageKey(), QMailStore::NoRemovalRecord)); + QCOMPARE(ms->queryMessages().count(), 0); + + /* Redownload the same messages. */ + retrieve.synchronize(account.id()); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + + /* Ensure we have all the messages we expect */ + QCOMPARE(ms->countMessages(), mails.count()); + + /* OK, now download the entire message bodies. */ + fetched = ms->queryMessages(); + QCOMPARE(fetched.count(), mails.count()); + retrieve.retrieveMessages(fetched, QMailRetrievalAction::Content); + waitForActivity(&retrieve, QMailServiceAction::Successful, MAXTIME); + if (QTest::currentTestFailed()) return; + } + + compareMessages(fetched, mails); +} + +void tst_MessageServer::replaceMessages_data() +{ completeRetrievalImap_data(); } + + +void tst_MessageServer::onActivityChanged(QMailServiceAction::Activity a) +{ + if (!m_loop) return; + + /* + Exit the inner loop with success if we got the expected state, failure + if we got a failure state. + Set m_loop = 0 so it can be determined we didn't time out. + */ + if (a == m_expectedState) { + m_loop->exit(0); + m_loop = 0; + } + else if (a == QMailServiceAction::Failed) { + m_loop->exit(1); + m_loop = 0; + } +} + +void tst_MessageServer::onProgressChanged(uint value,uint total) +{ + /* + Running in valgrind takes a long time... output some progress so it's + clear we haven't frozen. + */ + static int i = 0; + bool output = RUNNING_ON_VALGRIND || verbose; + if (output && !(i++ % 25)) { + qWarning() << "Progress:" << value << "/" << total; + } + + /* We are making some progress, so reset the timeout timer. */ + if (m_timer) { + m_timer->start(); + } +} + +int main(int argc, char** argv) +{ + /* + Ensure that this test doesn't run as the QWS server. + This is because we intend to fork() later on and we don't want to + have to process events for the sake of the child process. + */ + QApplication app(argc, argv); + + QString home = QDir::homePath(); + /* Sanity check - make sure we're not using the real home directory */ + QRegExp homeRegex("^/home/[^/]+/?$"); + if (-1 != homeRegex.indexIn(home)) { + qFatal( "It looks like you are using your real home directory for this test - don't!\n" + "Set the HOME environment variable to a temporary directory or run this " + "test with `qbuild test'."); + } + + int iters = 1; + bool verbose = false; + for (int i = 0; i < argc; ++i) { + if (i < argc-1 && !strcmp(argv[i], "-iterations")) { + bool ok; + int n = QString::fromLatin1(argv[i+1]).toInt(&ok); + if (ok && n > 0) iters = n; + } + else if (!strncmp(argv[i], "-v", 2)) { + verbose = true; + } + } + + int ret = 0; + for (int i = 0; i < iters; ++i) { + tst_MessageServer test; + test.verbose = verbose; + ret += QTest::qExec(&test, argc, argv); + } + return ret; +} + +#include "tst_messageserver.moc" + diff --git a/benchmarks/tst_messageserver/tst_messageserver.pro b/benchmarks/tst_messageserver/tst_messageserver.pro new file mode 100644 index 00000000..6be57ebe --- /dev/null +++ b/benchmarks/tst_messageserver/tst_messageserver.pro @@ -0,0 +1,49 @@ +CONFIG += qtestlib unittest +TEMPLATE = app +TARGET = tst_messageserver +target.path += $$QMF_INSTALL_ROOT/tests +INSTALLS += target +DEPENDPATH += . 3rdparty + +BASE=$$PWD/../.. + +IMAP_PLUGIN=$$BASE/src/plugins/messageservices/imap/ +MESSAGE_SERVER=$$BASE/src/tools/messageserver + +INCLUDEPATH += . 3rdparty $$BASE/src/libraries/qtopiamail \ + $$BASE/src/libraries/qtopiamail/support \ + $$BASE/src/libraries/messageserver \ + $$IMAP_PLUGIN \ + $$MESSAGE_SERVER + +LIBS += -L$$BASE/src/libraries/messageserver -lmessageserver \ + -L$$BASE/src/libraries/qtopiamail -lqtopiamail + +QMAKE_LFLAGS += -Wl,-rpath,$$BASE/src/libraries/qtopiamail \ + -Wl,-rpath,$$BASE/src/libraries/messageserver + +HEADERS += benchmarkcontext.h \ + qscopedconnection.h \ + testfsusage.h \ + testmalloc.h \ + 3rdparty/cycle_p.h \ + $$IMAP_PLUGIN/imapconfiguration.h \ + $$MESSAGE_SERVER/mailmessageclient.h \ + $$MESSAGE_SERVER/messageserver.h \ + $$MESSAGE_SERVER/servicehandler.h \ + $$MESSAGE_SERVER/newcountnotifier.h + +SOURCES += benchmarkcontext.cpp \ + qscopedconnection.cpp \ + testfsusage.cpp \ + testmalloc.cpp \ + tst_messageserver.cpp \ + $$IMAP_PLUGIN/imapconfiguration.cpp \ + $$MESSAGE_SERVER/mailmessageclient.cpp \ + $$MESSAGE_SERVER/messageserver.cpp \ + $$MESSAGE_SERVER/prepareaccounts.cpp \ + $$MESSAGE_SERVER/servicehandler.cpp \ + $$MESSAGE_SERVER/newcountnotifier.cpp + + + |