diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
commit | f15b8a83e2e51955776a3f07cb85ebfc342dd8ef (patch) | |
tree | c5dc684986051654898db11ce73e03b9fec8db99 /src/testlib | |
download | Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.zip Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.gz Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.bz2 |
Initial import of statemachine branch from the old kinetic repository
Diffstat (limited to 'src/testlib')
44 files changed, 13329 insertions, 0 deletions
diff --git a/src/testlib/3rdparty/callgrind_p.h b/src/testlib/3rdparty/callgrind_p.h new file mode 100644 index 0000000..10d7931 --- /dev/null +++ b/src/testlib/3rdparty/callgrind_p.h @@ -0,0 +1,147 @@ + +/* + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (callgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of callgrind, a valgrind tool for cache simulation + and call tree tracing. + + Copyright (C) 2003-2007 Josef Weidendorfer. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (vgprof.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + +#ifndef __CALLGRIND_H +#define __CALLGRIND_H + +#include "valgrind_p.h" + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. + + The identification ('C','T') for Callgrind has historical + reasons: it was called "Calltree" before. Besides, ('C','G') would + clash with cachegrind. + */ + +typedef + enum { + VG_USERREQ__DUMP_STATS = VG_USERREQ_TOOL_BASE('C','T'), + VG_USERREQ__ZERO_STATS, + VG_USERREQ__TOGGLE_COLLECT, + VG_USERREQ__DUMP_STATS_AT, + VG_USERREQ__START_INSTRUMENTATION, + VG_USERREQ__STOP_INSTRUMENTATION + } Vg_CallgrindClientRequest; + +/* Dump current state of cost centers, and zero them afterwards */ +#define CALLGRIND_DUMP_STATS \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DUMP_STATS, \ + 0, 0, 0, 0, 0); \ + } + +/* Dump current state of cost centers, and zero them afterwards. + The argument is appended to a string stating the reason which triggered + the dump. This string is written as a description field into the + profile data dump. */ +#define CALLGRIND_DUMP_STATS_AT(pos_str) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DUMP_STATS_AT, \ + pos_str, 0, 0, 0, 0); \ + } + +/* Zero cost centers */ +#define CALLGRIND_ZERO_STATS \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__ZERO_STATS, \ + 0, 0, 0, 0, 0); \ + } + +/* Toggles collection state. + The collection state specifies whether the happening of events + should be noted or if they are to be ignored. Events are noted + by increment of counters in a cost center */ +#define CALLGRIND_TOGGLE_COLLECT \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__TOGGLE_COLLECT, \ + 0, 0, 0, 0, 0); \ + } + +/* Start full callgrind instrumentation if not already switched on. + When cache simulation is done, it will flush the simulated cache; + this will lead to an artifical cache warmup phase afterwards with + cache misses which would not have happened in reality. */ +#define CALLGRIND_START_INSTRUMENTATION \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__START_INSTRUMENTATION, \ + 0, 0, 0, 0, 0); \ + } + +/* Stop full callgrind instrumentation if not already switched off. + This flushes Valgrinds translation cache, and does no additional + instrumentation afterwards, which effectivly will run at the same + speed as the "none" tool (ie. at minimal slowdown). + Use this to bypass Callgrind aggregation for uninteresting code parts. + To start Callgrind in this mode to ignore the setup phase, use + the option "--instr-atstart=no". */ +#define CALLGRIND_STOP_INSTRUMENTATION \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STOP_INSTRUMENTATION, \ + 0, 0, 0, 0, 0); \ + } + +#endif /* __CALLGRIND_H */ diff --git a/src/testlib/3rdparty/cycle_p.h b/src/testlib/3rdparty/cycle_p.h new file mode 100644 index 0000000..b4b6876 --- /dev/null +++ b/src/testlib/3rdparty/cycle_p.h @@ -0,0 +1,494 @@ +/* + * 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(_WIN32_WCE) && defined(_X86_))) && !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 + +#if _MSC_VER >= 1400 && defined(_WIN32_WCE) && !defined(HAVE_TICK_COUNTER) +#include <windows.h> +typedef DWORD CycleCounterTicks; + +static __inline CycleCounterTicks getticks(void) +{ + return GetTickCount(); +} + +static __inline double elapsed(CycleCounterTicks t1, CycleCounterTicks t0) +{ + return (double)(t1 - t0); +} + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 +#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; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER + +# elif 0 // Doesn't compile +# include <machine/inline.h> +static inline unsigned long getticks(void) +{ + register CycleCounterTicks ret; + _MFCTL(16, ret); + return ret; +} +# endif + +#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/src/testlib/3rdparty/valgrind_p.h b/src/testlib/3rdparty/valgrind_p.h new file mode 100644 index 0000000..6380a9f --- /dev/null +++ b/src/testlib/3rdparty/valgrind_p.h @@ -0,0 +1,3926 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2007 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + +#include <stdarg.h> + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). */ +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_ppc32_aix5 +#undef PLAT_ppc64_aix5 + +#if !defined(_AIX) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif !defined(_AIX) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif !defined(_AIX) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif !defined(_AIX) && defined(__powerpc__) && defined(__powerpc64__) +# define PLAT_ppc64_linux 1 +#elif defined(_AIX) && defined(__64BIT__) +# define PLAT_ppc64_aix5 1 +#elif defined(_AIX) && !defined(__64BIT__) +# define PLAT_ppc32_aix5 1 +#endif + + +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +#if !defined(PLAT_x86_linux) && !defined(PLAT_amd64_linux) \ + && !defined(PLAT_ppc32_linux) && !defined(PLAT_ppc64_linux) \ + && !defined(PLAT_ppc32_aix5) && !defined(PLAT_ppc64_aix5) +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { \ + (_zzq_rlval) = (_zzq_default); \ + } + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-linux ------------------------- */ + +#if defined(PLAT_x86_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" +#endif /* PLAT_x86_linux */ + +/* ------------------------ amd64-linux ------------------------ */ + +#if defined(PLAT_amd64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" +#endif /* PLAT_amd64_linux */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[6]; \ + register unsigned long long int _zzq_result __asm__("r3"); \ + register unsigned long long int* _zzq_ptr __asm__("r4"); \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1" \ + : "=r" (_zzq_result) \ + : "0" (_zzq_default), "r" (_zzq_ptr) \ + : "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr __asm__("r3"); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + unsigned int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[7]; \ + register unsigned int _zzq_result; \ + register unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "lwz 3, 24(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[7]; \ + register unsigned long long int _zzq_result; \ + register unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int long long)(_zzq_request); \ + _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int long long)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "ld 3, 48(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_aix5 */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */ + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + _vgwZU_##soname##_##fnname + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + _vgwZZ_##soname##_##fnname + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +/* ------------------------- x86-linux ------------------------- */ + +#if defined(PLAT_x86_linux) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $4, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $8, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $12, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $20, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $24, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $28, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $36, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $40, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $44, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux */ + +/* ------------------------ amd64-linux ------------------------ */ + +#if defined(PLAT_amd64_linux) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CALL_NOREDIR_RAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $8, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $16, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $24, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $32, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $40, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $48, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "lwz 3," #_n_fr "(1)\n\t" \ + "stw 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,68(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "ld 3," #_n_fr "(1)\n\t" \ + "std 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_aix5 */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0); \ + _qzz_res; \ + }) + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + } + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. */ + +#if defined(NVALGRIND) + +# define VALGRIND_PRINTF(...) +# define VALGRIND_PRINTF_BACKTRACE(...) + +#else /* NVALGRIND */ + +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +static int +VALGRIND_PRINTF(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF, + (unsigned long)format, (unsigned long)vargs, + 0, 0, 0); + va_end(vargs); + return (int)_qzz_res; +} + +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +static int +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE, + (unsigned long)format, (unsigned long)vargs, + 0, 0, 0); + va_end(vargs); + return (int)_qzz_res; +} + +#endif /* NVALGRIND */ + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0); \ + _qyy_res; \ + }) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + __extension__ \ + ({unsigned int _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0); \ + _qyy_res; \ + }) + +/* Mark a block of memory as having been allocated by a malloc()-like + function. `addr' is the start of the usable block (ie. after any + redzone) `rzB' is redzone size if the allocator can apply redzones; + use '0' if not. Adding redzones makes it more likely Valgrind will spot + block overruns. `is_zeroed' indicates if the memory is zeroed, as it is + for calloc(). Put it immediately after the point where a block is + allocated. + + If you're using Memcheck: If you're allocating memory via superblocks, + and then handing out small chunks of each superblock, if you don't have + redzones on your small blocks, it's worth marking the superblock with + VALGRIND_MAKE_MEM_NOACCESS when it's created, so that block overruns are + detected. But if you can put redzones on, it's probably better to not do + this, so that messages for small overruns are described in terms of the + small block rather than the superblock (but if you have a big overrun + that skips over a redzone, you could miss an error this way). See + memcheck/tests/custom_alloc.c for an example. + + WARNING: if your allocator uses malloc() or 'new' to allocate + superblocks, rather than mmap() or brk(), this will not work properly -- + you'll likely get assertion failures during leak detection. This is + because Valgrind doesn't like seeing overlapping heap blocks. Sorry. + + Nb: block must be freed via a free()-like function specified + with VALGRIND_FREELIKE_BLOCK or mismatch errors will occur. */ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0); \ + } + +/* Mark a block of memory as having been freed by a free()-like function. + `rzB' is redzone size; it must match that given to + VALGRIND_MALLOCLIKE_BLOCK. Memory not freed will be detected by the leak + checker. Put it immediately after the point where the block is freed. */ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0); \ + } + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0); \ + } + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0); \ + } + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0); \ + } + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0); \ + } + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0); \ + } + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Mark a piece of memory as being a stack. Returns a stack id. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0); \ + } + +/* Change the start and end address of the stack id. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0); \ + } + + +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_ppc32_aix5 +#undef PLAT_ppc64_aix5 + +#endif /* __VALGRIND_H */ + + diff --git a/src/testlib/qabstracttestlogger.cpp b/src/testlib/qabstracttestlogger.cpp new file mode 100644 index 0000000..e5d5d59 --- /dev/null +++ b/src/testlib/qabstracttestlogger.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qabstracttestlogger_p.h" +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/qtestassert.h" + +#include <stdio.h> +#include <stdlib.h> + +#ifndef Q_OS_WIN +#include <unistd.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace QTest +{ + static FILE *stream = 0; +} + +void QAbstractTestLogger::outputString(const char *msg) +{ + QTEST_ASSERT(QTest::stream); + + ::fputs(msg, QTest::stream); + ::fflush(QTest::stream); +} + +bool QAbstractTestLogger::isTtyOutput() +{ + QTEST_ASSERT(QTest::stream); + +#if defined(Q_OS_WIN) || defined(Q_OS_INTEGRITY) + return true; +#else + static bool ttyoutput = isatty(fileno(QTest::stream)); + return ttyoutput; +#endif +} + +void QAbstractTestLogger::startLogging() +{ + QTEST_ASSERT(!QTest::stream); + + const char *out = QTestLog::outputFileName(); + if (!out) { + QTest::stream = stdout; + return; + } +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE) + if (::fopen_s(&QTest::stream, out, "wt")) { +#else + QTest::stream = ::fopen(out, "wt"); + if (!QTest::stream) { +#endif + printf("Unable to open file for logging: %s", out); + ::exit(1); + } +} + +void QAbstractTestLogger::stopLogging() +{ + QTEST_ASSERT(QTest::stream); + if (QTest::stream != stdout) + fclose(QTest::stream); + QTest::stream = 0; +} + +QT_END_NAMESPACE diff --git a/src/testlib/qabstracttestlogger_p.h b/src/testlib/qabstracttestlogger_p.h new file mode 100644 index 0000000..298fbad --- /dev/null +++ b/src/testlib/qabstracttestlogger_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTTESTLOGGER_P_H +#define QABSTRACTTESTLOGGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +class QBenchmarkResult; + +class QAbstractTestLogger +{ +public: + enum IncidentTypes { + Pass, + XFail, + Fail, + XPass + }; + + enum MessageTypes { + Warn, + QWarning, + QDebug, + QSystem, + QFatal, + Skip, + Info + }; + + QAbstractTestLogger() {} + virtual ~QAbstractTestLogger() {} + + virtual void startLogging(); + virtual void stopLogging(); + + virtual void enterTestFunction(const char *function) = 0; + virtual void leaveTestFunction() = 0; + + virtual void addIncident(IncidentTypes type, const char *description, + const char *file = 0, int line = 0) = 0; + virtual void addBenchmarkResult(const QBenchmarkResult &result) = 0; + + virtual void addMessage(MessageTypes type, const char *message, + const char *file = 0, int line = 0) = 0; + + static void outputString(const char *msg); + static bool isTtyOutput(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qasciikey.cpp b/src/testlib/qasciikey.cpp new file mode 100644 index 0000000..c02a8ad --- /dev/null +++ b/src/testlib/qasciikey.cpp @@ -0,0 +1,505 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/qtestcase.h" +#include "QtTest/qtestassert.h" + +QT_BEGIN_NAMESPACE + +/*! \internal + Convert an ascii char key value to a Qt Key value. + If the key is unknown a 0 is returned. + + Note: this may happen more than you like since not all known + ascii keys _are_ converted already. So feel free to add all the keys you need. + */ +Qt::Key QTest::asciiToKey(char ascii) +{ + switch ((unsigned char)ascii) { + case 0x08: return Qt::Key_Backspace; + case 0x09: return Qt::Key_Tab; + case 0x0b: return Qt::Key_Backtab; + case 0x0d: return Qt::Key_Return; + case 0x1b: return Qt::Key_Escape; + case 0x20: return Qt::Key_Space; + case 0x21: return Qt::Key_Exclam; + case 0x22: return Qt::Key_QuoteDbl; + case 0x23: return Qt::Key_NumberSign; + case 0x24: return Qt::Key_Dollar; + case 0x25: return Qt::Key_Percent; + case 0x26: return Qt::Key_Ampersand; + case 0x27: return Qt::Key_Apostrophe; + case 0x28: return Qt::Key_ParenLeft; + case 0x29: return Qt::Key_ParenRight; + case 0x2a: return Qt::Key_Asterisk; + case 0x2b: return Qt::Key_Plus; + case 0x2c: return Qt::Key_Comma; + case 0x2d: return Qt::Key_Minus; + case 0x2e: return Qt::Key_Period; + case 0x2f: return Qt::Key_Slash; + case 0x30: return Qt::Key_0; + case 0x31: return Qt::Key_1; + case 0x32: return Qt::Key_2; + case 0x33: return Qt::Key_3; + case 0x34: return Qt::Key_4; + case 0x35: return Qt::Key_5; + case 0x36: return Qt::Key_6; + case 0x37: return Qt::Key_7; + case 0x38: return Qt::Key_8; + case 0x39: return Qt::Key_9; + case 0x3a: return Qt::Key_Colon; + case 0x3b: return Qt::Key_Semicolon; + case 0x3c: return Qt::Key_Less; + case 0x3d: return Qt::Key_Equal; + case 0x3e: return Qt::Key_Greater; + case 0x3f: return Qt::Key_Question; + case 0x40: return Qt::Key_At; + case 0x41: return Qt::Key_A; + case 0x42: return Qt::Key_B; + case 0x43: return Qt::Key_C; + case 0x44: return Qt::Key_D; + case 0x45: return Qt::Key_E; + case 0x46: return Qt::Key_F; + case 0x47: return Qt::Key_G; + case 0x48: return Qt::Key_H; + case 0x49: return Qt::Key_I; + case 0x4a: return Qt::Key_J; + case 0x4b: return Qt::Key_K; + case 0x4c: return Qt::Key_L; + case 0x4d: return Qt::Key_M; + case 0x4e: return Qt::Key_N; + case 0x4f: return Qt::Key_O; + case 0x50: return Qt::Key_P; + case 0x51: return Qt::Key_Q; + case 0x52: return Qt::Key_R; + case 0x53: return Qt::Key_S; + case 0x54: return Qt::Key_T; + case 0x55: return Qt::Key_U; + case 0x56: return Qt::Key_V; + case 0x57: return Qt::Key_W; + case 0x58: return Qt::Key_X; + case 0x59: return Qt::Key_Y; + case 0x5a: return Qt::Key_Z; + case 0x5b: return Qt::Key_BracketLeft; + case 0x5c: return Qt::Key_Backslash; + case 0x5d: return Qt::Key_BracketRight; + case 0x5e: return Qt::Key_AsciiCircum; + case 0x5f: return Qt::Key_Underscore; + case 0x60: return Qt::Key_QuoteLeft; + case 0x61: return Qt::Key_A; + case 0x62: return Qt::Key_B; + case 0x63: return Qt::Key_C; + case 0x64: return Qt::Key_D; + case 0x65: return Qt::Key_E; + case 0x66: return Qt::Key_F; + case 0x67: return Qt::Key_G; + case 0x68: return Qt::Key_H; + case 0x69: return Qt::Key_I; + case 0x6a: return Qt::Key_J; + case 0x6b: return Qt::Key_K; + case 0x6c: return Qt::Key_L; + case 0x6d: return Qt::Key_M; + case 0x6e: return Qt::Key_N; + case 0x6f: return Qt::Key_O; + case 0x70: return Qt::Key_P; + case 0x71: return Qt::Key_Q; + case 0x72: return Qt::Key_R; + case 0x73: return Qt::Key_S; + case 0x74: return Qt::Key_T; + case 0x75: return Qt::Key_U; + case 0x76: return Qt::Key_V; + case 0x77: return Qt::Key_W; + case 0x78: return Qt::Key_X; + case 0x79: return Qt::Key_Y; + case 0x7a: return Qt::Key_Z; + case 0x7b: return Qt::Key_BraceLeft; + case 0x7c: return Qt::Key_Bar; + case 0x7d: return Qt::Key_BraceRight; + case 0x7e: return Qt::Key_AsciiTilde; + + // Latin 1 codes adapted from X: keysymdef.h,v 1.21 94/08/28 16:17:06 + case 0xa0: return Qt::Key_nobreakspace; + case 0xa1: return Qt::Key_exclamdown; + case 0xa2: return Qt::Key_cent; + case 0xa3: return Qt::Key_sterling; + case 0xa4: return Qt::Key_currency; + case 0xa5: return Qt::Key_yen; + case 0xa6: return Qt::Key_brokenbar; + case 0xa7: return Qt::Key_section; + case 0xa8: return Qt::Key_diaeresis; + case 0xa9: return Qt::Key_copyright; + case 0xaa: return Qt::Key_ordfeminine; + case 0xab: return Qt::Key_guillemotleft; + case 0xac: return Qt::Key_notsign; + case 0xad: return Qt::Key_hyphen; + case 0xae: return Qt::Key_registered; + case 0xaf: return Qt::Key_macron; + case 0xb0: return Qt::Key_degree; + case 0xb1: return Qt::Key_plusminus; + case 0xb2: return Qt::Key_twosuperior; + case 0xb3: return Qt::Key_threesuperior; + case 0xb4: return Qt::Key_acute; + case 0xb5: return Qt::Key_mu; + case 0xb6: return Qt::Key_paragraph; + case 0xb7: return Qt::Key_periodcentered; + case 0xb8: return Qt::Key_cedilla; + case 0xb9: return Qt::Key_onesuperior; + case 0xba: return Qt::Key_masculine; + case 0xbb: return Qt::Key_guillemotright; + case 0xbc: return Qt::Key_onequarter; + case 0xbd: return Qt::Key_onehalf; + case 0xbe: return Qt::Key_threequarters; + case 0xbf: return Qt::Key_questiondown; + case 0xc0: return Qt::Key_Agrave; + case 0xc1: return Qt::Key_Aacute; + case 0xc2: return Qt::Key_Acircumflex; + case 0xc3: return Qt::Key_Atilde; + case 0xc4: return Qt::Key_Adiaeresis; + case 0xc5: return Qt::Key_Aring; + case 0xc6: return Qt::Key_AE; + case 0xc7: return Qt::Key_Ccedilla; + case 0xc8: return Qt::Key_Egrave; + case 0xc9: return Qt::Key_Eacute; + case 0xca: return Qt::Key_Ecircumflex; + case 0xcb: return Qt::Key_Ediaeresis; + case 0xcc: return Qt::Key_Igrave; + case 0xcd: return Qt::Key_Iacute; + case 0xce: return Qt::Key_Icircumflex; + case 0xcf: return Qt::Key_Idiaeresis; + case 0xd0: return Qt::Key_ETH; + case 0xd1: return Qt::Key_Ntilde; + case 0xd2: return Qt::Key_Ograve; + case 0xd3: return Qt::Key_Oacute; + case 0xd4: return Qt::Key_Ocircumflex; + case 0xd5: return Qt::Key_Otilde; + case 0xd6: return Qt::Key_Odiaeresis; + case 0xd7: return Qt::Key_multiply; + case 0xd8: return Qt::Key_Ooblique; + case 0xd9: return Qt::Key_Ugrave; + case 0xda: return Qt::Key_Uacute; + case 0xdb: return Qt::Key_Ucircumflex; + case 0xdc: return Qt::Key_Udiaeresis; + case 0xdd: return Qt::Key_Yacute; + case 0xde: return Qt::Key_THORN; + case 0xdf: return Qt::Key_ssharp; + case 0xe5: return Qt::Key_Aring; + case 0xe6: return Qt::Key_AE; + case 0xf7: return Qt::Key_division; + case 0xf8: return Qt::Key_Ooblique; + case 0xff: return Qt::Key_ydiaeresis; + default: QTEST_ASSERT(false); return Qt::Key(0); + } +} + +/*! \internal + Convert a Qt Key to an ascii char value. + If the Qt key is unknown a 0 is returned. + + Note: this may happen more than you like since not all known + Qt keys _are_ converted already. So feel free to add all the keys you need. +*/ +char QTest::keyToAscii(Qt::Key key) +{ + switch (key) { + case Qt::Key_Backspace: return 0x8; //BS + case Qt::Key_Tab: return 0x09; // HT + case Qt::Key_Backtab: return 0x0b; // VT + case Qt::Key_Enter: + case Qt::Key_Return: return 0x0d; // CR + case Qt::Key_Escape: return 0x1b; // ESC + case Qt::Key_Space: return 0x20; // 7 bit printable ASCII + case Qt::Key_Exclam: return 0x21; + case Qt::Key_QuoteDbl: return 0x22; + case Qt::Key_NumberSign: return 0x23; + case Qt::Key_Dollar: return 0x24; + case Qt::Key_Percent: return 0x25; + case Qt::Key_Ampersand: return 0x26; + case Qt::Key_Apostrophe: return 0x27; + case Qt::Key_ParenLeft: return 0x28; + case Qt::Key_ParenRight: return 0x29; + case Qt::Key_Asterisk: return 0x2a; + case Qt::Key_Plus: return 0x2b; + case Qt::Key_Comma: return 0x2c; + case Qt::Key_Minus: return 0x2d; + case Qt::Key_Period: return 0x2e; + case Qt::Key_Slash: return 0x2f; + case Qt::Key_0: return 0x30; + case Qt::Key_1: return 0x31; + case Qt::Key_2: return 0x32; + case Qt::Key_3: return 0x33; + case Qt::Key_4: return 0x34; + case Qt::Key_5: return 0x35; + case Qt::Key_6: return 0x36; + case Qt::Key_7: return 0x37; + case Qt::Key_8: return 0x38; + case Qt::Key_9: return 0x39; + case Qt::Key_Colon: return 0x3a; + case Qt::Key_Semicolon: return 0x3b; + case Qt::Key_Less: return 0x3c; + case Qt::Key_Equal: return 0x3d; + case Qt::Key_Greater: return 0x3e; + case Qt::Key_Question: return 0x3f; + case Qt::Key_At: return 0x40; + case Qt::Key_A: return 0x61; // 0x41 == 'A', 0x61 == 'a' + case Qt::Key_B: return 0x62; + case Qt::Key_C: return 0x63; + case Qt::Key_D: return 0x64; + case Qt::Key_E: return 0x65; + case Qt::Key_F: return 0x66; + case Qt::Key_G: return 0x67; + case Qt::Key_H: return 0x68; + case Qt::Key_I: return 0x69; + case Qt::Key_J: return 0x6a; + case Qt::Key_K: return 0x6b; + case Qt::Key_L: return 0x6c; + case Qt::Key_M: return 0x6d; + case Qt::Key_N: return 0x6e; + case Qt::Key_O: return 0x6f; + case Qt::Key_P: return 0x70; + case Qt::Key_Q: return 0x71; + case Qt::Key_R: return 0x72; + case Qt::Key_S: return 0x73; + case Qt::Key_T: return 0x74; + case Qt::Key_U: return 0x75; + case Qt::Key_V: return 0x76; + case Qt::Key_W: return 0x77; + case Qt::Key_X: return 0x78; + case Qt::Key_Y: return 0x79; + case Qt::Key_Z: return 0x7a; + case Qt::Key_BracketLeft: return 0x5b; + case Qt::Key_Backslash: return 0x5c; + case Qt::Key_BracketRight: return 0x5d; + case Qt::Key_AsciiCircum: return 0x5e; + case Qt::Key_Underscore: return 0x5f; + case Qt::Key_QuoteLeft: return 0x60; + + case Qt::Key_BraceLeft: return 0x7b; + case Qt::Key_Bar: return 0x7c; + case Qt::Key_BraceRight: return 0x7d; + case Qt::Key_AsciiTilde: return 0x7e; + + case Qt::Key_Delete: return 0; + case Qt::Key_Insert: return 0; // = 0x1006, + case Qt::Key_Pause: return 0; // = 0x1008, + case Qt::Key_Print: return 0; // = 0x1009, + case Qt::Key_SysReq: return 0; // = 0x100a, + + case Qt::Key_Clear: return 0; // = 0x100b, + + case Qt::Key_Home: return 0; // = 0x1010, // cursor movement + case Qt::Key_End: return 0; // = 0x1011, + case Qt::Key_Left: return 0; // = 0x1012, + case Qt::Key_Up: return 0; // = 0x1013, + case Qt::Key_Right: return 0; // = 0x1014, + case Qt::Key_Down: return 0; // = 0x1015, + case Qt::Key_PageUp: return 0; // = 0x1016, + case Qt::Key_PageDown: return 0; // = 0x1017, + case Qt::Key_Shift: return 0; // = 0x1020, // modifiers + case Qt::Key_Control: return 0; // = 0x1021, + case Qt::Key_Meta: return 0; // = 0x1022, + case Qt::Key_Alt: return 0; // = 0x1023, + case Qt::Key_CapsLock: return 0; // = 0x1024, + case Qt::Key_NumLock: return 0; // = 0x1025, + case Qt::Key_ScrollLock: return 0; // = 0x1026, + case Qt::Key_F1: return 0; // = 0x1030, // function keys + case Qt::Key_F2: return 0; // = 0x1031, + case Qt::Key_F3: return 0; // = 0x1032, + case Qt::Key_F4: return 0; // = 0x1033, + case Qt::Key_F5: return 0; // = 0x1034, + case Qt::Key_F6: return 0; // = 0x1035, + case Qt::Key_F7: return 0; // = 0x1036, + case Qt::Key_F8: return 0; // = 0x1037, + case Qt::Key_F9: return 0; // = 0x1038, + case Qt::Key_F10: return 0; // = 0x1039, + case Qt::Key_F11: return 0; // = 0x103a, + case Qt::Key_F12: return 0; // = 0x103b, + case Qt::Key_F13: return 0; // = 0x103c, + case Qt::Key_F14: return 0; // = 0x103d, + case Qt::Key_F15: return 0; // = 0x103e, + case Qt::Key_F16: return 0; // = 0x103f, + case Qt::Key_F17: return 0; // = 0x1040, + case Qt::Key_F18: return 0; // = 0x1041, + case Qt::Key_F19: return 0; // = 0x1042, + case Qt::Key_F20: return 0; // = 0x1043, + case Qt::Key_F21: return 0; // = 0x1044, + case Qt::Key_F22: return 0; // = 0x1045, + case Qt::Key_F23: return 0; // = 0x1046, + case Qt::Key_F24: return 0; // = 0x1047, + case Qt::Key_F25: return 0; // = 0x1048, // F25 .. F35 only on X11 + case Qt::Key_F26: return 0; // = 0x1049, + case Qt::Key_F27: return 0; // = 0x104a, + case Qt::Key_F28: return 0; // = 0x104b, + case Qt::Key_F29: return 0; // = 0x104c, + case Qt::Key_F30: return 0; // = 0x104d, + case Qt::Key_F31: return 0; // = 0x104e, + case Qt::Key_F32: return 0; // = 0x104f, + case Qt::Key_F33: return 0; // = 0x1050, + case Qt::Key_F34: return 0; // = 0x1051, + case Qt::Key_F35: return 0; // = 0x1052, + case Qt::Key_Super_L: return 0; // = 0x1053, // extra keys + case Qt::Key_Super_R: return 0; // = 0x1054, + case Qt::Key_Menu: return 0; // = 0x1055, + case Qt::Key_Hyper_L: return 0; // = 0x1056, + case Qt::Key_Hyper_R: return 0; // = 0x1057, + case Qt::Key_Help: return 0; // = 0x1058, + case Qt::Key_Direction_L: return 0; // = 0x1059, + case Qt::Key_Direction_R: return 0; // = 0x1060, + + // Latin 1 codes adapted from X: keysymdef.h,v 1.21 94/08/28 16:17:06 + case Qt::Key_nobreakspace: return char(0xa0); + case Qt::Key_exclamdown: return char(0xa1); + case Qt::Key_cent: return char(0xa2); + case Qt::Key_sterling: return char(0xa3); + case Qt::Key_currency: return char(0xa4); + case Qt::Key_yen: return char(0xa5); + case Qt::Key_brokenbar: return char(0xa6); + case Qt::Key_section: return char(0xa7); + case Qt::Key_diaeresis: return char(0xa8); + case Qt::Key_copyright: return char(0xa9); + case Qt::Key_ordfeminine: return char(0xaa); + case Qt::Key_guillemotleft: return char(0xab); // left angle quotation mar + case Qt::Key_notsign: return char(0xac); + case Qt::Key_hyphen: return char(0xad); + case Qt::Key_registered: return char(0xae); + case Qt::Key_macron: return char(0xaf); + case Qt::Key_degree: return char(0xb0); + case Qt::Key_plusminus: return char(0xb1); + case Qt::Key_twosuperior: return char(0xb2); + case Qt::Key_threesuperior: return char(0xb3); + case Qt::Key_acute: return char(0xb4); + case Qt::Key_mu: return char(0xb5); + case Qt::Key_paragraph: return char(0xb6); + case Qt::Key_periodcentered: return char(0xb7); + case Qt::Key_cedilla: return char(0xb8); + case Qt::Key_onesuperior: return char(0xb9); + case Qt::Key_masculine: return char(0xba); + case Qt::Key_guillemotright: return char(0xbb); // right angle quotation mar + case Qt::Key_onequarter: return char(0xbc); + case Qt::Key_onehalf: return char(0xbd); + case Qt::Key_threequarters: return char(0xbe); + case Qt::Key_questiondown: return char(0xbf); + case Qt::Key_Agrave: return char(0xc0); + case Qt::Key_Aacute: return char(0xc1); + case Qt::Key_Acircumflex: return char(0xc2); + case Qt::Key_Atilde: return char(0xc3); + case Qt::Key_Adiaeresis: return char(0xc4); + case Qt::Key_Aring: return char(0xe5); + case Qt::Key_AE: return char(0xe6); + case Qt::Key_Ccedilla: return char(0xc7); + case Qt::Key_Egrave: return char(0xc8); + case Qt::Key_Eacute: return char(0xc9); + case Qt::Key_Ecircumflex: return char(0xca); + case Qt::Key_Ediaeresis: return char(0xcb); + case Qt::Key_Igrave: return char(0xcc); + case Qt::Key_Iacute: return char(0xcd); + case Qt::Key_Icircumflex: return char(0xce); + case Qt::Key_Idiaeresis: return char(0xcf); + case Qt::Key_ETH: return char(0xd0); + case Qt::Key_Ntilde: return char(0xd1); + case Qt::Key_Ograve: return char(0xd2); + case Qt::Key_Oacute: return char(0xd3); + case Qt::Key_Ocircumflex: return char(0xd4); + case Qt::Key_Otilde: return char(0xd5); + case Qt::Key_Odiaeresis: return char(0xd6); + case Qt::Key_multiply: return char(0xd7); + case Qt::Key_Ooblique: return char(0xf8); + case Qt::Key_Ugrave: return char(0xd9); + case Qt::Key_Uacute: return char(0xda); + case Qt::Key_Ucircumflex: return char(0xdb); + case Qt::Key_Udiaeresis: return char(0xdc); + case Qt::Key_Yacute: return char(0xdd); + case Qt::Key_THORN: return char(0xde); + case Qt::Key_ssharp: return char(0xdf); + case Qt::Key_division: return char(0xf7); + case Qt::Key_ydiaeresis: return char(0xff); + + // multimedia/internet keys - ignored by default - see QKeyEvent c'tor + + case Qt::Key_Back : return 0; // = 0x1061, + case Qt::Key_Forward : return 0; // = 0x1062, + case Qt::Key_Stop : return 0; // = 0x1063, + case Qt::Key_Refresh : return 0; // = 0x1064, + + case Qt::Key_VolumeDown: return 0; // = 0x1070, + case Qt::Key_VolumeMute : return 0; // = 0x1071, + case Qt::Key_VolumeUp: return 0; // = 0x1072, + case Qt::Key_BassBoost: return 0; // = 0x1073, + case Qt::Key_BassUp: return 0; // = 0x1074, + case Qt::Key_BassDown: return 0; // = 0x1075, + case Qt::Key_TrebleUp: return 0; // = 0x1076, + case Qt::Key_TrebleDown: return 0; // = 0x1077, + + case Qt::Key_MediaPlay : return 0; // = 0x1080, + case Qt::Key_MediaStop : return 0; // = 0x1081, + case Qt::Key_MediaPrevious : return 0; // = 0x1082, + case Qt::Key_MediaNext : return 0; // = 0x1083, + case Qt::Key_MediaRecord: return 0; // = 0x1084, + + case Qt::Key_HomePage : return 0; // = 0x1090, + case Qt::Key_Favorites : return 0; // = 0x1091, + case Qt::Key_Search : return 0; // = 0x1092, + case Qt::Key_Standby: return 0; // = 0x1093, + case Qt::Key_OpenUrl: return 0; // = 0x1094, + + case Qt::Key_LaunchMail : return 0; // = 0x10a0, + case Qt::Key_LaunchMedia: return 0; // = 0x10a1, + case Qt::Key_Launch0 : return 0; // = 0x10a2, + case Qt::Key_Launch1 : return 0; // = 0x10a3, + case Qt::Key_Launch2 : return 0; // = 0x10a4, + case Qt::Key_Launch3 : return 0; // = 0x10a5, + case Qt::Key_Launch4 : return 0; // = 0x10a6, + case Qt::Key_Launch5 : return 0; // = 0x10a7, + case Qt::Key_Launch6 : return 0; // = 0x10a8, + case Qt::Key_Launch7 : return 0; // = 0x10a9, + case Qt::Key_Launch8 : return 0; // = 0x10aa, + case Qt::Key_Launch9 : return 0; // = 0x10ab, + case Qt::Key_LaunchA : return 0; // = 0x10ac, + case Qt::Key_LaunchB : return 0; // = 0x10ad, + case Qt::Key_LaunchC : return 0; // = 0x10ae, + case Qt::Key_LaunchD : return 0; // = 0x10af, + case Qt::Key_LaunchE : return 0; // = 0x10b0, + case Qt::Key_LaunchF : return 0; // = 0x10b1, + + default: QTEST_ASSERT(false); return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/testlib/qbenchmark.cpp b/src/testlib/qbenchmark.cpp new file mode 100644 index 0000000..7687fec --- /dev/null +++ b/src/testlib/qbenchmark.cpp @@ -0,0 +1,281 @@ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/qbenchmark.h" +#include "QtTest/private/qbenchmark_p.h" + +#ifdef QT_GUI_LIB +#include <QtGui/qapplication.h> +#endif + +#include <QtCore/qprocess.h> +#include <QtCore/qdir.h> +#include <QtCore/qset.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QBenchmarkGlobalData *QBenchmarkGlobalData::current; + +QBenchmarkGlobalData::QBenchmarkGlobalData() + : measurer(0) + , walltimeMinimum(-1) + , iterationCount(-1) + , medianIterationCount(-1) + , createChart(false) + , verboseOutput(false) + , mode_(WallTime) +{ + setMode(mode_); +} + +QBenchmarkGlobalData::~QBenchmarkGlobalData() +{ + delete measurer; + QBenchmarkGlobalData::current = 0; +} + +void QBenchmarkGlobalData::setMode(Mode mode) +{ + mode_ = mode; + + if (measurer) + delete measurer; + measurer = createMeasurer(); +} + +QBenchmarkMeasurerBase * QBenchmarkGlobalData::createMeasurer() +{ + QBenchmarkMeasurerBase *measurer = 0; + if (0) { +#ifdef QTESTLIB_USE_VALGRIND + } else if (mode_ == CallgrindChildProcess || mode_ == CallgrindParentProcess) { + measurer = new QBenchmarkCallgrindMeasurer; +#endif +#ifdef HAVE_TICK_COUNTER + } else if (mode_ == TickCounter) { + measurer = new QBenchmarkTickMeasurer; +#endif + } else if (mode_ == EventCounter) { + measurer = new QBenchmarkEvent; + } else { + measurer = new QBenchmarkTimeMeasurer; + } + measurer->init(); + return measurer; +} + +int QBenchmarkGlobalData::adjustMedianIterationCount() +{ + if (medianIterationCount != -1) { + return medianIterationCount; + } else { + return measurer->adjustMedianCount(1); + } +} + + +QBenchmarkTestMethodData *QBenchmarkTestMethodData::current; + +QBenchmarkTestMethodData::QBenchmarkTestMethodData() +:resultAccepted(false), iterationCount(-1) +{ + +} + +QBenchmarkTestMethodData::~QBenchmarkTestMethodData() +{ + QBenchmarkTestMethodData::current = 0; +} + +void QBenchmarkTestMethodData::beginDataRun() +{ + iterationCount = adjustIterationCount(1); +} + +void QBenchmarkTestMethodData::endDataRun() +{ + +} + +int QBenchmarkTestMethodData::adjustIterationCount(int suggestion) +{ + // Let the -iteration-count option override the measurer. + if (QBenchmarkGlobalData::current->iterationCount != -1) { + iterationCount = QBenchmarkGlobalData::current->iterationCount; + } else { + iterationCount = QBenchmarkGlobalData::current->measurer->adjustIterationCount(suggestion); + } + + return iterationCount; +} + +void QBenchmarkTestMethodData::setResult(qint64 value) +{ + bool accepted = false; + + // Always accept the result if the iteration count has been + // specified on the command line with -iteartion-count. + if (QBenchmarkGlobalData::current->iterationCount != -1) + accepted = true; + + // Test the result directly without calling the measurer if the minimum time + // has been specifed on the command line with -minimumvalue. + else if (QBenchmarkGlobalData::current->walltimeMinimum != -1) + accepted = (value > QBenchmarkGlobalData::current->walltimeMinimum); + else + accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(value); + + // Accept the result or double the number of iterations. + if (accepted) + resultAccepted = true; + else + iterationCount *= 2; + + this->result = + QBenchmarkResult(QBenchmarkGlobalData::current->context, value, iterationCount); +} + +/*! \internal + The QBenchmarkIterationController class is used by the QBENCHMARK macro to + drive the benchmarking loop. It is repsonsible for starting and stopping + the timing measurements as well as calling the result reporting functions. +*/ +QTest::QBenchmarkIterationController::QBenchmarkIterationController() +{ + QTest::beginBenchmarkMeasurement(); + i = 0; +} +/*! \internal +*/ +QTest::QBenchmarkIterationController::~QBenchmarkIterationController() +{ + QBenchmarkTestMethodData::current->setResult(QTest::endBenchmarkMeasurement()); +} + +/*! \internal +*/ +bool QTest::QBenchmarkIterationController::isDone() +{ + return i >= QTest::iterationCount(); +} + +/*! \internal +*/ +void QTest::QBenchmarkIterationController::next() +{ + ++i; +} + +/*! \internal +*/ +int QTest::iterationCount() +{ + return QBenchmarkTestMethodData::current->iterationCount; +} + +/*! \internal +*/ +void QTest::setIterationCountHint(int count) +{ + QBenchmarkTestMethodData::current->adjustIterationCount(count); +} +/*! \internal +*/ +void QTest::setIterationCount(int count) +{ + QBenchmarkTestMethodData::current->iterationCount = count; + QBenchmarkTestMethodData::current->resultAccepted = true; +} + +/*! \internal +*/ +void QTest::beginBenchmarkMeasurement() +{ + QBenchmarkGlobalData::current->measurer->start(); + // the clock is ticking after the line above, don't add code here. +} + +/*! \internal +*/ +qint64 QTest::endBenchmarkMeasurement() +{ + // the clock is ticking before the line below, don't add code here. + return QBenchmarkGlobalData::current->measurer->stop(); +} + +/*! \internal +*/ +void QTest::setResult(qint64 result) +{ + QBenchmarkTestMethodData::current->setResult(result); +} + +/*! \internal +*/ +void QTest::setResult(const QString &tag, qint64 result) +{ + QBenchmarkContext context = QBenchmarkGlobalData::current->context; + context.tag = tag; + QBenchmarkTestMethodData::current->result = + QBenchmarkResult( context, result, + QBenchmarkTestMethodData::current->iterationCount); +} + +template <typename T> +Q_TYPENAME T::value_type qAverage(const T &container) +{ + Q_TYPENAME T::const_iterator it = container.constBegin(); + Q_TYPENAME T::const_iterator end = container.constEnd(); + Q_TYPENAME T::value_type acc = Q_TYPENAME T::value_type(); + int count = 0; + while (it != end) { + acc += *it; + ++it; + ++count; + } + return acc / count; +} + + +QT_END_NAMESPACE + diff --git a/src/testlib/qbenchmark.h b/src/testlib/qbenchmark.h new file mode 100644 index 0000000..e2b0a74 --- /dev/null +++ b/src/testlib/qbenchmark.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARK_H +#define QBENCHMARK_H + +#include <QtTest/qtest_global.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + +// +// W A R N I N G +// ------------- +// +// The QBenchmarkIterationController class is not a part of the +// QTestlib API. It exists purely as an implementation detail. +// +// +class Q_TESTLIB_EXPORT QBenchmarkIterationController +{ +public: + QBenchmarkIterationController(); + ~QBenchmarkIterationController(); + bool isDone(); + void next(); + int i; +}; + +} + +#define QBENCHMARK \ + for (QTest::QBenchmarkIterationController __iteration_controller; __iteration_controller.isDone() == false; __iteration_controller.next()) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QBENCHMARK_H diff --git a/src/testlib/qbenchmark_p.h b/src/testlib/qbenchmark_p.h new file mode 100644 index 0000000..8bb6e84 --- /dev/null +++ b/src/testlib/qbenchmark_p.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARK_P_H +#define QBENCHMARK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#if defined(Q_OS_LINUX) && !defined(QT_NO_PROCESS) +#define QTESTLIB_USE_VALGRIND +#else +#undef QTESTLIB_USE_VALGRIND +#endif + +#include "QtTest/private/qbenchmarkmeasurement_p.h" +#include <QtCore/QMap> +#include <QtTest/qtest_global.h> +#ifdef QTESTLIB_USE_VALGRIND +#include "QtTest/private/qbenchmarkvalgrind_p.h" +#endif +#include "QtTest/private/qbenchmarkevent_p.h" + +QT_BEGIN_NAMESPACE + +struct QBenchmarkContext +{ + // None of the strings below are assumed to contain commas (see toString() below) + QString slotName; + QString tag; // from _data() function + + int checkpointIndex; + + QString toString() const + { + QString s = QString(QLatin1String("%1,%2,%3")).arg(slotName).arg(tag).arg(checkpointIndex); + return s; + } + + QBenchmarkContext() : checkpointIndex(-1) {} +}; + +class QBenchmarkResult +{ +public: + QBenchmarkContext context; + qint64 value; + int iterations; + bool valid; + + QBenchmarkResult() + : value(-1) + , iterations(-1) + , valid(false) + { } + + QBenchmarkResult(const QBenchmarkContext &context, const qint64 value, const int iterations) + : context(context) + , value(value) + , iterations(iterations) + , valid(true) + { + } + + bool operator<(const QBenchmarkResult &other) const + { + return (value / iterations) < (other.value / other.iterations); + } +}; + +/* + The QBenchmarkGlobalData class stores global benchmark-related data. + QBenchmarkGlobalData:current is created at the beginning of qExec() + and cleared at the end. +*/ +class QBenchmarkGlobalData +{ +public: + static QBenchmarkGlobalData *current; + + QBenchmarkGlobalData(); + ~QBenchmarkGlobalData(); + enum Mode { WallTime, CallgrindParentProcess, CallgrindChildProcess, TickCounter, EventCounter }; + void setMode(Mode mode); + Mode mode() const { return mode_; } + QBenchmarkMeasurerBase *createMeasurer(); + int adjustMedianIterationCount(); + + QBenchmarkMeasurerBase *measurer; + QBenchmarkContext context; + int walltimeMinimum; + int iterationCount; + int medianIterationCount; + bool createChart; + bool verboseOutput; + QString callgrindOutFileBase; +private: + Mode mode_; +}; + +/* + The QBenchmarkTestMethodData class stores all benchmark-related data + for the current test case. QBenchmarkTestMethodData:current is + created at the beginning of qInvokeTestMethod() and cleared at + the end. +*/ +class QBenchmarkTestMethodData +{ +public: + static QBenchmarkTestMethodData *current; + QBenchmarkTestMethodData(); + ~QBenchmarkTestMethodData(); + + // Called once for each data row created by the _data function, + // before and after calling the test function itself. + void beginDataRun(); + void endDataRun(); + + bool isBenchmark() const { return result.valid; } + bool resultsAccepted() const { return resultAccepted; } + int adjustIterationCount(int suggestion); + void setResult(qint64 value); + + QBenchmarkResult result; + bool resultAccepted; + int iterationCount; +}; + +// low-level API: +namespace QTest +{ + int iterationCount(); + void setIterationCountHint(int count); + void setIterationCount(int count); + + Q_TESTLIB_EXPORT void beginBenchmarkMeasurement(); + Q_TESTLIB_EXPORT qint64 endBenchmarkMeasurement(); + + void setResult(qint64 result); + void setResult(const QString &tag, qint64 result); +} + +QT_END_NAMESPACE + +#endif // QBENCHMARK_H diff --git a/src/testlib/qbenchmarkevent.cpp b/src/testlib/qbenchmarkevent.cpp new file mode 100644 index 0000000..fdd81ff2 --- /dev/null +++ b/src/testlib/qbenchmarkevent.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qbenchmarkevent_p.h" +#include "QtTest/private/qbenchmark_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QAbstractEventDispatcher::EventFilter oldEventFilter = 0; +qint64 QBenchmarkEvent::eventCounter = 0; + +QBenchmarkEvent::~QBenchmarkEvent() +{ +} + +void QBenchmarkEvent::start() +{ + QBenchmarkEvent::eventCounter = 0; + QAbstractEventDispatcher *parent = QAbstractEventDispatcher::instance(); + oldEventFilter = parent->setEventFilter(QBenchmarkEvent::eventCountingMechanism); +} + +qint64 QBenchmarkEvent::checkpoint() +{ + return QBenchmarkEvent::eventCounter; +} + +qint64 QBenchmarkEvent::stop() +{ + QAbstractEventDispatcher *parent = QAbstractEventDispatcher::instance(); + parent->setEventFilter(oldEventFilter); + return QBenchmarkEvent::eventCounter; +} + +// It's very tempting to simply reject a measurement if 0 events +// where counted, however that is a possible situation and returning +// false here will create a infinite loop. Do not change this. +bool QBenchmarkEvent::isMeasurementAccepted(qint64 measurement) +{ + Q_UNUSED(measurement); + return true; +} + +int QBenchmarkEvent::adjustIterationCount(int suggestion) +{ + return suggestion; +} + +int QBenchmarkEvent::adjustMedianCount(int suggestion) +{ + Q_UNUSED(suggestion); + return 1; +} + +QString QBenchmarkEvent::unitText() +{ + return QLatin1String("events"); +} + +QString QBenchmarkEvent::metricText() +{ + return QLatin1String("events"); +} + +// This could be done in a much better way, this is just the beginning. +bool QBenchmarkEvent::eventCountingMechanism(void *message) +{ + Q_UNUSED(message); + QBenchmarkEvent::eventCounter++; + return false; +} + +QT_END_NAMESPACE diff --git a/src/testlib/qbenchmarkevent_p.h b/src/testlib/qbenchmarkevent_p.h new file mode 100644 index 0000000..e027163 --- /dev/null +++ b/src/testlib/qbenchmarkevent_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARKEVENT_P_H +#define QBENCHMARKEVENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qbenchmarkmeasurement_p.h" +#include <QAbstractEventDispatcher> +#include <QObject> + +QT_BEGIN_NAMESPACE + +class QBenchmarkEvent : public QBenchmarkMeasurerBase +{ +public: + ~QBenchmarkEvent(); + void start(); + qint64 checkpoint(); + qint64 stop(); + bool isMeasurementAccepted(qint64 measurement); + int adjustIterationCount(int suggestion); + int adjustMedianCount(int suggestion); + bool repeatCount() { return 1; } + QString unitText(); + QString metricText(); + static bool eventCountingMechanism(void *message); + static qint64 eventCounter; +}; + +QT_END_NAMESPACE + +#endif // QBENCHMARKEVENT_H diff --git a/src/testlib/qbenchmarkmeasurement.cpp b/src/testlib/qbenchmarkmeasurement.cpp new file mode 100644 index 0000000..9307450 --- /dev/null +++ b/src/testlib/qbenchmarkmeasurement.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qbenchmarkmeasurement_p.h" +#include "QtTest/private/qbenchmark_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +// QBenchmarkTimeMeasurer implementation + +void QBenchmarkTimeMeasurer::start() +{ + time.start(); +} + +qint64 QBenchmarkTimeMeasurer::checkpoint() +{ + return time.elapsed(); +} + +qint64 QBenchmarkTimeMeasurer::stop() +{ + return time.elapsed(); +} + +bool QBenchmarkTimeMeasurer::isMeasurementAccepted(qint64 measurement) +{ + return (measurement > 20); +} + +int QBenchmarkTimeMeasurer::adjustIterationCount(int suggestion) +{ + return suggestion; +} + +int QBenchmarkTimeMeasurer::adjustMedianCount(int) +{ + return 1; +} + +QString QBenchmarkTimeMeasurer::unitText() +{ + return QLatin1String("msec"); +} + +QString QBenchmarkTimeMeasurer::metricText() +{ + return QLatin1String("walltime"); +} + +#ifdef HAVE_TICK_COUNTER // defined in 3rdparty/cycle_p.h + +void QBenchmarkTickMeasurer::start() +{ + startTicks = getticks(); +} + +qint64 QBenchmarkTickMeasurer::checkpoint() +{ + CycleCounterTicks now = getticks(); + return qRound64(elapsed(now, startTicks)); +} + +qint64 QBenchmarkTickMeasurer::stop() +{ + CycleCounterTicks now = getticks(); + return qRound64(elapsed(now, startTicks)); +} + +bool QBenchmarkTickMeasurer::isMeasurementAccepted(qint64) +{ + return true; +} + +int QBenchmarkTickMeasurer::adjustIterationCount(int) +{ + return 1; +} + +int QBenchmarkTickMeasurer::adjustMedianCount(int) +{ + return 1; +} + +bool QBenchmarkTickMeasurer::needsWarmupIteration() +{ + return true; +} + +QString QBenchmarkTickMeasurer::unitText() +{ + return QLatin1String("ticks"); +} + +QString QBenchmarkTickMeasurer::metricText() +{ + return QLatin1String("cputicks"); +} + +#endif + + +QT_END_NAMESPACE diff --git a/src/testlib/qbenchmarkmeasurement_p.h b/src/testlib/qbenchmarkmeasurement_p.h new file mode 100644 index 0000000..693a315 --- /dev/null +++ b/src/testlib/qbenchmarkmeasurement_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARK_MEASUREMENT_P_H +#define QBENCHMARK_MEASUREMENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qdatetime.h> +#include "3rdparty/cycle_p.h" + +QT_BEGIN_NAMESPACE + +class QBenchmarkMeasurerBase +{ +public: + virtual ~QBenchmarkMeasurerBase(){}; + virtual void init() {}; + virtual void start() = 0; + virtual qint64 checkpoint() = 0; + virtual qint64 stop() = 0; + virtual bool isMeasurementAccepted(qint64 measurement) = 0; + virtual int adjustIterationCount(int suggestion) = 0; + virtual int adjustMedianCount(int suggestion) = 0; + virtual bool repeatCount() { return 1; } + virtual bool needsWarmupIteration() { return false; } + virtual QString unitText() = 0; + virtual QString metricText() = 0; +}; + +class QBenchmarkTimeMeasurer : public QBenchmarkMeasurerBase +{ +public: + void start(); + qint64 checkpoint(); + qint64 stop(); + bool isMeasurementAccepted(qint64 measurement); + int adjustIterationCount(int sugestion); + int adjustMedianCount(int suggestion); + QString unitText(); + QString metricText(); +private: + QTime time; +}; + +#ifdef HAVE_TICK_COUNTER // defined in 3rdparty/cycle_p.h + +class QBenchmarkTickMeasurer : public QBenchmarkMeasurerBase +{ +public: + void start(); + qint64 checkpoint(); + qint64 stop(); + bool isMeasurementAccepted(qint64 measurement); + int adjustIterationCount(int); + int adjustMedianCount(int suggestion); + bool needsWarmupIteration(); + QString unitText(); + QString metricText(); +private: + CycleCounterTicks startTicks; +}; + +#endif + +QT_END_NAMESPACE + +#endif // QBENCHMARK_MEASUREMENT_P_H diff --git a/src/testlib/qbenchmarkvalgrind.cpp b/src/testlib/qbenchmarkvalgrind.cpp new file mode 100644 index 0000000..4b4ccd7 --- /dev/null +++ b/src/testlib/qbenchmarkvalgrind.cpp @@ -0,0 +1,275 @@ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qbenchmark_p.h" + +#ifdef QTESTLIB_USE_VALGRIND + +#include "QtTest/private/qbenchmarkvalgrind_p.h" +#include <QtCore/qstringlist.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qprocess.h> +#include <QtCore/qdir.h> +#include <QtCore/qset.h> +#include "3rdparty/callgrind_p.h" + +// Returns true iff a sufficiently recent valgrind is available. +bool QBenchmarkValgrindUtils::haveValgrind() +{ +#ifdef NVALGRIND + return false; +#else + QProcess process; + QStringList args; + args << QLatin1String("--version"); + process.start(QLatin1String("valgrind"), args); + if (!process.waitForFinished(-1)) + return false; + const QByteArray out = process.readAllStandardOutput(); + const QRegExp rx(QLatin1String("^valgrind-([0-9]).([0-9]).[0-9]")); + if (rx.indexIn(QLatin1String(out.data())) == -1) + return false; + bool ok; + const int major = rx.cap(1).toInt(&ok); + if (!ok) + return false; + const int minor = rx.cap(2).toInt(&ok); + if (!ok) + return false; +// return (major > 3 || (major == 3 && minor >= 3)); // v >= 3.3 for --callgrind-out-file option + Q_UNUSED(major); + Q_UNUSED(minor); + return true; // skip version restriction for now +#endif +} + +// Reruns this program through callgrind. +// Returns true upon success, otherwise false. +bool QBenchmarkValgrindUtils::rerunThroughCallgrind(const QStringList &origAppArgs, int &exitCode) +{ + if (!QBenchmarkValgrindUtils::runCallgrindSubProcess(origAppArgs, exitCode)) { + qWarning("failed to run callgrind subprocess"); + return false; + } + return true; +} + +static void dumpOutput(const QByteArray &data, FILE *fh) +{ + QFile file; + file.open(fh, QIODevice::WriteOnly); + file.write(data); +} + +qint64 QBenchmarkValgrindUtils::extractResult(const QString &fileName) +{ + QFile file(fileName); + const bool openOk = file.open(QIODevice::ReadOnly | QIODevice::Text); + Q_ASSERT(openOk); + Q_UNUSED(openOk); + + qint64 val = -1; + bool valSeen = false; + const QRegExp rxValue(QLatin1String("^summary: (\\d+)")); + while (!file.atEnd()) { + const QString line(QLatin1String(file.readLine())); + if (rxValue.indexIn(line) != -1) { + Q_ASSERT(rxValue.numCaptures() == 1); + bool ok; + val = rxValue.cap(1).toLongLong(&ok); + Q_ASSERT(ok); + valSeen = true; + break; + } + } + Q_ASSERT(valSeen); + return val; +} + +// Gets the newest file name (i.e. the one with the highest integer suffix). +QString QBenchmarkValgrindUtils::getNewestFileName() +{ + QStringList nameFilters; + QString base = QBenchmarkGlobalData::current->callgrindOutFileBase; + Q_ASSERT(!base.isEmpty()); + + nameFilters << QString(QLatin1String("%1.*")).arg(base); + QFileInfoList fiList = QDir().entryInfoList(nameFilters, QDir::Files | QDir::Readable); + Q_ASSERT(!fiList.empty()); + int hiSuffix = -1; + QFileInfo lastFileInfo; + const QString pattern = QString(QLatin1String("%1.(\\d+)")).arg(base); + const QRegExp rx(pattern); + foreach (QFileInfo fileInfo, fiList) { + const int index = rx.indexIn(fileInfo.fileName()); + Q_ASSERT(index == 0); + Q_UNUSED(index); + bool ok; + const int suffix = rx.cap(1).toInt(&ok); + Q_ASSERT(ok); + Q_ASSERT(suffix >= 0); + if (suffix > hiSuffix) { + lastFileInfo = fileInfo; + hiSuffix = suffix; + } + } + + return lastFileInfo.fileName(); +} + +qint64 QBenchmarkValgrindUtils::extractLastResult() +{ + return extractResult(getNewestFileName()); +} + +void QBenchmarkValgrindUtils::cleanup() +{ + QStringList nameFilters; + QString base = QBenchmarkGlobalData::current->callgrindOutFileBase; + Q_ASSERT(!base.isEmpty()); + nameFilters + << QString(QLatin1String("%1")).arg(base) // overall summary + << QString(QLatin1String("%1.*")).arg(base); // individual dumps + QFileInfoList fiList = QDir().entryInfoList(nameFilters, QDir::Files | QDir::Readable); + foreach (QFileInfo fileInfo, fiList) { + const bool removeOk = QFile::remove(fileInfo.fileName()); + Q_ASSERT(removeOk); + Q_UNUSED(removeOk); + } +} + +QString QBenchmarkValgrindUtils::outFileBase(qint64 pid) +{ + return QString(QLatin1String("callgrind.out.%1")).arg( + pid != -1 ? pid : QCoreApplication::applicationPid()); +} + +// Reruns this program through callgrind, storing callgrind result files in the +// current directory. +// Returns true upon success, otherwise false. +bool QBenchmarkValgrindUtils::runCallgrindSubProcess(const QStringList &origAppArgs, int &exitCode) +{ + const QString execFile(origAppArgs.at(0)); + QStringList args; + args << QLatin1String("--tool=callgrind") << QLatin1String("--instr-atstart=yes") + << QLatin1String("--quiet") + << execFile << QLatin1String("-callgrindchild"); + +#if (defined Q_WS_QWS) + // While running the child process, we aren't processing events, and hence aren't + // acting as the QWS server. Therefore it's necessary to tell the child to act + // as its own server instead of connecting to us. + args << QLatin1String("-qws"); +#endif + + // pass on original arguments that make sense (e.g. avoid wasting time producing output + // that will be ignored anyway) ... + for (int i = 1; i < origAppArgs.size(); ++i) { + const QString arg(origAppArgs.at(i)); + if (arg == QLatin1String("-callgrind")) + continue; + args << arg; // ok to pass on + } + + QProcess process; + process.start(QLatin1String("valgrind"), args); + process.waitForStarted(-1); + QBenchmarkGlobalData::current->callgrindOutFileBase = + QBenchmarkValgrindUtils::outFileBase(process.pid()); + const bool finishedOk = process.waitForFinished(-1); + exitCode = process.exitCode(); + + dumpOutput(process.readAllStandardOutput(), stdout); + dumpOutput(process.readAllStandardError(), stderr); + + return finishedOk; +} + +void QBenchmarkCallgrindMeasurer::start() +{ + CALLGRIND_ZERO_STATS; +} + +qint64 QBenchmarkCallgrindMeasurer::checkpoint() +{ + CALLGRIND_DUMP_STATS; + const qint64 result = QBenchmarkValgrindUtils::extractLastResult(); + return result; +} + +qint64 QBenchmarkCallgrindMeasurer::stop() +{ + return checkpoint(); +} + +bool QBenchmarkCallgrindMeasurer::isMeasurementAccepted(qint64 measurement) +{ + Q_UNUSED(measurement); + return true; +} + +int QBenchmarkCallgrindMeasurer::adjustIterationCount(int) +{ + return 1; +} + +int QBenchmarkCallgrindMeasurer::adjustMedianCount(int) +{ + return 1; +} + +bool QBenchmarkCallgrindMeasurer::needsWarmupIteration() +{ + return true; +} + +QString QBenchmarkCallgrindMeasurer::unitText() +{ + return QLatin1String("instr. loads"); +} + +QString QBenchmarkCallgrindMeasurer::metricText() +{ + return QLatin1String("callgrind"); +} + +#endif // QTESTLIB_USE_VALGRIND diff --git a/src/testlib/qbenchmarkvalgrind_p.h b/src/testlib/qbenchmarkvalgrind_p.h new file mode 100644 index 0000000..39dc77e --- /dev/null +++ b/src/testlib/qbenchmarkvalgrind_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARKVALGRIND_P_H +#define QBENCHMARKVALGRIND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtTest/private/qbenchmarkmeasurement_p.h" +#include <QtCore/qmap.h> +#include <QtCore/qstring.h> + +class QStringList; + +QT_BEGIN_NAMESPACE + +class QBenchmarkValgrindUtils +{ +public: + static bool haveValgrind(); + static bool rerunThroughCallgrind(const QStringList &origAppArgs, int &exitCode); + static bool runCallgrindSubProcess(const QStringList &origAppArgs, int &exitCode); + static qint64 extractResult(const QString &fileName); + static QString getNewestFileName(); + static qint64 extractLastResult(); + static void cleanup(); + static QString outFileBase(qint64 pid = -1); +}; + +class QBenchmarkCallgrindMeasurer : public QBenchmarkMeasurerBase +{ +public: + void start(); + qint64 checkpoint(); + qint64 stop(); + bool isMeasurementAccepted(qint64 measurement); + int adjustIterationCount(int); + int adjustMedianCount(int); + bool needsWarmupIteration(); + QString unitText(); + QString metricText(); +}; + +QT_END_NAMESPACE + +#endif // QBENCHMARKVALGRIND_H diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp new file mode 100644 index 0000000..6f10d72 --- /dev/null +++ b/src/testlib/qplaintestlogger.cpp @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/qtestassert.h" +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qplaintestlogger_p.h" +#include "QtTest/private/qbenchmark_p.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef Q_OS_WIN +#include "windows.h" +#endif + +#ifdef Q_OS_WINCE +#include <QtCore/QString> +#endif + +#include <QtCore/QByteArray> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace QTest { + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + + static CRITICAL_SECTION outputCriticalSection; + static HANDLE hConsole = INVALID_HANDLE_VALUE; + static WORD consoleAttributes = 0; + + static const char *qWinColoredMsg(int prefix, int color, const char *msg) + { + if (!hConsole) + return msg; + + WORD attr = consoleAttributes & ~(FOREGROUND_GREEN | FOREGROUND_BLUE + | FOREGROUND_RED | FOREGROUND_INTENSITY); + if (prefix) + attr |= FOREGROUND_INTENSITY; + if (color == 32) + attr |= FOREGROUND_GREEN; + if (color == 36) + attr |= FOREGROUND_BLUE | FOREGROUND_GREEN; + if (color == 31) + attr |= FOREGROUND_RED | FOREGROUND_INTENSITY; + if (color == 37) + attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + if (color == 33) + attr |= FOREGROUND_RED | FOREGROUND_GREEN; + SetConsoleTextAttribute(hConsole, attr); + printf(msg); + SetConsoleTextAttribute(hConsole, consoleAttributes); + return ""; + } + +# define COLORED_MSG(prefix, color, msg) colored ? qWinColoredMsg(prefix, color, msg) : msg +#else +# define COLORED_MSG(prefix, color, msg) colored && QAbstractTestLogger::isTtyOutput() ? "\033["#prefix";"#color"m" msg "\033[0m" : msg +#endif + + static const char *incidentType2String(QAbstractTestLogger::IncidentTypes type) + { + static bool colored = (!qgetenv("QTEST_COLORED").isEmpty()); + switch (type) { + case QAbstractTestLogger::Pass: + return COLORED_MSG(0, 32, "PASS "); //green + case QAbstractTestLogger::XFail: + return COLORED_MSG(1, 32, "XFAIL "); //light green + case QAbstractTestLogger::Fail: + return COLORED_MSG(0, 31, "FAIL! "); //red + case QAbstractTestLogger::XPass: + return COLORED_MSG(0, 31, "XPASS "); //red, too + } + return "??????"; + } + + static const char *benchmarkResult2String() + { + static bool colored = (!qgetenv("QTEST_COLORED").isEmpty()); + return COLORED_MSG(0, 36, "RESULT "); // cyan + } + + static const char *messageType2String(QAbstractTestLogger::MessageTypes type) + { + static bool colored = (!qgetenv("QTEST_COLORED").isEmpty()); + switch (type) { + case QAbstractTestLogger::Skip: + return COLORED_MSG(0, 37, "SKIP "); //white + case QAbstractTestLogger::Warn: + return COLORED_MSG(0, 33, "WARNING"); // yellow + case QAbstractTestLogger::QWarning: + return COLORED_MSG(1, 33, "QWARN "); + case QAbstractTestLogger::QDebug: + return COLORED_MSG(1, 33, "QDEBUG "); + case QAbstractTestLogger::QSystem: + return COLORED_MSG(1, 33, "QSYSTEM"); + case QAbstractTestLogger::QFatal: + return COLORED_MSG(0, 31, "QFATAL "); // red + case QAbstractTestLogger::Info: + return "INFO "; // no coloring + } + return "??????"; + } + + static void outputMessage(const char *str) + { +#if defined(Q_OS_WINCE) + int length = strlen(str); + for (int pos = 0; pos < length; pos +=255) { + QString uniText = QString::fromLatin1(str + pos, 255); + OutputDebugStringW((const LPCWSTR) uniText.utf16()); + } + if (QTestLog::outputFileName()) +#elif defined(Q_OS_WIN) + EnterCriticalSection(&outputCriticalSection); + // OutputDebugString is not threadsafe + OutputDebugStringA(str); + LeaveCriticalSection(&outputCriticalSection); +#endif + QAbstractTestLogger::outputString(str); + } + + static void printMessage(const char *type, const char *msg, const char *file = 0, int line = 0) + { + QTEST_ASSERT(type); + QTEST_ASSERT(msg); + + char buf[1024]; + + const char *fn = QTestResult::currentTestFunction() ? QTestResult::currentTestFunction() + : "UnknownTestFunc"; + const char *tag = QTestResult::currentDataTag() ? QTestResult::currentDataTag() : ""; + const char *gtag = QTestResult::currentGlobalDataTag() + ? QTestResult::currentGlobalDataTag() + : ""; + const char *filler = (tag[0] && gtag[0]) ? ":" : ""; + if (file) { + QTest::qt_snprintf(buf, sizeof(buf), "%s: %s::%s(%s%s%s)%s%s\n" +#ifdef Q_OS_WIN + "%s(%d) : failure location\n" +#else + " Loc: [%s(%d)]\n" +#endif + , type, QTestResult::currentTestObjectName(), fn, gtag, filler, tag, + msg[0] ? " " : "", msg, file, line); + } else { + QTest::qt_snprintf(buf, sizeof(buf), "%s: %s::%s(%s%s%s)%s%s\n", + type, QTestResult::currentTestObjectName(), fn, gtag, filler, tag, + msg[0] ? " " : "", msg); + } + memcpy(buf, type, strlen(type)); + outputMessage(buf); + } + + template <typename T> + static int countSignificantDigits(T num) + { + if (num <= 0) + return 0; + + int digits = 0; + qreal divisor = 1; + + while (num / divisor >= 1) { + divisor *= 10; + ++digits; + } + + return digits; + } + + // Pretty-prints a benchmark result using the given number of digits. + template <typename T> QString formatResult(T number, int significantDigits) + { + if (number < T(0)) + return QString(QLatin1String("NAN")); + if (number == T(0)) + return QString(QLatin1String("0")); + + QString beforeDecimalPoint = QString::number(qint64(number), 'f', 0); + QString afterDecimalPoint = QString::number(number, 'f', 20); + afterDecimalPoint.remove(0, beforeDecimalPoint.count() + 1); + + int beforeUse = qMin(beforeDecimalPoint.count(), significantDigits); + int beforeRemove = beforeDecimalPoint.count() - beforeUse; + + // Replace insignificant digits before the decimal point with zeros. + beforeDecimalPoint.chop(beforeRemove); + for (int i = 0; i < beforeRemove; ++i) { + beforeDecimalPoint.append(QLatin1Char('0')); + } + + int afterUse = significantDigits - beforeUse; + + // leading zeroes after the decimal point does not count towards the digit use. + if (beforeDecimalPoint == QLatin1String("0") && afterDecimalPoint.isEmpty() == false) { + ++afterUse; + + int i = 0; + while (i < afterDecimalPoint.count() && afterDecimalPoint.at(i) == QLatin1Char('0')) { + ++i; + } + + afterUse += i; + } + + int afterRemove = afterDecimalPoint.count() - afterUse; + afterDecimalPoint.chop(afterRemove); + + QChar separator = QLatin1Char(','); + QChar decimalPoint = QLatin1Char('.'); + + // insert thousands separators + int length = beforeDecimalPoint.length(); + for (int i = beforeDecimalPoint.length() -1; i >= 1; --i) { + if ((length - i) % 3 == 0) + beforeDecimalPoint.insert(i, separator); + } + + QString print; + print = beforeDecimalPoint; + if (afterUse > 0) + print.append(decimalPoint); + + print += afterDecimalPoint; + + + return print; + } + + template <typename T> + int formatResult(char * buffer, int bufferSize, T number, int significantDigits) + { + QString result = formatResult(number, significantDigits); + qstrncpy(buffer, result.toAscii().constData(), bufferSize); + int size = result.count(); + return size; + } + +// static void printBenchmarkResult(const char *bmtag, int value, int iterations) + static void printBenchmarkResult(const QBenchmarkResult &result) + { + const char *bmtag = QTest::benchmarkResult2String(); + + char buf1[1024]; + QTest::qt_snprintf( + buf1, sizeof(buf1), "%s: %s::%s", + bmtag, + QTestResult::currentTestObjectName(), + result.context.slotName.toAscii().data()); + + + char bufTag[1024]; + bufTag[0] = 0; + QByteArray tag = result.context.tag.toAscii(); + if (tag.isEmpty() == false) { + QTest::qt_snprintf(bufTag, sizeof(bufTag), ":\"%s\"", tag.data()); + } + + + char fillFormat[8]; + int fillLength = 5; + QTest::qt_snprintf( + fillFormat, sizeof(fillFormat), ":\n%%%ds", fillLength); + char fill[1024]; + QTest::qt_snprintf(fill, sizeof(fill), fillFormat, ""); + + + QByteArray unitText = QBenchmarkGlobalData::current->measurer->unitText().toAscii(); + + qreal valuePerIteration = qreal(result.value) / qreal(result.iterations); + char resultBuffer[100] = ""; + formatResult(resultBuffer, 100, valuePerIteration, countSignificantDigits(result.value)); + + QByteArray iterationText = "per iteration"; + + char buf2[1024]; + Q_ASSERT(result.iterations > 0); + QTest::qt_snprintf( + buf2, sizeof(buf2), "%s %s %s", + resultBuffer, + unitText.data(), + iterationText.data()); + + char buf3[1024]; + Q_ASSERT(result.iterations > 0); + QTest::qt_snprintf( + buf3, sizeof(buf3), " (total: %s, iterations: %d)\n", + QByteArray::number(result.value).constData(), // no 64-bit qt_snprintf support + result.iterations); + + char buf[1024]; + QTest::qt_snprintf(buf, sizeof(buf), "%s%s%s%s%s", buf1, bufTag, fill, buf2, buf3); + memcpy(buf, bmtag, strlen(bmtag)); + outputMessage(buf); + } +} + +QPlainTestLogger::QPlainTestLogger() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + InitializeCriticalSection(&QTest::outputCriticalSection); + QTest::hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (QTest::hConsole != INVALID_HANDLE_VALUE) { + CONSOLE_SCREEN_BUFFER_INFO info; + if (GetConsoleScreenBufferInfo(QTest::hConsole, &info)) { + QTest::consoleAttributes = info.wAttributes; + } else { + QTest::hConsole = INVALID_HANDLE_VALUE; + } + } +#endif +} + +QPlainTestLogger::~QPlainTestLogger() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + DeleteCriticalSection(&QTest::outputCriticalSection); +#endif +} + +void QPlainTestLogger::startLogging() +{ + QAbstractTestLogger::startLogging(); + + char buf[1024]; + if (QTestLog::verboseLevel() < 0) { + QTest::qt_snprintf(buf, sizeof(buf), "Testing %s\n", + QTestResult::currentTestObjectName()); + } else { + QTest::qt_snprintf(buf, sizeof(buf), + "********* Start testing of %s *********\n" + "Config: Using QTest library " QTEST_VERSION_STR + ", Qt %s\n", QTestResult::currentTestObjectName(), qVersion()); + } + QTest::outputMessage(buf); +} + +void QPlainTestLogger::stopLogging() +{ + char buf[1024]; + if (QTestLog::verboseLevel() < 0) { + QTest::qt_snprintf(buf, sizeof(buf), "Totals: %d passed, %d failed, %d skipped\n", + QTestResult::passCount(), QTestResult::failCount(), + QTestResult::skipCount()); + } else { + QTest::qt_snprintf(buf, sizeof(buf), + "Totals: %d passed, %d failed, %d skipped\n" + "********* Finished testing of %s *********\n", + QTestResult::passCount(), QTestResult::failCount(), + QTestResult::skipCount(), QTestResult::currentTestObjectName()); + } + QTest::outputMessage(buf); + + QAbstractTestLogger::stopLogging(); +} + + +void QPlainTestLogger::enterTestFunction(const char * /*function*/) +{ + if (QTestLog::verboseLevel() >= 1) + QTest::printMessage(QTest::messageType2String(Info), "entering"); +} + +void QPlainTestLogger::leaveTestFunction() +{ +} + +void QPlainTestLogger::addIncident(IncidentTypes type, const char *description, + const char *file, int line) +{ + // suppress PASS in silent mode + if (type == QAbstractTestLogger::Pass && QTestLog::verboseLevel() < 0) + return; + + QTest::printMessage(QTest::incidentType2String(type), description, file, line); +} + +void QPlainTestLogger::addBenchmarkResult(const QBenchmarkResult &result) +{ +// QTest::printBenchmarkResult(QTest::benchmarkResult2String(), value, iterations); + QTest::printBenchmarkResult(result); +} + +void QPlainTestLogger::addMessage(MessageTypes type, const char *message, + const char *file, int line) +{ + // suppress PASS in silent mode + if ((type == QAbstractTestLogger::Skip || type == QAbstractTestLogger::Info) + && QTestLog::verboseLevel() < 0) + return; + + QTest::printMessage(QTest::messageType2String(type), message, file, line); +} + +QT_END_NAMESPACE diff --git a/src/testlib/qplaintestlogger_p.h b/src/testlib/qplaintestlogger_p.h new file mode 100644 index 0000000..fac6415 --- /dev/null +++ b/src/testlib/qplaintestlogger_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLAINTESTLOGGER_P_H +#define QPLAINTESTLOGGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtTest/private/qabstracttestlogger_p.h> + +QT_BEGIN_NAMESPACE + +class QPlainTestLogger : public QAbstractTestLogger +{ +public: + QPlainTestLogger(); + ~QPlainTestLogger(); + + void startLogging(); + void stopLogging(); + + void enterTestFunction(const char *function); + void leaveTestFunction(); + + void addIncident(IncidentTypes type, const char *description, + const char *file = 0, int line = 0); + void addBenchmarkResult(const QBenchmarkResult &result); + + void addMessage(MessageTypes type, const char *message, + const char *file = 0, int line = 0); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qsignaldumper.cpp b/src/testlib/qsignaldumper.cpp new file mode 100644 index 0000000..0a32a6d --- /dev/null +++ b/src/testlib/qsignaldumper.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qsignaldumper_p.h" + +#include <QtCore/qlist.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> + +#include "QtTest/private/qtestlog_p.h" + +QT_BEGIN_NAMESPACE + +namespace QTest +{ + +inline static void qPrintMessage(const QByteArray &ba) +{ + QTestLog::info(ba.constData(), 0, 0); +} + +Q_GLOBAL_STATIC(QList<QByteArray>, ignoreClasses) +static int iLevel = 0; +static int ignoreLevel = 0; +enum { IndentSpacesCount = 4 }; + +static QByteArray memberName(const QMetaMethod &member) +{ + QByteArray ba = member.signature(); + return ba.left(ba.indexOf('(')); +} + +static void qSignalDumperCallback(QObject *caller, int method_index, void **argv) +{ + Q_ASSERT(caller); Q_ASSERT(argv); Q_UNUSED(argv); + const QMetaObject *mo = caller->metaObject(); + Q_ASSERT(mo); + QMetaMethod member = mo->method(method_index); + Q_ASSERT(member.signature()); + + if (QTest::ignoreClasses() && QTest::ignoreClasses()->contains(mo->className())) { + ++QTest::ignoreLevel; + return; + } + + QByteArray str; + str.fill(' ', QTest::iLevel++ * QTest::IndentSpacesCount); + str += "Signal: "; + str += mo->className(); + str += "("; + + QString objname = caller->objectName(); + str += objname.toLocal8Bit(); + if (!objname.isEmpty()) + str += ' '; + str += QByteArray::number(quintptr(caller), 16); + + str += ") "; + str += QTest::memberName(member); + str += " ("; + + QList<QByteArray> args = member.parameterTypes(); + for (int i = 0; i < args.count(); ++i) { + const QByteArray &arg = args.at(i); + int typeId = QMetaType::type(args.at(i).constData()); + if (arg.endsWith('*') || arg.endsWith('&')) { + str += '('; + str += arg; + str += ')'; + if (arg.endsWith('&')) + str += '@'; + + quintptr addr = quintptr(*reinterpret_cast<void **>(argv[i + 1])); + str.append(QByteArray::number(addr, 16)); + } else if (typeId != QMetaType::Void) { + str.append(arg) + .append("(") + .append(QVariant(typeId, argv[i + 1]).toString().toLocal8Bit()) + .append(")"); + } + str.append(", "); + } + if (str.endsWith(", ")) + str.chop(2); + str.append(")"); + qPrintMessage(str); +} + +static void qSignalDumperCallbackSlot(QObject *caller, int method_index, void **argv) +{ + Q_ASSERT(caller); Q_ASSERT(argv); Q_UNUSED(argv); + const QMetaObject *mo = caller->metaObject(); + Q_ASSERT(mo); + QMetaMethod member = mo->method(method_index); + if (!member.signature()) + return; + + if (QTest::ignoreLevel || + (QTest::ignoreClasses() && QTest::ignoreClasses()->contains(mo->className()))) + return; + + QByteArray str; + str.fill(' ', QTest::iLevel * QTest::IndentSpacesCount); + str += "Slot: "; + str += mo->className(); + str += "("; + + QString objname = caller->objectName(); + str += objname.toLocal8Bit(); + if (!objname.isEmpty()) + str += ' '; + str += QByteArray::number(quintptr(caller), 16); + + str += ") "; + str += member.signature(); + qPrintMessage(str); +} + +static void qSignalDumperCallbackEndSignal(QObject *caller, int /*method_index*/) +{ + Q_ASSERT(caller); Q_ASSERT(caller->metaObject()); + if (QTest::ignoreClasses() + && QTest::ignoreClasses()->contains(caller->metaObject()->className())) { + --QTest::ignoreLevel; + Q_ASSERT(QTest::ignoreLevel >= 0); + return; + } + --QTest::iLevel; + Q_ASSERT(QTest::iLevel >= 0); +} + +} + +// this struct is copied from qobject_p.h to prevent us +// from including private Qt headers. +struct QSignalSpyCallbackSet +{ + typedef void (*BeginCallback)(QObject *caller, int method_index, void **argv); + typedef void (*EndCallback)(QObject *caller, int method_index); + BeginCallback signal_begin_callback, + slot_begin_callback; + EndCallback signal_end_callback, + slot_end_callback; +}; +extern void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &); + +void QSignalDumper::startDump() +{ + static QSignalSpyCallbackSet set = { QTest::qSignalDumperCallback, + QTest::qSignalDumperCallbackSlot, QTest::qSignalDumperCallbackEndSignal, 0 }; + qt_register_signal_spy_callbacks(set); +} + +void QSignalDumper::endDump() +{ + static QSignalSpyCallbackSet nset = { 0, 0, 0 ,0 }; + qt_register_signal_spy_callbacks(nset); +} + +void QSignalDumper::ignoreClass(const QByteArray &klass) +{ + if (QTest::ignoreClasses()) + QTest::ignoreClasses()->append(klass); +} + +void QSignalDumper::clearIgnoredClasses() +{ + if (QTest::ignoreClasses()) + QTest::ignoreClasses()->clear(); +} + +QT_END_NAMESPACE diff --git a/src/testlib/qsignaldumper_p.h b/src/testlib/qsignaldumper_p.h new file mode 100644 index 0000000..8d8bbc9 --- /dev/null +++ b/src/testlib/qsignaldumper_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALDUMPER_P_H +#define QSIGNALDUMPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +class QByteArray; + +class QSignalDumper +{ +public: + static void startDump(); + static void endDump(); + + static void ignoreClass(const QByteArray &klass); + static void clearIgnoredClasses(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h new file mode 100644 index 0000000..d37bd6f --- /dev/null +++ b/src/testlib/qsignalspy.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALSPY_H +#define QSIGNALSPY_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +class QVariant; + +/* ### Qt5: change the class to use regular BC mechanisms, such that we can + * implement things like suggested in task 160192. */ +class QSignalSpy: public QObject, public QList<QList<QVariant> > +{ +public: + QSignalSpy(QObject *obj, const char *aSignal) + { +#ifdef Q_CC_BOR + const int memberOffset = QObject::staticMetaObject.methodCount(); +#else + static const int memberOffset = QObject::staticMetaObject.methodCount(); +#endif + Q_ASSERT(obj); + Q_ASSERT(aSignal); + + if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) { + qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro"); + return; + } + + QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1); + const QMetaObject *mo = obj->metaObject(); + int sigIndex = mo->indexOfMethod(ba.constData()); + if (sigIndex < 0) { + qWarning("QSignalSpy: No such signal: '%s'", ba.constData()); + return; + } + + if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, + Qt::DirectConnection, 0)) { + qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect."); + return; + } + sig = ba; + initArgs(mo->method(sigIndex)); + } + + inline bool isValid() const { return !sig.isEmpty(); } + inline QByteArray signal() const { return sig; } + + + int qt_metacall(QMetaObject::Call call, int methodId, void **a) + { + methodId = QObject::qt_metacall(call, methodId, a); + if (methodId < 0) + return methodId; + + if (call == QMetaObject::InvokeMetaMethod) { + if (methodId == 0) { + appendArgs(a); + } + --methodId; + } + return methodId; + } + +private: + void initArgs(const QMetaMethod &member) + { + QList<QByteArray> params = member.parameterTypes(); + for (int i = 0; i < params.count(); ++i) { + int tp = QMetaType::type(params.at(i).constData()); + if (tp == QMetaType::Void) + qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.", + params.at(i).constData()); + args << tp; + } + } + + void appendArgs(void **a) + { + QList<QVariant> list; + for (int i = 0; i < args.count(); ++i) { + QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i)); + list << QVariant(type, a[i + 1]); + } + append(list); + } + + // the full, normalized signal name + QByteArray sig; + // holds the QMetaType types for the argument list of the signal + QList<int> args; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h new file mode 100644 index 0000000..6ea38f7 --- /dev/null +++ b/src/testlib/qtest.h @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEST_H +#define QTEST_H + +#include <QtTest/qtest_global.h> +#include <QtTest/qtestcase.h> +#include <QtTest/qtestdata.h> +#include <QtTest/qtestdata.h> +#include <QtTest/qbenchmark.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qobject.h> +#include <QtCore/qurl.h> + +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> +#include <QtCore/qrect.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + +template<> inline char *toString(const QLatin1String &str) +{ + return qstrdup(str.latin1()); +} + +template<> inline char *toString(const QString &str) +{ + return qstrdup(str.toLatin1().constData()); +} + +template<> inline char *toString(const QByteArray &ba) +{ + return QTest::toHexRepresentation(ba.constData(), ba.length()); +} + +template<> inline char *toString(const QTime &time) +{ + return time.isValid() + ? qstrdup(time.toString(QLatin1String("hh:mm:ss.zzz")).toLatin1()) + : qstrdup("Invalid QTime"); +} + +template<> inline char *toString(const QDate &date) +{ + return date.isValid() + ? qstrdup(date.toString(QLatin1String("yyyy/MM/dd")).toLatin1()) + : qstrdup("Invalid QDate"); +} + +template<> inline char *toString(const QDateTime &dateTime) +{ + return dateTime.isValid() + ? qstrdup((dateTime.toString(QLatin1String("yyyy/MM/dd hh:mm:ss.zzz")) + + (dateTime.timeSpec() == Qt::LocalTime ? QLatin1String("[local time]") : QLatin1String("[UTC]"))).toLatin1()) + : qstrdup("Invalid QDateTime"); +} + +template<> inline char *toString(const QChar &c) +{ + return qstrdup(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16)).toLatin1().constData()); +} + +template<> inline char *toString(const QPoint &p) +{ + return qstrdup(QString::fromLatin1("QPoint(%1,%2)").arg(p.x()).arg(p.y()).toLatin1().constData()); +} + +template<> inline char *toString(const QSize &s) +{ + return qstrdup(QString::fromLatin1("QSize(%1x%2)").arg(s.width()).arg(s.height()).toLatin1().constData()); +} + +template<> inline char *toString(const QRect &s) +{ + return qstrdup(QString::fromLatin1("QRect(%1,%2 %5x%6) (bottomright %3,%4)").arg(s.left()).arg(s.top()).arg(s.right()).arg(s.bottom()).arg(s.width()).arg(s.height()).toLatin1().constData()); +} + +template<> inline char *toString(const QPointF &p) +{ + return qstrdup(QString::fromLatin1("QPointF(%1,%2)").arg(p.x()).arg(p.y()).toLatin1().constData()); +} + +template<> inline char *toString(const QSizeF &s) +{ + return qstrdup(QString::fromLatin1("QSizeF(%1x%2)").arg(s.width()).arg(s.height()).toLatin1().constData()); +} + +template<> inline char *toString(const QRectF &s) +{ + return qstrdup(QString::fromLatin1("QRectF(%1,%2 %5x%6) (bottomright %3,%4)").arg(s.left()).arg(s.top()).arg(s.right()).arg(s.bottom()).arg(s.width()).arg(s.height()).toLatin1().constData()); +} + +template<> inline char *toString(const QUrl &uri) +{ + return qstrdup(uri.toEncoded().constData()); +} + +#ifndef QTEST_NO_SPECIALIZATIONS +template<> +#endif +inline bool qCompare(QString const &t1, QLatin1String const &t2, const char *actual, + const char *expected, const char *file, int line) +{ + return qCompare<QString>(t1, QString(t2), actual, expected, file, line); +} +#ifndef QTEST_NO_SPECIALIZATIONS +template<> +#endif +inline bool qCompare(QLatin1String const &t1, QString const &t2, const char *actual, + const char *expected, const char *file, int line) +{ + return qCompare<QString>(QString(t1), t2, actual, expected, file, line); +} + +template<> +inline bool qCompare(QStringList const &t1, QStringList const &t2, + const char *actual, const char *expected, const char *file, int line) +{ + char msg[1024]; + msg[0] = '\0'; + bool isOk = true; + if (t1.count() != t2.count()) { + qt_snprintf(msg, 1024, "Compared QStringLists have different sizes.\n" + " Actual (%s) size : '%d'\n" + " Expected (%s) size: '%d'", actual, t1.count(), expected, t2.count()); + isOk = false; + } + const int min = qMin(t1.count(), t2.count()); + for (int i = 0; isOk && i < min; ++i) { + if (t1.at(i) != t2.at(i)) { + qt_snprintf(msg, 1024, "Compared QStringLists differ at index %d.\n" + " Actual (%s) : '%s'\n" + " Expected (%s) : '%s'", i, actual, t1.at(i).toLatin1().constData(), + expected, t2.at(i).toLatin1().constData()); + isOk = false; + } + } + return compare_helper(isOk, msg, file, line); +} + +template <typename T> +inline bool qCompare(QFlags<T> const &t1, T const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + return qCompare(int(t1), int(t2), actual, expected, file, line); +} + +template <typename T> +inline bool qCompare(QFlags<T> const &t1, int const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + return qCompare(int(t1), t2, actual, expected, file, line); +} + +} +QT_END_NAMESPACE + +#define QTEST_APPLESS_MAIN(TestObject) \ +int main(int argc, char *argv[]) \ +{ \ + TestObject tc; \ + return QTest::qExec(&tc, argc, argv); \ +} + +#define QTEST_NOOP_MAIN \ +int main(int argc, char *argv[]) \ +{ \ + QObject tc; \ + return QTest::qExec(&tc, argc, argv); \ +} + +#include <QtTest/qtestsystem.h> + +#ifdef QT_GUI_LIB + +#include <QtTest/qtest_gui.h> + +#define QTEST_MAIN(TestObject) \ +int main(int argc, char *argv[]) \ +{ \ + QApplication app(argc, argv); \ + TestObject tc; \ + return QTest::qExec(&tc, argc, argv); \ +} + +#else + +#define QTEST_MAIN(TestObject) \ +int main(int argc, char *argv[]) \ +{ \ + QCoreApplication app(argc, argv); \ + TestObject tc; \ + return QTest::qExec(&tc, argc, argv); \ +} + +#endif // QT_GUI_LIB + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtest_global.h b/src/testlib/qtest_global.h new file mode 100644 index 0000000..d6b0655 --- /dev/null +++ b/src/testlib/qtest_global.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEST_GLOBAL_H +#define QTEST_GLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +#ifdef QTEST_EMBED +# define Q_TESTLIB_EXPORT +#elif !defined(QT_SHARED) +# define Q_TESTLIB_EXPORT +#else +# ifdef QTESTLIB_MAKEDLL +# define Q_TESTLIB_EXPORT Q_DECL_EXPORT +# else +# define Q_TESTLIB_EXPORT Q_DECL_IMPORT +# endif +#endif + +#if (defined (Q_CC_MSVC) && _MSC_VER < 1310) || defined (Q_CC_SUN) || defined (Q_CC_XLC) || (defined (Q_CC_GNU) && (__GNUC__ - 0 < 3)) +# define QTEST_NO_SPECIALIZATIONS +#endif + +#if (defined Q_CC_HPACC) && (defined __ia64) +# ifdef Q_TESTLIB_EXPORT +# undef Q_TESTLIB_EXPORT +# endif +# define Q_TESTLIB_EXPORT +#endif + +#define QTEST_VERSION QT_VERSION +#define QTEST_VERSION_STR QT_VERSION_STR + +namespace QTest +{ + enum SkipMode { SkipSingle = 1, SkipAll = 2 }; + enum TestFailMode { Abort = 1, Continue = 2 }; + + int Q_TESTLIB_EXPORT qt_snprintf(char *str, int size, const char *format, ...); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtest_gui.h b/src/testlib/qtest_gui.h new file mode 100644 index 0000000..d389f1c --- /dev/null +++ b/src/testlib/qtest_gui.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEST_GUI_H +#define QTEST_GUI_H + +#include <QtTest/qtestassert.h> +#include <QtTest/qtest.h> +#include <QtTest/qtestevent.h> +#include <QtTest/qtestmouse.h> +#include <QtTest/qtestkeyboard.h> + +#include <QtGui/qicon.h> +#include <QtGui/qpixmap.h> + +#if 0 +// inform syncqt +#pragma qt_no_master_include +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + +template<> +inline bool qCompare(QIcon const &t1, QIcon const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + QTEST_ASSERT(sizeof(QIcon) == sizeof(void *)); + return qCompare<void *>(*reinterpret_cast<void * const *>(&t1), + *reinterpret_cast<void * const *>(&t2), actual, expected, file, line); +} + +template<> +inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + return qCompare(t1.toImage(), t2.toImage(), actual, expected, file, line); +} + +} + +/* compatibility */ + +inline static bool pixmapsAreEqual(const QPixmap *actual, const QPixmap *expected) +{ + if (!actual && !expected) + return true; + if (!actual || !expected) + return false; + if (actual->isNull() && expected->isNull()) + return true; + if (actual->isNull() || expected->isNull() || actual->size() != expected->size()) + return false; + return actual->toImage() == expected->toImage(); +} + +#ifdef Q_WS_X11 +extern void qt_x11_wait_for_window_manager(QWidget *w); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestaccessible.h b/src/testlib/qtestaccessible.h new file mode 100644 index 0000000..557690b --- /dev/null +++ b/src/testlib/qtestaccessible.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTACCESSIBLE_H +#define QTESTACCESSIBLE_H + +#if 0 +// inform syncqt +#pragma qt_no_master_include +#endif + +#ifndef QT_NO_ACCESSIBILITY + +#define QTEST_ACCESSIBILITY + +#define QVERIFY_EVENT(object, child, event) \ + QVERIFY(QTestAccessibility::verifyEvent(object, child, (int)event)) + +#include <QtCore/qlist.h> +#include <QtGui/qaccessible.h> +#include <QtGui/qapplication.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +class QObject; + +struct QTestAccessibilityEvent +{ + QTestAccessibilityEvent(QObject* o = 0, int c = 0, int e = 0) + : object(o), child(c), event(e) {} + + bool operator==(const QTestAccessibilityEvent &o) const + { + return o.object == object && o.child == child && o.event == event; + } + + QObject* object; + int child; + int event; +}; + +typedef QList<QTestAccessibilityEvent> EventList; + +class QTestAccessibility +{ +public: + static void initialize() + { + if (!instance()) { + instance() = new QTestAccessibility; + qAddPostRoutine(cleanup); + } + } + static void cleanup() + { + delete instance(); + instance() = 0; + } + static void clearEvents() { eventList().clear(); } + static EventList events() { return eventList(); } + static bool verifyEvent(const QTestAccessibilityEvent& ev) + { + if (eventList().isEmpty()) + return FALSE; + return eventList().takeFirst() == ev; + } + + static bool verifyEvent(QObject *o, int c, int e) + { + return verifyEvent(QTestAccessibilityEvent(o, c, e)); + } + +private: + QTestAccessibility() + { + QAccessible::installUpdateHandler(updateHandler); + QAccessible::installRootObjectHandler(rootObjectHandler); + } + + ~QTestAccessibility() + { + QAccessible::installUpdateHandler(0); + QAccessible::installRootObjectHandler(0); + } + + static void rootObjectHandler(QObject *object) + { + // qDebug("rootObjectHandler called %p", object); + if (object) { + QApplication* app = qobject_cast<QApplication*>(object); + if ( !app ) + qWarning("QTEST_ACCESSIBILITY: root Object is not a QApplication!"); + } else { + qWarning("QTEST_ACCESSIBILITY: root Object called with 0 pointer"); + } + } + + static void updateHandler(QObject *o, int c, QAccessible::Event e) + { + // qDebug("updateHandler called: %p %d %d", o, c, (int)e); + eventList().append(QTestAccessibilityEvent(o, c, (int)e)); + } + + static EventList &eventList() + { + static EventList list; + return list; + } + + static QTestAccessibility *&instance() + { + static QTestAccessibility *ta = 0; + return ta; + } +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestassert.h b/src/testlib/qtestassert.h new file mode 100644 index 0000000..1425ceb --- /dev/null +++ b/src/testlib/qtestassert.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTASSERT_H +#define QTESTASSERT_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +#define QTEST_ASSERT(cond) do {if(!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0) + +#define QTEST_ASSERT_X(cond, where, what) do {if(!(cond))qt_assert_x(where, what,__FILE__,__LINE__);} while (0) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp new file mode 100644 index 0000000..f17d95d --- /dev/null +++ b/src/testlib/qtestcase.cpp @@ -0,0 +1,1933 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/qtestcase.h" +#include "QtTest/qtestassert.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qobject.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvector.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> +#include <QtCore/qprocess.h> +#include <QtCore/qdebug.h> + +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qtesttable_p.h" +#include "QtTest/qtestdata.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qsignaldumper_p.h" +#include "QtTest/private/qbenchmark_p.h" +#include "3rdparty/cycle_p.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef Q_OS_WIN +#include <windows.h> // for Sleep +#endif +#ifdef Q_OS_UNIX +#include <time.h> +#endif + +#ifdef Q_WS_MAC +#include <Carbon/Carbon.h> // for SetFrontProcess +#ifdef QT_MAC_USE_COCOA +#include <IOKit/pwr_mgt/IOPMLib.h> +#else +#include <Security/AuthSession.h> +#endif +#undef verify +#endif + +QT_BEGIN_NAMESPACE + +/*! + \namespace QTest + \inmodule QtTest + + \brief The QTest namespace contains all the functions and + declarations that are related to the QTestLib tool. + + Please refer to the \l{QTestLib Manual} documentation for information on + how to write unit tests. +*/ + +/*! \macro QVERIFY(condition) + + \relates QTest + + The QVERIFY() macro checks whether the \a condition is true or not. If it is + true, execution continues. If not, a failure is recorded in the test log + and the test won't be executed further. + + \bold {Note:} This macro can only be used in a test function that is invoked + by the test framework. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 0 + + \sa QCOMPARE() +*/ + +/*! \macro QVERIFY2(condition, message) + + \relates QTest + + The QVERIFY2() macro behaves exactly like QVERIFY(), except that it outputs + a verbose \a message when \a condition is false. The \a message is a plain + C string. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 1 + + \sa QVERIFY(), QCOMPARE() +*/ + +/*! \macro QCOMPARE(actual, expected) + + \relates QTest + + The QCOMPARE macro compares an \a actual value to an \a expected value using + the equals operator. If \a actual and \a expected are identical, execution + continues. If not, a failure is recorded in the test log and the test + won't be executed further. + + In the case of comparing floats and doubles, qFuzzyCompare() is used for + comparing. This means that comparing to 0 will likely fail. One solution + to this is to compare to 1, and add 1 to the produced output. + + QCOMPARE tries to output the contents of the values if the comparison fails, + so it is visible from the test log why the comparison failed. + + QCOMPARE is very strict on the data types. Both \a actual and \a expected + have to be of the same type, otherwise the test won't compile. This prohibits + unspecified behavior from being introduced; that is behavior that usually + occurs when the compiler implicitly casts the argument. + + If you use QCOMPARE() to compare two QStringList objects, it will start + comparing the objects from the end of the lists. + + For your own classes, you can use \l QTest::toString() to format values for + outputting into the test log. + + \note This macro can only be used in a test function that is invoked + by the test framework. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 2 + + \sa QVERIFY(), QTest::toString() +*/ + +/*! \macro QFETCH(type, name) + + \relates QTest + + The fetch macro creates a local variable named \a name with the type \a type + on the stack. \a name has to match the element name from the test's data. + If no such element exists, the test will assert. + + Assuming a test has the following data: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 3 + + The test data has two elements, a QString called \c aString and an integer + called \c expected. To fetch these values in the actual test: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 4 + + \c aString and \c expected are variables on the stack that are initialized with + the current test data. + + \bold {Note:} This macro can only be used in a test function that is invoked + by the test framework. The test function must have a _data function. +*/ + +/*! \macro QWARN(message) + + \relates QTest + \threadsafe + + Appends \a message as a warning to the test log. This macro can be used anywhere + in your tests. +*/ + +/*! \macro QFAIL(message) + + \relates QTest + + This macro can be used to force a test failure. The test stops + executing and the failure \a message is appended to the test log. + + \bold {Note:} This macro can only be used in a test function that is invoked + by the test framework. + + Example: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 5 +*/ + +/*! \macro QTEST(actual, testElement) + + \relates QTest + + QTEST() is a convenience macro for \l QCOMPARE() that compares + the value \a actual with the element \a testElement from the test's data. + If there is no such element, the test asserts. + + Apart from that, QTEST() behaves exactly as \l QCOMPARE(). + + Instead of writing: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 6 + + you can write: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 7 + + \sa QCOMPARE() +*/ + +/*! \macro QSKIP(description, mode) + + \relates QTest + + The QSKIP() macro stops execution of the test without adding a failure to the + test log. You can use it to skip tests that wouldn't make sense in the current + configuration. The text \a description is appended to the test log and should + contain an explanation why the test couldn't be executed. \a mode is a QTest::SkipMode + and describes whether to proceed with the rest of the test data or not. + + \bold {Note:} This macro can only be used in a test function that is invoked + by the test framework. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 8 + + \sa QTest::SkipMode +*/ + +/*! \macro QEXPECT_FAIL(dataIndex, comment, mode) + + \relates QTest + + The QEXPECT_FAIL() macro marks the next \l QCOMPARE() or \l QVERIFY() as an + expected failure. Instead of adding a failure to the test log, an expected + failure will be reported. + + If a \l QVERIFY() or \l QCOMPARE() is marked as an expected failure, + but passes instead, an unexpected pass (XPASS) is written to the test log. + + The parameter \a dataIndex describes for which entry in the test data the + failure is expected. Pass an empty string (\c{""}) if the failure + is expected for all entries or if no test data exists. + + \a comment will be appended to the test log for the expected failure. + + \a mode is a \l QTest::TestFailMode and sets whether the test should + continue to execute or not. + + \bold {Note:} This macro can only be used in a test function that is invoked + by the test framework. + + Example 1: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 9 + + In the example above, an expected fail will be written into the test output + if the variable \c i is not 42. If the variable \c i is 42, an unexpected pass + is written instead. The QEXPECT_FAIL() has no influence on the second QCOMPARE() + statement in the example. + + Example 2: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 10 + + The above testfunction will not continue executing for the test data + entry \c{data27}. + + \sa QTest::TestFailMode, QVERIFY(), QCOMPARE() +*/ + +/*! \macro QTEST_MAIN(TestClass) + + \relates QTest + + Implements a main() function that instantiates a QApplication object and + the \a TestClass, and executes all tests in the order they were defined. + Use this macro to build stand-alone executables. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 11 + + \sa QTEST_APPLESS_MAIN(), QTest::qExec() +*/ + +/*! \macro QTEST_APPLESS_MAIN(TestClass) + + \relates QTest + + Implements a main() function that executes all tests in \a TestClass. + + Behaves like \l QTEST_MAIN(), but doesn't instantiate a QApplication + object. Use this macro for really simple stand-alone non-GUI tests. + + \sa QTEST_MAIN() +*/ + +/*! \macro QTEST_NOOP_MAIN() + + \relates QTest + + Implements a main() function with a test class that does absolutely nothing. + Use this macro to create a test that produces valid test output but just + doesn't execute any test, for example in conditional compilations: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 12 + + \sa QTEST_MAIN() +*/ + +/*! + \macro QBENCHMARK + + \relates QTest + + This macro is used to measure the performance of code within a test. + The code to be benchmarked is contained within a code block following + this macro. + + For example: + + \snippet examples/qtestlib/tutorial5/benchmarking.cpp 0 + + \sa {QTestLib Manual#Creating a Benchmark}{Creating a Benchmark}, + {Chapter 5: Writing a Benchmark}{Writing a Benchmark} +*/ + +/*! \enum QTest::SkipMode + + This enum describes the modes for skipping tests during execution + of the test data. + + \value SkipSingle Skips the current entry in the test table; continues + execution of all the other entries in the table. + + \value SkipAll Skips all the entries in the test table; the test won't + be executed further. + + \sa QSKIP() +*/ + +/*! \enum QTest::TestFailMode + + This enum describes the modes for handling an expected failure of the + \l QVERIFY() or \l QCOMPARE() macros. + + \value Abort Aborts the execution of the test. Use this mode when it + doesn't make sense to execute the test any further after the + expected failure. + + \value Continue Continues execution of the test after the expected failure. + + \sa QEXPECT_FAIL() +*/ + +/*! \enum QTest::KeyAction + + This enum describes possible actions for key handling. + + \value Press The key is pressed. + \value Release The key is released. + \value Click The key is clicked (pressed and released). +*/ + +/*! \enum QTest::MouseAction + + This enum describes possible actions for mouse handling. + + \value MousePress A mouse button is pressed. + \value MouseRelease A mouse button is released. + \value MouseClick A mouse button is clicked (pressed and released). + \value MouseDClick A mouse button is double clicked (pressed and released twice). + \value MouseMove The mouse pointer has moved. +*/ + +/*! \fn void QTest::keyClick(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + \overload + + Simulates clicking of \a key with an optional \a modifier on a \a widget. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 13 + + The example above simulates clicking \c a on \c myWidget without + any keyboard modifiers and without delay of the test. + + \sa QTest::keyClicks() +*/ + +/*! \fn void QTest::keyClick(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + Simulates clicking of \a key with an optional \a modifier on a \a widget. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + + Examples: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 14 + + The first example above simulates clicking the \c escape key on \c + myWidget without any keyboard modifiers and without delay. The + second example simulates clicking \c shift-escape on \c myWidget + with a following 200 ms delay of the test. + + \sa QTest::keyClicks() +*/ + +/*! \fn void QTest::keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + Sends a Qt key event to \a widget with the given \a key and an associated \a action. Optionally, a keyboard \a modifier can be specified, as well as a \a delay (in milliseconds) of the test before sending the event. +*/ + +/*! \fn void QTest::keyEvent(KeyAction action, QWidget *widget, char ascii, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + \overload + + Sends a Qt key event to \a widget with the given key \a ascii and an associated \a action. Optionally, a keyboard \a modifier can be specified, as well as a \a delay (in milliseconds) of the test before sending the event. + +*/ + +/*! \fn void QTest::keyPress(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + Simulates pressing a \a key with an optional \a modifier on a \a widget. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + + \bold {Note:} At some point you should release the key using \l keyRelease(). + + \sa QTest::keyRelease(), QTest::keyClick() +*/ + +/*! \fn void QTest::keyPress(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + \overload + + Simulates pressing a \a key with an optional \a modifier on a \a widget. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + + \bold {Note:} At some point you should release the key using \l keyRelease(). + + \sa QTest::keyRelease(), QTest::keyClick() +*/ + +/*! \fn void QTest::keyRelease(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + Simulates releasing a \a key with an optional \a modifier on a \a widget. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + + \sa QTest::keyPress(), QTest::keyClick() +*/ + +/*! \fn void QTest::keyRelease(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + \overload + + Simulates releasing a \a key with an optional \a modifier on a \a widget. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + + \sa QTest::keyClick() +*/ + + +/*! \fn void QTest::keyClicks(QWidget *widget, const QString &sequence, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + + Simulates clicking a \a sequence of keys on a \a + widget. Optionally, a keyboard \a modifier can be specified as + well as a \a delay (in milliseconds) of the test before each key + click. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 15 + + The example above simulates clicking the sequence of keys + representing "hello world" on \c myWidget without any keyboard + modifiers and without delay of the test. + + \sa QTest::keyClick() +*/ + +/*! \fn void QTest::mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) + + Simulates pressing a mouse \a button with an optional \a modifier + on a \a widget. The position is defined by \a pos; the default + position is the center of the widget. If \a delay is specified, + the test will wait for the specified amount of milliseconds before + the press. + + \sa QTest::mouseRelease(), QTest::mouseClick() +*/ + +/*! \fn void QTest::mouseRelease(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) + + Simulates releasing a mouse \a button with an optional \a modifier + on a \a widget. The position of the release is defined by \a pos; + the default position is the center of the widget. If \a delay is + specified, the test will wait for the specified amount of + milliseconds before releasing the button. + + \sa QTest::mousePress(), QTest::mouseClick() +*/ + +/*! \fn void QTest::mouseClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) + + Simulates clicking a mouse \a button with an optional \a modifier + on a \a widget. The position of the click is defined by \a pos; + the default position is the center of the widget. If \a delay is + specified, the test will wait for the specified amount of + milliseconds before pressing and before releasing the button. + + \sa QTest::mousePress(), QTest::mouseRelease() +*/ + +/*! \fn void QTest::mouseDClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1) + + Simulates double clicking a mouse \a button with an optional \a + modifier on a \a widget. The position of the click is defined by + \a pos; the default position is the center of the widget. If \a + delay is specified, the test will wait for the specified amount of + milliseconds before each press and release. + + \sa QTest::mouseClick() +*/ + +/*! \fn void QTest::mouseMove(QWidget *widget, QPoint pos = QPoint(), int delay=-1) + + Moves the mouse pointer to a \a widget. If \a pos is not + specified, the mouse pointer moves to the center of the widget. If + a \a delay (in milliseconds) is given, the test will wait before + moving the mouse pointer. +*/ + +/*! + \fn char *QTest::toString(const T &value) + + Returns a textual representation of \a value. This function is used by + \l QCOMPARE() to output verbose information in case of a test failure. + + You can add specializations of this function to your test to enable + verbose output. + + \bold {Note:} The caller of toString() must delete the returned data + using \c{delete[]}. Your implementation should return a string + created with \c{new[]} or qstrdup(). + + Example: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 16 + + The example above defines a toString() specialization for a class + called \c MyPoint. Whenever a comparison of two instances of \c + MyPoint fails, \l QCOMPARE() will call this function to output the + contents of \c MyPoint to the test log. + + \sa QCOMPARE() +*/ + +/*! + \fn char *QTest::toString(const QLatin1String &string) + \overload + + Returns a textual representation of the given \a string. +*/ + +/*! + \fn char *QTest::toString(const QString &string) + \overload + + Returns a textual representation of the given \a string. +*/ + +/*! + \fn char *QTest::toString(const QByteArray &ba) + \overload + + Returns a textual representation of the byte array \a ba. + + \sa QTest::toHexRepresentation() +*/ + +/*! + \fn char *QTest::toString(const QTime &time) + \overload + + Returns a textual representation of the given \a time. +*/ + +/*! + \fn char *QTest::toString(const QDate &date) + \overload + + Returns a textual representation of the given \a date. +*/ + +/*! + \fn char *QTest::toString(const QDateTime &dateTime) + \overload + + Returns a textual representation of the date and time specified by + \a dateTime. +*/ + +/*! + \fn char *QTest::toString(const QChar &character) + \overload + + Returns a textual representation of the given \a character. +*/ + +/*! + \fn char *QTest::toString(const QPoint &point) + \overload + + Returns a textual representation of the given \a point. +*/ + +/*! + \fn char *QTest::toString(const QSize &size) + \overload + + Returns a textual representation of the given \a size. +*/ + +/*! + \fn char *QTest::toString(const QRect &rectangle) + \overload + + Returns a textual representation of the given \a rectangle. +*/ + +/*! + \fn char *QTest::toString(const QUrl &url) + \since 4.4 + \overload + + Returns a textual representation of the given \a url. +*/ + +/*! + \fn char *QTest::toString(const QPointF &point) + \overload + + Returns a textual representation of the given \a point. +*/ + +/*! + \fn char *QTest::toString(const QSizeF &size) + \overload + + Returns a textual representation of the given \a size. +*/ + +/*! + \fn char *QTest::toString(const QRectF &rectangle) + \overload + + Returns a textual representation of the given \a rectangle. +*/ + +/*! \fn void QTest::qWait(int ms) + + Waits for \a ms milliseconds. While waiting, events will be processed and + your test will stay responsive to user interface events or network communication. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 17 + + The code above will wait until the network server is responding for a + maximum of about 12.5 seconds. + + \sa QTest::qSleep() +*/ + +namespace QTest +{ + static QObject *currentTestObject = 0; + + struct TestFunction { + TestFunction():function(0), data(0) {} + ~TestFunction() { delete [] data; } + int function; + char *data; + } testFuncs[512]; + + /** + * Contains the count of test functions that was supplied + * on the command line, if any. Hence, if lastTestFuncIdx is + * more than zero, those functions should be run instead of + * all appearing in the test case. + */ + static int lastTestFuncIdx = -1; + + static int keyDelay = -1; + static int mouseDelay = -1; + static int eventDelay = -1; + static int keyVerbose = -1; + +/*! \internal + */ +int qt_snprintf(char *str, int size, const char *format, ...) +{ + va_list ap; + int res = 0; + + va_start(ap, format); + qvsnprintf(str, size, format, ap); + va_end(ap); + str[size - 1] = '\0'; + + char *idx = str; + while (*idx) { + if (((*idx < 0x20 && *idx != '\n' && *idx != '\t') || *idx > 0x7e)) + *idx = '?'; + ++idx; + } + return res; +} + +bool Q_TESTLIB_EXPORT defaultKeyVerbose() +{ + if (keyVerbose == -1) { + keyVerbose = qgetenv("QTEST_KEYEVENT_VERBOSE").constData() ? 1 : 0; + } + return keyVerbose == 1; +} + +int defaultEventDelay() +{ + if (eventDelay == -1) { + if (qgetenv("QTEST_EVENT_DELAY").constData()) + eventDelay = atoi(qgetenv("QTEST_EVENT_DELAY")); + else + eventDelay = 0; + } + return eventDelay; +} + +int Q_TESTLIB_EXPORT defaultMouseDelay() +{ + if (mouseDelay == -1) { + if (qgetenv("QTEST_MOUSEEVENT_DELAY").constData()) + mouseDelay = atoi((qgetenv("QTEST_MOUSEEVENT_DELAY"))); + else + mouseDelay = defaultEventDelay(); + } + return mouseDelay; +} + +int Q_TESTLIB_EXPORT defaultKeyDelay() +{ + if (keyDelay == -1) { + if (qgetenv("QTEST_KEYEVENT_DELAY").constData()) + keyDelay = atoi(qgetenv("QTEST_KEYEVENT_DELAY").constData()); + else + keyDelay = defaultEventDelay(); + } + return keyDelay; +} + +static bool isValidSlot(const QMetaMethod &sl) +{ + if (sl.access() != QMetaMethod::Private || !sl.parameterTypes().isEmpty() + || qstrlen(sl.typeName()) || sl.methodType() != QMetaMethod::Slot) + return false; + const char *sig = sl.signature(); + int len = qstrlen(sig); + if (len < 2) + return false; + if (sig[len - 2] != '(' || sig[len - 1] != ')') + return false; + if (len > 7 && strcmp(sig + (len - 7), "_data()") == 0) + return false; + if (strcmp(sig, "initTestCase()") == 0 || strcmp(sig, "cleanupTestCase()") == 0 + || strcmp(sig, "cleanup()") == 0 || strcmp(sig, "init()") == 0) + return false; + return true; +} + +static void qPrintTestSlots() +{ + for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) { + QMetaMethod sl = QTest::currentTestObject->metaObject()->method(i); + if (isValidSlot(sl)) + printf("%s\n", sl.signature()); + } +} + +static int qToInt(char *str) +{ + char *pEnd; + int l = (int)strtol(str, &pEnd, 10); + if (*pEnd != 0) { + printf("Invalid numeric parameter: '%s'\n", str); + exit(1); + } + return l; +} + +static void qParseArgs(int argc, char *argv[]) +{ + lastTestFuncIdx = -1; + + const char *testOptions = + " options:\n" + " -functions : Returns a list of current testfunctions\n" + " -xml : Outputs results as XML document\n" + " -lightxml : Outputs results as stream of XML tags\n" + " -o filename: Writes all output into a file\n" + " -silent : Only outputs warnings and failures\n" + " -v1 : Print enter messages for each testfunction\n" + " -v2 : Also print out each QVERIFY/QCOMPARE/QTEST\n" + " -vs : Print every signal emitted\n" + " -eventdelay ms : Set default delay for mouse and keyboard simulation to ms milliseconds\n" + " -keydelay ms : Set default delay for keyboard simulation to ms milliseconds\n" + " -mousedelay ms : Set default delay for mouse simulation to ms milliseconds\n" + " -keyevent-verbose : Turn on verbose messages for keyboard simulation\n" + " -maxwarnings n : Sets the maximum amount of messages to output.\n" + " 0 means unlimited, default: 2000\n" + "\n" + " Benchmark related options:\n" +#ifdef QTESTLIB_USE_VALGRIND + " -callgrind : Use callgrind to time benchmarks\n" +#endif +#ifdef HAVE_TICK_COUNTER + " -tickcounter : Use CPU tick counters to time benchmarks\n" +#endif + " -eventcounter : Counts events received during benchmarks\n" + " -minimumvalue n : Sets the minimum acceptable measurement value\n" + " -iterations n : Sets the number of accumulation iterations.\n" + " -median n : Sets the number of median iterations.\n" + " -vb : Print out verbose benchmarking information.\n" +#ifndef QT_NO_PROCESS +// Will be enabled when tools are integrated. +// " -chart : Runs the chart generator after the test. No output is printed to the console\n" +#endif + "\n" + " -help : This help\n"; + + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0 + || strcmp(argv[i], "/?") == 0) { + printf(" Usage: %s [options] [testfunction[:testdata]]...\n" + " By default, all testfunctions will be run.\n\n" + "%s", argv[0], testOptions); + exit(0); + } else if (strcmp(argv[i], "-functions") == 0) { + qPrintTestSlots(); + exit(0); + } else if (strcmp(argv[i], "-xml") == 0) { + QTestLog::setLogMode(QTestLog::XML); + } else if (strcmp(argv[i], "-lightxml") == 0) { + QTestLog::setLogMode(QTestLog::LightXML); + } else if (strcmp(argv[i], "-silent") == 0) { + QTestLog::setVerboseLevel(-1); + } else if (strcmp(argv[i], "-v1") == 0) { + QTestLog::setVerboseLevel(1); + } else if (strcmp(argv[i], "-v2") == 0) { + QTestLog::setVerboseLevel(2); + } else if (strcmp(argv[i], "-vs") == 0) { + QSignalDumper::startDump(); + } else if (strcmp(argv[i], "-o") == 0) { + if (i + 1 >= argc) { + printf("-o needs an extra parameter specifying the filename\n"); + exit(1); + } else { + QTestLog::redirectOutput(argv[++i]); + } + } else if (strcmp(argv[i], "-eventdelay") == 0) { + if (i + 1 >= argc) { + printf("-eventdelay needs an extra parameter to indicate the delay(ms)\n"); + exit(1); + } else { + QTest::eventDelay = qToInt(argv[++i]); + } + } else if (strcmp(argv[i], "-keydelay") == 0) { + if (i + 1 >= argc) { + printf("-keydelay needs an extra parameter to indicate the delay(ms)\n"); + exit(1); + } else { + QTest::keyDelay = qToInt(argv[++i]); + } + } else if (strcmp(argv[i], "-mousedelay") == 0) { + if (i + 1 >= argc) { + printf("-mousedelay needs an extra parameter to indicate the delay(ms)\n"); + exit(1); + } else { + QTest::mouseDelay = qToInt(argv[++i]); + } + } else if (strcmp(argv[i], "-maxwarnings") == 0) { + if (i + 1 >= argc) { + printf("-maxwarnings needs an extra parameter with the amount of warnings\n"); + exit(1); + } else { + QTestLog::setMaxWarnings(qToInt(argv[++i])); + } + } else if (strcmp(argv[i], "-keyevent-verbose") == 0) { + QTest::keyVerbose = 1; +#ifdef QTESTLIB_USE_VALGRIND + } else if (strcmp(argv[i], "-callgrind") == 0) { + if (QBenchmarkValgrindUtils::haveValgrind()) + if (QFileInfo(QDir::currentPath()).isWritable()) { + QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindParentProcess); + } else { + printf("WARNING: Current directory not writable. Using the walltime measurer.\n"); + } + else { + printf("WARNING: Valgrind not found or too old. Make sure it is installed and in your path. " + "Using the walltime measurer.\n"); + } + } else if (strcmp(argv[i], "-callgrindchild") == 0) { // "private" option + QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindChildProcess); + QBenchmarkGlobalData::current->callgrindOutFileBase = + QBenchmarkValgrindUtils::outFileBase(); +#endif +#ifdef HAVE_TICK_COUNTER + } else if (strcmp(argv[i], "-tickcounter") == 0) { + QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::TickCounter); +#endif + } else if (strcmp(argv[i], "-eventcounter") == 0) { + QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::EventCounter); + } else if (strcmp(argv[i], "-minimumvalue") == 0) { + if (i + 1 >= argc) { + printf("-minimumvalue needs an extra parameter to indicate the minimum time(ms)\n"); + exit(1); + } else { + QBenchmarkGlobalData::current->walltimeMinimum = qToInt(argv[++i]); + } + } else if (strcmp(argv[i], "-iterations") == 0) { + if (i + 1 >= argc) { + printf("-iterations needs an extra parameter to indicate the number of iterations\n"); + exit(1); + } else { + QBenchmarkGlobalData::current->iterationCount = qToInt(argv[++i]); + } + } else if (strcmp(argv[i], "-median") == 0) { + if (i + 1 >= argc) { + printf("-median needs an extra parameter to indicate the number of median iterations\n"); + exit(1); + } else { + QBenchmarkGlobalData::current->medianIterationCount = qToInt(argv[++i]); + } + + } else if (strcmp(argv[i], "-vb") == 0) { + QBenchmarkGlobalData::current->verboseOutput = true; +#ifndef QT_NO_PROCESS + } else if (strcmp(argv[i], "-chart") == 0) { + QBenchmarkGlobalData::current->createChart = true; + QTestLog::setLogMode(QTestLog::XML); + QTestLog::redirectOutput("results.xml"); +#endif + } else if (strcmp(argv[i], "-qws") == 0) { + // do nothing + } else if (argv[i][0] == '-') { + printf("Unknown option: '%s'\n\n%s", argv[i], testOptions); + exit(1); + } else { + int colon = -1; + char buf[512], *data=0; + int off; + for(off = 0; *(argv[i]+off); ++off) { + if (*(argv[i]+off) == ':') { + colon = off; + break; + } + } + if(colon != -1) { + data = qstrdup(argv[i]+colon+1); + } + QTest::qt_snprintf(buf, qMin(512, off + 1), "%s", argv[i]); // copy text before the ':' into buf + QTest::qt_snprintf(buf + off, qMin(512 - off, 3), "()"); // append "()" + int idx = QTest::currentTestObject->metaObject()->indexOfMethod(buf); + if (idx < 0 || !isValidSlot(QTest::currentTestObject->metaObject()->method(idx))) { + printf("Unknown testfunction: '%s'\n", buf); + printf("Available testfunctions:\n"); + qPrintTestSlots(); + exit(1); + } + ++QTest::lastTestFuncIdx; + QTest::testFuncs[QTest::lastTestFuncIdx].function = idx; + QTest::testFuncs[QTest::lastTestFuncIdx].data = data; + QTEST_ASSERT(QTest::lastTestFuncIdx < 512); + } + } +} + +QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container) +{ + const int count = container.count(); + if (count == 0) + return QBenchmarkResult(); + + if (count == 1) + return container.at(0); + + QList<QBenchmarkResult> containerCopy = container; + qSort(containerCopy); + + const int middle = count / 2; + + // ### handle even-sized containers here by doing an aritmetic mean of the two middle items. + return containerCopy.at(middle); +} + +struct QTestDataSetter +{ + QTestDataSetter(QTestData *data) + { + QTestResult::setCurrentTestData(data); + } + ~QTestDataSetter() + { + QTestResult::setCurrentTestData(0); + } +}; + +static void qInvokeTestMethodDataEntry(char *slot) +{ + /* Benchmarking: for each median iteration*/ + + int i = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0; + + QList<QBenchmarkResult> results; + do { + QBenchmarkTestMethodData::current->beginDataRun(); + + /* Benchmarking: for each accumulation iteration*/ + bool invokeOk; + do { + QTestResult::setCurrentTestLocation(QTestResult::InitFunc); + QMetaObject::invokeMethod(QTest::currentTestObject, "init"); + if (QTestResult::skipCurrentTest()) + break; + + QTestResult::setCurrentTestLocation(QTestResult::Func); + + QBenchmarkTestMethodData::current->result = QBenchmarkResult(); + QBenchmarkTestMethodData::current->resultAccepted = false; + + QBenchmarkGlobalData::current->context.tag = + QLatin1String( + QTestResult::currentDataTag() + ? QTestResult::currentDataTag() : ""); + + invokeOk = QMetaObject::invokeMethod(QTest::currentTestObject, slot, + Qt::DirectConnection); + if (!invokeOk) + QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__); + + QTestResult::setCurrentTestLocation(QTestResult::CleanupFunc); + QMetaObject::invokeMethod(QTest::currentTestObject, "cleanup"); + QTestResult::setCurrentTestLocation(QTestResult::NoWhere); + + // If this test method has a benchmark, repeat until all measurements are + // acceptable. + // The QBENCHMARK macro increases the number of iterations for each run until + // this happens. + } while (invokeOk + && QBenchmarkTestMethodData::current->isBenchmark() + && QBenchmarkTestMethodData::current->resultsAccepted() == false); + + QBenchmarkTestMethodData::current->endDataRun(); + if (i > -1) // iteration -1 is the warmup iteration. + results.append(QBenchmarkTestMethodData::current->result); + + if (QBenchmarkTestMethodData::current->isBenchmark() && + QBenchmarkGlobalData::current->verboseOutput) { + if (i == -1) { + qDebug() << "warmup stage result :" << QBenchmarkTestMethodData::current->result.value; + } else { + qDebug() << "accumulation stage result:" << QBenchmarkTestMethodData::current->result.value; + } + } + } while (QBenchmarkTestMethodData::current->isBenchmark() + && (++i < QBenchmarkGlobalData::current->adjustMedianIterationCount())); + + if (QBenchmarkTestMethodData::current->isBenchmark() + && QBenchmarkTestMethodData::current->resultsAccepted()) + QTestLog::addBenchmarkResult(qMedian(results)); +} + +/*! + \internal + + Call init(), slot_data(), slot(), slot(), slot()..., cleanup() + If data is set then it is the only test that is performed + + If the function was successfully called, true is returned, otherwise + false. + */ +static bool qInvokeTestMethod(const char *slotName, const char *data=0) +{ + QTEST_ASSERT(slotName); + + QBenchmarkTestMethodData benchmarkData; + QBenchmarkTestMethodData::current = &benchmarkData; + + QBenchmarkGlobalData::current->context.slotName = QLatin1String(slotName); + + char member[512]; + QTestTable table; + + char *slot = qstrdup(slotName); + slot[strlen(slot) - 2] = '\0'; + QTestResult::setCurrentTestFunction(slot); + + const QTestTable *gTable = QTestTable::globalTestTable(); + const int globalDataCount = gTable->dataCount(); + int curGlobalDataIndex = 0; + + /* For each test function that has a *_data() table/function, do: */ + do { + if (!gTable->isEmpty()) + QTestResult::setCurrentGlobalTestData(gTable->testData(curGlobalDataIndex)); + + if (curGlobalDataIndex == 0) { + QTestResult::setCurrentTestLocation(QTestResult::DataFunc); + QTest::qt_snprintf(member, 512, "%s_data", slot); + QMetaObject::invokeMethod(QTest::currentTestObject, member, Qt::DirectConnection); + // if we encounter a SkipAll in the _data slot, we skip the whole + // testfunction, no matter how much global data exists + if (QTestResult::skipCurrentTest()) { + QTestResult::setCurrentGlobalTestData(0); + break; + } + } + + bool foundFunction = false; + if (!QTestResult::skipCurrentTest()) { + int curDataIndex = 0; + const int dataCount = table.dataCount(); + QTestResult::setSkipCurrentTest(false); + + /* For each entry in the data table, do: */ + do { + if (!data || !qstrcmp(data, table.testData(curDataIndex)->dataTag())) { + foundFunction = true; + QTestDataSetter s(table.isEmpty() ? static_cast<QTestData *>(0) + : table.testData(curDataIndex)); + + qInvokeTestMethodDataEntry(slot); + + if (QTestResult::skipCurrentTest()) + // check whether SkipAll was requested + break; + if (data) + break; + } + ++curDataIndex; + } while (curDataIndex < dataCount); + } + + if (data && !foundFunction) { + printf("Unknown testdata for function %s: '%s'\n", slotName, data); + printf("Available testdata:\n"); + for(int i = 0; i < table.dataCount(); ++i) + printf("%s\n", table.testData(i)->dataTag()); + return false; + } + + QTestResult::setCurrentGlobalTestData(0); + ++curGlobalDataIndex; + } while (curGlobalDataIndex < globalDataCount); + + QTestResult::finishedCurrentTestFunction(); + QTestResult::setSkipCurrentTest(false); + QTestResult::setCurrentTestData(0); + delete[] slot; + + return true; +} + +void *fetchData(QTestData *data, const char *tagName, int typeId) +{ + QTEST_ASSERT(typeId); + QTEST_ASSERT_X(data, "QTest::fetchData()", "Test data requested, but no testdata available."); + QTEST_ASSERT(data->parent()); + + int idx = data->parent()->indexOf(tagName); + + if (idx == -1 || idx >= data->dataCount()) { + qFatal("QFETCH: Requested testdata '%s' not available, check your _data function.", + tagName); + } + + if (typeId != data->parent()->elementTypeId(idx)) { + qFatal("Requested type '%s' does not match available type '%s'.", + QMetaType::typeName(typeId), + QMetaType::typeName(data->parent()->elementTypeId(idx))); + } + + return data->data(idx); +} + +/*! + \fn char* QTest::toHexRepresentation(const char *ba, int length) + + Returns a pointer to a string that is the string \a ba represented + as a space-separated sequence of hex characters. If the input is + considered too long, it is truncated. A trucation is indicated in + the returned string as an ellipsis at the end. + + \a length is the length of the string \a ba. + */ +char *toHexRepresentation(const char *ba, int length) +{ + if(length == 0) + return qstrdup(""); + + /* We output at maximum about maxLen characters in order to avoid + * running out of memory and flooding things when the byte array + * is large. + * + * maxLen can't be for example 200 because QTestLib is sprinkled with fixed + * size char arrays. + * */ + const int maxLen = 50; + const int len = qMin(maxLen, length); + char *result = 0; + + if(length > maxLen) { + const int size = len * 3 + 4; + result = new char[size]; + + char *const forElipsis = result + size - 5; + forElipsis[0] = ' '; + forElipsis[1] = '.'; + forElipsis[2] = '.'; + forElipsis[3] = '.'; + result[size - 1] = '\0'; + } + else { + const int size = len * 3; + result = new char[size]; + result[size - 1] = '\0'; + } + + const char toHex[] = "0123456789ABCDEF"; + int i = 0; + int o = 0; + + while(true) { + const char at = ba[i]; + + result[o] = toHex[(at >> 4) & 0x0F]; + ++o; + result[o] = toHex[at & 0x0F]; + + ++i; + ++o; + if(i == len) + break; + else { + result[o] = ' '; + ++o; + } + } + + return result; +} + +static void qInvokeTestMethods(QObject *testObject) +{ + const QMetaObject *metaObject = testObject->metaObject(); + QTEST_ASSERT(metaObject); + + QTestLog::startLogging(); + + QTestResult::setCurrentTestFunction("initTestCase"); + QTestResult::setCurrentTestLocation(QTestResult::DataFunc); + QTestTable::globalTestTable(); + QMetaObject::invokeMethod(testObject, "initTestCase_data", Qt::DirectConnection); + + if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) { + QTestResult::setCurrentTestLocation(QTestResult::InitFunc); + QMetaObject::invokeMethod(testObject, "initTestCase"); + + // finishedCurrentTestFunction() resets QTestResult::testFailed(), so use a local copy. + const bool previousFailed = QTestResult::testFailed(); + QTestResult::finishedCurrentTestFunction(); + + if(!QTestResult::skipCurrentTest() && !previousFailed) { + + if (lastTestFuncIdx >= 0) { + for (int i = 0; i <= lastTestFuncIdx; ++i) { + if (!qInvokeTestMethod(metaObject->method(testFuncs[i].function).signature(), + testFuncs[i].data)) + break; + } + } else { + int methodCount = metaObject->methodCount(); + for (int i = 0; i < methodCount; ++i) { + QMetaMethod slotMethod = metaObject->method(i); + if (!isValidSlot(slotMethod)) + continue; + if (!qInvokeTestMethod(slotMethod.signature())) + break; + } + } + } + + QTestResult::setSkipCurrentTest(false); + QTestResult::setCurrentTestFunction("cleanupTestCase"); + QMetaObject::invokeMethod(testObject, "cleanupTestCase"); + } + QTestResult::finishedCurrentTestFunction(); + QTestResult::setCurrentTestFunction(0); + QTestTable::clearGlobalTestTable(); + + QTestLog::stopLogging(); +} + +} // namespace + +/*! + Executes tests declared in \a testObject. In addition, the private slots + \c{initTestCase()}, \c{cleanupTestCase()}, \c{init()} and \c{cleanup()} + are executed if they exist. See \l{Creating a Test} for more details. + + Optionally, the command line arguments \a argc and \a argv can be provided. + For a list of recognized arguments, read \l {QTestLib Command Line Arguments}. + + For stand-alone tests, the convenience macro \l QTEST_MAIN() can + be used to declare a main method that parses the command line arguments + and executes the tests. + + Returns 0 if all tests passed. Returns a value other than 0 if tests failed + or in case of unhandled exceptions. The return value from this function is + also the exit code of the test application when the \l QTEST_MAIN() macro + is used. + + The following example will run all tests in \c MyFirstTestObject and + \c{MySecondTestObject}: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 18 + + Note: This function is not reentrant, only one test can run at a time. A + test that was executed with qExec() can't run another test via qExec() and + threads are not allowed to call qExec() simultaneously. + + If you have programatically created the arguments, as opposed to getting them + from the arguments in \c main(), it is likely of interest to use + QTest::qExec(QObject *, const QStringList &) since it is Unicode safe. + + \sa QTEST_MAIN() +*/ + +int QTest::qExec(QObject *testObject, int argc, char **argv) +{ + QBenchmarkGlobalData benchmarkData; + QBenchmarkGlobalData::current = &benchmarkData; + +#ifdef QTESTLIB_USE_VALGRIND + int callgrindChildExitCode = 0; +#endif + +#ifdef Q_WS_MAC + bool macNeedsActivate = qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0); +#ifdef QT_MAC_USE_COCOA + IOPMAssertionID powerID; +#endif +#endif +#ifndef QT_NO_EXCEPTIONS + try { +#endif + + #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); + #endif + +#ifdef Q_WS_MAC + // Starting with Qt 4.4, applications launched from the command line + // no longer get focus automatically. Since some tests might depend + // on this, call SetFrontProcess here to get the pre 4.4 behavior. + if (macNeedsActivate) { + ProcessSerialNumber psn = { 0, kCurrentProcess }; + SetFrontProcess(&psn); +# ifdef QT_MAC_USE_COCOA + IOReturn ok = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &powerID); + if (ok != kIOReturnSuccess) + macNeedsActivate = false; // no need to release the assertion on exit. +# else + UpdateSystemActivity(1); // Wake the display. +# endif + } +#endif + + QTestResult::reset(); + + QTEST_ASSERT(testObject); + QTEST_ASSERT(!currentTestObject); + currentTestObject = testObject; + + const QMetaObject *metaObject = testObject->metaObject(); + QTEST_ASSERT(metaObject); + + QTestResult::setCurrentTestObject(metaObject->className()); + qParseArgs(argc, argv); +#ifdef QTESTLIB_USE_VALGRIND + if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) { + const QStringList origAppArgs(QCoreApplication::arguments()); + if (!QBenchmarkValgrindUtils::rerunThroughCallgrind(origAppArgs, callgrindChildExitCode)) + return -1; + + QBenchmarkValgrindUtils::cleanup(); + + } else { +#endif + + qInvokeTestMethods(testObject); + +#ifdef QTESTLIB_USE_VALGRIND + } +#endif + + #ifndef QT_NO_EXCEPTIONS + } catch (...) { + QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__); + if (QTestResult::currentTestFunction()) { + QTestResult::finishedCurrentTestFunction(); + QTestResult::setCurrentTestFunction(0); + } + + QTestLog::stopLogging(); +#ifdef QT_MAC_USE_COCOA + if (macNeedsActivate) { + IOPMAssertionRelease(powerID); + } +#endif + #ifdef Q_OS_WIN + // rethrow exception to make debugging easier + throw; + #endif + return -1; + } + #endif + + currentTestObject = 0; +#ifdef QT_MAC_USE_COCOA + if (macNeedsActivate) { + IOPMAssertionRelease(powerID); + } +#endif + + +#ifndef QT_NO_PROCESS + if (QBenchmarkGlobalData::current->createChart) { + +#define XSTR(s) STR(s) +#define STR(s) #s +#ifdef Q_OS_WIN + const char * path = XSTR(QBENCHLIB_BASE) "/tools/generatereport/generatereport.exe"; +#else + const char * path = XSTR(QBENCHLIB_BASE) "/tools/generatereport/generatereport"; +#endif +#undef XSTR +#undef STR + + if (QFile::exists(QLatin1String(path))) { + QProcess p; + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.start(QLatin1String(path), QStringList() << QLatin1String("results.xml")); + p.waitForFinished(-1); + } else { + qWarning("Could not find %s, please make sure it is compiled.", path); + } + } +#endif + +#if defined(QTEST_NOEXITCODE) || (defined(QT_BUILD_INTERNAL) && !defined(QTEST_FORCE_EXITCODE)) + return 0; +#else + +#ifdef QTESTLIB_USE_VALGRIND + if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) + return callgrindChildExitCode; +#endif + // make sure our exit code is never going above 127 + // since that could wrap and indicate 0 test fails + return qMin(QTestResult::failCount(), 127); + +#endif +} + +/*! + \overload + \since 4.4 + + Behaves identically to qExec(QObject *, int, char**) but takes a + QStringList of \a arguments instead of a \c char** list. + */ +int QTest::qExec(QObject *testObject, const QStringList &arguments) +{ + const int argc = arguments.count(); + QVarLengthArray<char *> argv(argc); + + QVector<QByteArray> args; + args.reserve(argc); + + for(int i = 0; i < argc; ++i) + { + args.append(arguments.at(i).toLocal8Bit().constData()); + argv[i] = args.last().data(); + } + + return qExec(testObject, argc, argv.data()); +} + +/*! \internal + */ +void QTest::qFail(const char *statementStr, const char *file, int line) +{ + QTestResult::addFailure(statementStr, file, line); +} + +/*! \internal + */ +bool QTest::qVerify(bool statement, const char *statementStr, const char *description, + const char *file, int line) +{ + return QTestResult::verify(statement, statementStr, description, file, line); +} + +/*! \fn void QTest::qSkip(const char *message, SkipMode mode, const char *file, int line) +\internal + */ +void QTest::qSkip(const char *message, QTest::SkipMode mode, + const char *file, int line) +{ + QTestResult::addSkip(message, mode, file, line); + if (mode == QTest::SkipAll) + QTestResult::setSkipCurrentTest(true); +} + +/*! \fn bool QTest::qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line) +\internal + */ +bool QTest::qExpectFail(const char *dataIndex, const char *comment, + QTest::TestFailMode mode, const char *file, int line) +{ + return QTestResult::expectFail(dataIndex, qstrdup(comment), mode, file, line); +} + +/*! \internal + */ +void QTest::qWarn(const char *message) +{ + QTestLog::warn(message); +} + +/*! + Ignores messages created by qDebug() or qWarning(). If the \a message + with the corresponding \a type is outputted, it will be removed from the + test log. If the test finished and the \a message was not outputted, + a test failure is appended to the test log. + + \bold {Note:} Invoking this function will only ignore one message. + If the message you want to ignore is outputted twice, you have to + call ignoreMessage() twice, too. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 19 + + The example above tests that QDir::mkdir() outputs the right warning when invoked + with an invalid file name. +*/ +void QTest::ignoreMessage(QtMsgType type, const char *message) +{ + QTestResult::ignoreMessage(type, message); +} + +/*! \internal + */ +void *QTest::qData(const char *tagName, int typeId) +{ + return fetchData(QTestResult::currentTestData(), tagName, typeId); +} + +/*! \internal + */ +void *QTest::qGlobalData(const char *tagName, int typeId) +{ + return fetchData(QTestResult::currentGlobalTestData(), tagName, typeId); +} + +/*! \internal + */ +void *QTest::qElementData(const char *tagName, int metaTypeId) +{ + QTEST_ASSERT(tagName); + QTestData *data = QTestResult::currentTestData(); + QTEST_ASSERT(data); + QTEST_ASSERT(data->parent()); + + int idx = data->parent()->indexOf(tagName); + QTEST_ASSERT(idx != -1); + QTEST_ASSERT(data->parent()->elementTypeId(idx) == metaTypeId); + + return data->data(data->parent()->indexOf(tagName)); +} + +/*! \internal + */ +void QTest::addColumnInternal(int id, const char *name) +{ + QTestTable *tbl = QTestTable::currentTestTable(); + QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot."); + + tbl->addColumn(id, name); +} + +/*! + Appends a new row to the current test data. \a dataTag is the name of + the testdata that will appear in the test output. Returns a QTestData reference + that can be used to stream in data. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 20 + + \bold {Note:} This macro can only be used in a test's data function + that is invoked by the test framework. + + See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for + a more extensive example. + + \sa addColumn(), QFETCH() +*/ +QTestData &QTest::newRow(const char *dataTag) +{ + QTestTable *tbl = QTestTable::currentTestTable(); + QTEST_ASSERT_X(tbl, "QTest::addColumn()", "Cannot add testdata outside of a _data slot."); + + return *tbl->newData(dataTag); +} + +/*! \fn void QTest::addColumn(const char *name, T *dummy = 0) + + Adds a column with type \c{T} to the current test data. + \a name is the name of the column. \a dummy is a workaround + for buggy compilers and can be ignored. + + To populate the column with values, newRow() can be used. Use + \l QFETCH() to fetch the data in the actual test. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 21 + + To add custom types to the testdata, the type must be registered with + QMetaType via \l Q_DECLARE_METATYPE(). + + \bold {Note:} This macro can only be used in a test's data function + that is invoked by the test framework. + + See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for + a more extensive example. + + \sa QTest::newRow(), QFETCH(), QMetaType +*/ + +/*! + Returns the name of the test function that is currently executed. + + Example: + + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 22 +*/ +const char *QTest::currentTestFunction() +{ + return QTestResult::currentTestFunction(); +} + +/*! + Returns the name of the current test data. If the test doesn't + have any assigned testdata, the function returns 0. +*/ +const char *QTest::currentDataTag() +{ + return QTestResult::currentDataTag(); +} + +/*! + Returns true if the current test function failed, otherwise false. +*/ +bool QTest::currentTestFailed() +{ + return QTestResult::currentTestFailed(); +} + +/*! + Sleeps for \a ms milliseconds, blocking execution of the + test. qSleep() will not do any event processing and leave your test + unresponsive. Network communication might time out while + sleeping. Use \l qWait() to do non-blocking sleeping. + + \a ms must be greater than 0. + + \bold {Note:} The qSleep() function calls either \c nanosleep() on + unix or \c Sleep() on windows, so the accuracy of time spent in + qSleep() depends on the operating system. + + Example: + \snippet doc/src/snippets/code/src_qtestlib_qtestcase.cpp 23 + + \sa qWait() +*/ +void QTest::qSleep(int ms) +{ + QTEST_ASSERT(ms > 0); + +#ifdef Q_OS_WIN + Sleep(uint(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif +} + +/*! \internal + */ +QObject *QTest::testObject() +{ + return currentTestObject; +} + +/*! \internal + */ +bool QTest::compare_helper(bool success, const char *msg, const char *file, int line) +{ + return QTestResult::compare(success, msg, file, line); +} + +/*! \internal + */ +bool QTest::compare_helper(bool success, const char *msg, char *val1, char *val2, + const char *actual, const char *expected, const char *file, int line) +{ + return QTestResult::compare(success, msg, val1, val2, actual, expected, file, line); +} + +/*! \fn bool QTest::qCompare<float>(float const &t1, float const &t2, const char *actual, const char *expected, const char *file, int line) +\internal + */ +template <> +bool QTest::qCompare<float>(float const &t1, float const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + return qFuzzyCompare(t1, t2) + ? compare_helper(true, "COMPARE()", file, line) + : compare_helper(false, "Compared floats are not the same (fuzzy compare)", + toString(t1), toString(t2), actual, expected, file, line); +} + +/*! \fn bool QTest::qCompare<double>(double const &t1, double const &t2, const char *actual, const char *expected, const char *file, int line) +\internal + */ +template <> +bool QTest::qCompare<double>(double const &t1, double const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + return qFuzzyCompare(t1, t2) + ? compare_helper(true, "COMPARE()", file, line) + : compare_helper(false, "Compared doubles are not the same (fuzzy compare)", + toString(t1), toString(t2), actual, expected, file, line); +} + +#define COMPARE_IMPL2(TYPE, FORMAT) \ +template <> char *QTest::toString<TYPE >(const TYPE &t) \ +{ \ + char *msg = new char[128]; \ + qt_snprintf(msg, 128, #FORMAT, t); \ + return msg; \ +} + +COMPARE_IMPL2(short, %hd) +COMPARE_IMPL2(ushort, %hu) +COMPARE_IMPL2(int, %d) +COMPARE_IMPL2(uint, %u) +COMPARE_IMPL2(long, %ld) +COMPARE_IMPL2(ulong, %lu) +#if defined(Q_OS_WIN) +COMPARE_IMPL2(qint64, %I64d) +COMPARE_IMPL2(quint64, %I64u) +#else +COMPARE_IMPL2(qint64, %lld) +COMPARE_IMPL2(quint64, %llu) +#endif +COMPARE_IMPL2(bool, %d) +COMPARE_IMPL2(char, %c) +COMPARE_IMPL2(float, %g); +COMPARE_IMPL2(double, %lg); + +/*! \internal + */ +char *QTest::toString(const char *str) +{ + if (!str) + return 0; + char *msg = new char[strlen(str) + 1]; + return qstrcpy(msg, str); +} + +/*! \internal + */ +char *QTest::toString(const void *p) +{ + char *msg = new char[128]; + qt_snprintf(msg, 128, "%p", p); + return msg; +} + +/*! \internal + */ +bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual, + const char *expected, const char *file, int line) +{ + return (qstrcmp(t1, t2) == 0) + ? compare_helper(true, "COMPARE()", file, line) + : compare_helper(false, "Compared strings are not the same", + toString(t1), toString(t2), actual, expected, file, line); +} + +/*! \fn bool QTest::compare_ptr_helper(const void *t1, const void *t2, const char *actual, const char *expected, const char *file, int line); + \internal +*/ + +/*! \fn bool QTest::qCompare(T1 const &, T2 const &, const char *, const char *, const char *, int); + \internal +*/ + + +/*! \fn void QTest::mouseEvent(MouseAction action, QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1) + \internal +*/ + +/*! \fn bool QTest::qCompare(QIcon const &t1, QIcon const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(T const &t1, T const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(const T *t1, const T *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(T *t1, T *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(const T1 *t1, const T2 *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(T1 *t1, T2 *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(const char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(char *t1, char *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(char *t1, const char *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(const char *t1, char *t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(QString const &t1, QLatin1String const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(QLatin1String const &t1, QString const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(QStringList const &t1, QStringList const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(QFlags<T> const &t1, T const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qCompare(QFlags<T> const &t1, int const &t2, const char *actual, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn bool QTest::qTest(const T& actual, const char *elementName, const char *actualStr, const char *expected, const char *file, int line) + \internal +*/ + +/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, QString text, Qt::KeyboardModifiers modifier, int delay=-1) + \internal +*/ + +/*! \fn void QTest::sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, char ascii, Qt::KeyboardModifiers modifier, int delay=-1) + \internal +*/ + +/*! \fn void QTest::simulateEvent(QWidget *widget, bool press, int code, Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1) + \internal +*/ + +/*! \fn int QTest::qt_snprintf(char *str, int size, const char *format, ...) + \internal +*/ + +QT_END_NAMESPACE diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h new file mode 100644 index 0000000..4e5deff --- /dev/null +++ b/src/testlib/qtestcase.h @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTCASE_H +#define QTESTCASE_H + +#include <QtTest/qtest_global.h> + +#include <QtCore/qnamespace.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +#define QVERIFY(statement) \ +do {\ + if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ + return;\ +} while (0) + +#define QFAIL(message) \ +do {\ + QTest::qFail(message, __FILE__, __LINE__);\ + return;\ +} while (0) + +#define QVERIFY2(statement, description) \ +do {\ + if (statement) {\ + if (!QTest::qVerify(true, #statement, (description), __FILE__, __LINE__))\ + return;\ + } else {\ + if (!QTest::qVerify(false, #statement, (description), __FILE__, __LINE__))\ + return;\ + }\ +} while (0) + +#define QCOMPARE(actual, expected) \ +do {\ + if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ + return;\ +} while (0) + +#define QSKIP(statement, mode) \ +do {\ + QTest::qSkip(statement, QTest::mode, __FILE__, __LINE__);\ + return;\ +} while (0) + +#define QEXPECT_FAIL(dataIndex, comment, mode)\ +do {\ + if (!QTest::qExpectFail(dataIndex, comment, QTest::mode, __FILE__, __LINE__))\ + return;\ +} while (0) + +#define QFETCH(type, name)\ + type name = *static_cast<type *>(QTest::qData(#name, ::qMetaTypeId<type >())) + +#define QFETCH_GLOBAL(type, name)\ + type name = *static_cast<type *>(QTest::qGlobalData(#name, ::qMetaTypeId<type >())) + +#define DEPENDS_ON(funcName) + +#define QTEST(actual, testElement)\ +do {\ + if (!QTest::qTest(actual, testElement, #actual, #testElement, __FILE__, __LINE__))\ + return;\ +} while (0) + +#define QWARN(msg)\ + QTest::qWarn(msg) + +class QObject; +class QTestData; + +#define QTEST_COMPARE_DECL(KLASS)\ + template<> Q_TESTLIB_EXPORT char *toString<KLASS >(const KLASS &); + +namespace QTest +{ + template <typename T> + inline char *toString(const T &) + { + return 0; + } + + Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, int length); + Q_TESTLIB_EXPORT char *toString(const char *); + Q_TESTLIB_EXPORT char *toString(const void *); + + Q_TESTLIB_EXPORT int qExec(QObject *testObject, int argc = 0, char **argv = 0); + Q_TESTLIB_EXPORT int qExec(QObject *testObject, const QStringList &arguments); + + Q_TESTLIB_EXPORT bool qVerify(bool statement, const char *statementStr, const char *description, + const char *file, int line); + Q_TESTLIB_EXPORT void qFail(const char *statementStr, const char *file, int line); + Q_TESTLIB_EXPORT void qSkip(const char *message, SkipMode mode, const char *file, int line); + Q_TESTLIB_EXPORT bool qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, + const char *file, int line); + Q_TESTLIB_EXPORT void qWarn(const char *message); + Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const char *message); + + Q_TESTLIB_EXPORT void *qData(const char *tagName, int typeId); + Q_TESTLIB_EXPORT void *qGlobalData(const char *tagName, int typeId); + Q_TESTLIB_EXPORT void *qElementData(const char *elementName, int metaTypeId); + Q_TESTLIB_EXPORT QObject *testObject(); + + Q_TESTLIB_EXPORT const char *currentTestFunction(); + Q_TESTLIB_EXPORT const char *currentDataTag(); + Q_TESTLIB_EXPORT bool currentTestFailed(); + + Q_TESTLIB_EXPORT Qt::Key asciiToKey(char ascii); + Q_TESTLIB_EXPORT char keyToAscii(Qt::Key key); + + Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *msg, const char *file, + int line); + Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *msg, char *val1, char *val2, + const char *expected, const char *actual, + const char *file, int line); + Q_TESTLIB_EXPORT void qSleep(int ms); + Q_TESTLIB_EXPORT void addColumnInternal(int id, const char *name); + + template <typename T> + inline void addColumn(const char *name, T * = 0) + { + addColumnInternal(qMetaTypeId<T>(), name); + } + Q_TESTLIB_EXPORT QTestData &newRow(const char *dataTag); + + template <typename T> + inline bool qCompare(T const &t1, T const &t2, const char *actual, const char *expected, + const char *file, int line) + { + return (t1 == t2) + ? compare_helper(true, "COMPARE()", file, line) + : compare_helper(false, "Compared values are not the same", + toString<T>(t1), toString<T>(t2), actual, expected, file, line); + } + + template <> + Q_TESTLIB_EXPORT bool qCompare<float>(float const &t1, float const &t2, + const char *actual, const char *expected, const char *file, int line); + + template <> + Q_TESTLIB_EXPORT bool qCompare<double>(double const &t1, double const &t2, + const char *actual, const char *expected, const char *file, int line); + + inline bool compare_ptr_helper(const void *t1, const void *t2, const char *actual, + const char *expected, const char *file, int line) + { + return (t1 == t2) + ? compare_helper(true, "COMPARE()", file, line) + : compare_helper(false, "Compared pointers are not the same", + toString(t1), toString(t2), actual, expected, file, line); + } + + Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual, + const char *expected, const char *file, int line); + +#ifndef qdoc + QTEST_COMPARE_DECL(short) + QTEST_COMPARE_DECL(ushort) + QTEST_COMPARE_DECL(int) + QTEST_COMPARE_DECL(uint) + QTEST_COMPARE_DECL(long) + QTEST_COMPARE_DECL(ulong) + QTEST_COMPARE_DECL(qint64) + QTEST_COMPARE_DECL(quint64) + + QTEST_COMPARE_DECL(float) + QTEST_COMPARE_DECL(double) + QTEST_COMPARE_DECL(char) + QTEST_COMPARE_DECL(bool) +#endif + +#ifndef QTEST_NO_SPECIALIZATIONS + template <typename T1, typename T2> + bool qCompare(T1 const &, T2 const &, const char *, const char *, const char *, int); + +#if defined(QT_ARCH_WINDOWSCE) && defined(QT_COORD_TYPE) + template <> + inline bool qCompare<qreal, float>(qreal const &t1, float const &t2, const char *actual, + const char *expected, const char *file, int line) + { + return qCompare<qreal>(t1, qreal(t2), actual, expected, file, line); + } + + template <> + inline bool qCompare<float, qreal>(float const &t1, qreal const &t2, const char *actual, + const char *expected, const char *file, int line) + { + return qCompare<qreal>(qreal(t1), t2, actual, expected, file, line); + } + +#elif defined(QT_COORD_TYPE) || defined(QT_ARCH_ARM) || defined(QT_NO_FPU) || defined(QT_ARCH_WINDOWSCE) + template <> + inline bool qCompare<qreal, double>(qreal const &t1, double const &t2, const char *actual, + const char *expected, const char *file, int line) + { + return qCompare<float>(float(t1), float(t2), actual, expected, file, line); + } + + template <> + inline bool qCompare<double, qreal>(double const &t1, qreal const &t2, const char *actual, + const char *expected, const char *file, int line) + { + return qCompare<float>(float(t1), float(t2), actual, expected, file, line); + } + +#endif + + template <typename T> + inline bool qCompare(const T *t1, const T *t2, const char *actual, const char *expected, + const char *file, int line) + { + return compare_ptr_helper(t1, t2, actual, expected, file, line); + } + template <typename T> + inline bool qCompare(T *t1, T *t2, const char *actual, const char *expected, + const char *file, int line) + { + return compare_ptr_helper(t1, t2, actual, expected, file, line); + } + + template <typename T1, typename T2> + inline bool qCompare(const T1 *t1, const T2 *t2, const char *actual, const char *expected, + const char *file, int line) + { + return compare_ptr_helper(t1, static_cast<const T1 *>(t2), actual, expected, file, line); + } + template <typename T1, typename T2> + inline bool qCompare(T1 *t1, T2 *t2, const char *actual, const char *expected, + const char *file, int line) + { + return compare_ptr_helper(const_cast<const T1 *>(t1), + static_cast<const T1 *>(const_cast<const T2 *>(t2)), actual, expected, file, line); + } + template<> + inline bool qCompare<char>(const char *t1, const char *t2, const char *actual, + const char *expected, const char *file, int line) + { + return compare_string_helper(t1, t2, actual, expected, file, line); + } + template<> + inline bool qCompare<char>(char *t1, char *t2, const char *actual, const char *expected, + const char *file, int line) + { + return compare_string_helper(t1, t2, actual, expected, file, line); + } +#else /* QTEST_NO_SPECIALIZATIONS */ + inline bool qCompare(const char *t1, const char *t2, const char *actual, + const char *expected, const char *file, int line) + { + return compare_string_helper(t1, t2, actual, expected, file, line); + } + + inline bool qCompare(char *t1, char *t2, const char *actual, const char *expected, + const char *file, int line) + { + return compare_string_helper(t1, t2, actual, expected, file, line); + } +#endif + + /* The next two specializations are for MSVC that shows problems with implicit + conversions + */ +#ifndef QTEST_NO_SPECIALIZATIONS + template<> +#endif + inline bool qCompare(char *t1, const char *t2, const char *actual, + const char *expected, const char *file, int line) + { + return compare_string_helper(t1, t2, actual, expected, file, line); + } +#ifndef QTEST_NO_SPECIALIZATIONS + template<> +#endif + inline bool qCompare(const char *t1, char *t2, const char *actual, + const char *expected, const char *file, int line) + { + return compare_string_helper(t1, t2, actual, expected, file, line); + } + + template <class T> + inline bool qTest(const T& actual, const char *elementName, const char *actualStr, + const char *expected, const char *file, int line) + { + return qCompare(actual, *static_cast<const T *>(QTest::qElementData(elementName, + qMetaTypeId<T>())), actualStr, expected, file, line); + } +} + +#undef QTEST_COMPARE_DECL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestdata.cpp b/src/testlib/qtestdata.cpp new file mode 100644 index 0000000..3f766b4 --- /dev/null +++ b/src/testlib/qtestdata.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qmetaobject.h> + +#include "QtTest/qtestassert.h" +#include "QtTest/qtestdata.h" +#include "QtTest/private/qtesttable_p.h" + +#include <string.h> +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +class QTestDataPrivate +{ +public: + QTestDataPrivate(): tag(0), parent(0), data(0), dataCount(0) {} + + char *tag; + QTestTable *parent; + void **data; + int dataCount; +}; + +QTestData::QTestData(const char *tag, QTestTable *parent) +{ + QTEST_ASSERT(tag); + QTEST_ASSERT(parent); + d = new QTestDataPrivate; + d->tag = qstrdup(tag); + d->parent = parent; + d->data = new void *[parent->elementCount()]; + memset(d->data, 0, parent->elementCount() * sizeof(void*)); +} + +QTestData::~QTestData() +{ + for (int i = 0; i < d->dataCount; ++i) { + if (d->data[i]) + QMetaType::destroy(d->parent->elementTypeId(i), d->data[i]); + } + delete [] d->data; + delete [] d->tag; + delete d; +} + +void QTestData::append(int type, const void *data) +{ + QTEST_ASSERT(d->dataCount < d->parent->elementCount()); + if (d->parent->elementTypeId(d->dataCount) != type) { + qDebug("expected data of type '%s', got '%s' for element %d of data with tag '%s'", + QMetaType::typeName(d->parent->elementTypeId(d->dataCount)), + QMetaType::typeName(type), + d->dataCount, d->tag); + QTEST_ASSERT(false); + } + d->data[d->dataCount] = QMetaType::construct(type, data); + ++d->dataCount; +} + +void *QTestData::data(int index) const +{ + QTEST_ASSERT(index >= 0); + QTEST_ASSERT(index < d->parent->elementCount()); + return d->data[index]; +} + +QTestTable *QTestData::parent() const +{ + return d->parent; +} + +const char *QTestData::dataTag() const +{ + return d->tag; +} + +int QTestData::dataCount() const +{ + return d->dataCount; +} + +QT_END_NAMESPACE diff --git a/src/testlib/qtestdata.h b/src/testlib/qtestdata.h new file mode 100644 index 0000000..b673bb9 --- /dev/null +++ b/src/testlib/qtestdata.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTDATA_H +#define QTESTDATA_H + +#include <QtTest/qtest_global.h> + +#include <QtCore/qmetatype.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +class QTestTable; +class QTestDataPrivate; + +class Q_TESTLIB_EXPORT QTestData +{ +public: + ~QTestData(); + + void append(int type, const void *data); + void *data(int index) const; + const char *dataTag() const; + QTestTable *parent() const; + int dataCount() const; + +private: + friend class QTestTable; + QTestData(const char *tag = 0, QTestTable *parent = 0); + + Q_DISABLE_COPY(QTestData) + + QTestDataPrivate *d; +}; + +template<typename T> +QTestData &operator<<(QTestData &data, const T &value) +{ + data.append(qMetaTypeId<T>(), &value); + return data; +} + +inline QTestData &operator<<(QTestData &data, const char * value) +{ + QString str = QString::fromAscii(value); + data.append(QMetaType::QString, &str); + return data; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestevent.h b/src/testlib/qtestevent.h new file mode 100644 index 0000000..1eefd7b --- /dev/null +++ b/src/testlib/qtestevent.h @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTEVENT_H +#define QTESTEVENT_H + +#if 0 +// inform syncqt +#pragma qt_no_master_include +#endif + +#include <QtTest/qtest_global.h> +#include <QtTest/qtestkeyboard.h> +#include <QtTest/qtestmouse.h> +#include <QtTest/qtestsystem.h> + +#include <QtCore/qlist.h> + +#include <stdlib.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +class QTestEvent +{ +public: + virtual void simulate(QWidget *w) = 0; + virtual QTestEvent *clone() const = 0; + + virtual ~QTestEvent() {} +}; + +class QTestKeyEvent: public QTestEvent +{ +public: + inline QTestKeyEvent(QTest::KeyAction action, Qt::Key key, Qt::KeyboardModifiers modifiers, int delay) + : _action(action), _delay(delay), _modifiers(modifiers), _ascii(0), _key(key) {} + inline QTestKeyEvent(QTest::KeyAction action, char ascii, Qt::KeyboardModifiers modifiers, int delay) + : _action(action), _delay(delay), _modifiers(modifiers), + _ascii(ascii), _key(Qt::Key_unknown) {} + inline QTestEvent *clone() const { return new QTestKeyEvent(*this); } + + inline void simulate(QWidget *w) + { + if (_ascii == 0) + QTest::keyEvent(_action, w, _key, _modifiers, _delay); + else + QTest::keyEvent(_action, w, _ascii, _modifiers, _delay); + } + +protected: + QTest::KeyAction _action; + int _delay; + Qt::KeyboardModifiers _modifiers; + char _ascii; + Qt::Key _key; +}; + +class QTestKeyClicksEvent: public QTestEvent +{ +public: + inline QTestKeyClicksEvent(const QString &keys, Qt::KeyboardModifiers modifiers, int delay) + : _keys(keys), _modifiers(modifiers), _delay(delay) {} + inline QTestEvent *clone() const { return new QTestKeyClicksEvent(*this); } + + inline void simulate(QWidget *w) + { + QTest::keyClicks(w, _keys, _modifiers, _delay); + } + +private: + QString _keys; + Qt::KeyboardModifiers _modifiers; + int _delay; +}; + +class QTestMouseEvent: public QTestEvent +{ +public: + inline QTestMouseEvent(QTest::MouseAction action, Qt::MouseButton button, + Qt::KeyboardModifiers modifiers, QPoint position, int delay) + : _action(action), _button(button), _modifiers(modifiers), _pos(position), _delay(delay) {} + inline QTestEvent *clone() const { return new QTestMouseEvent(*this); } + + inline void simulate(QWidget *w) + { + QTest::mouseEvent(_action, w, _button, _modifiers, _pos, _delay); + } + +private: + QTest::MouseAction _action; + Qt::MouseButton _button; + Qt::KeyboardModifiers _modifiers; + QPoint _pos; + int _delay; +}; + +class QTestDelayEvent: public QTestEvent +{ +public: + inline QTestDelayEvent(int msecs): _delay(msecs) {} + inline QTestEvent *clone() const { return new QTestDelayEvent(*this); } + + inline void simulate(QWidget * /*w*/) { QTest::qWait(_delay); } + +private: + int _delay; +}; + +class QTestEventList: public QList<QTestEvent *> +{ +public: + inline QTestEventList() {} + inline QTestEventList(const QTestEventList &other): QList<QTestEvent *>() + { for (int i = 0; i < other.count(); ++i) append(other.at(i)->clone()); } + inline ~QTestEventList() + { clear(); } + inline void clear() + { qDeleteAll(*this); QList<QTestEvent *>::clear(); } + + inline void addKeyClick(Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { addKeyEvent(QTest::Click, qtKey, modifiers, msecs); } + inline void addKeyPress(Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { addKeyEvent(QTest::Press, qtKey, modifiers, msecs); } + inline void addKeyRelease(Qt::Key qtKey, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { addKeyEvent(QTest::Release, qtKey, modifiers, msecs); } + inline void addKeyEvent(QTest::KeyAction action, Qt::Key qtKey, + Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { append(new QTestKeyEvent(action, qtKey, modifiers, msecs)); } + + inline void addKeyClick(char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { addKeyEvent(QTest::Click, ascii, modifiers, msecs); } + inline void addKeyPress(char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { addKeyEvent(QTest::Press, ascii, modifiers, msecs); } + inline void addKeyRelease(char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { addKeyEvent(QTest::Release, ascii, modifiers, msecs); } + inline void addKeyClicks(const QString &keys, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { append(new QTestKeyClicksEvent(keys, modifiers, msecs)); } + inline void addKeyEvent(QTest::KeyAction action, char ascii, Qt::KeyboardModifiers modifiers = Qt::NoModifier, int msecs = -1) + { append(new QTestKeyEvent(action, ascii, modifiers, msecs)); } + + inline void addMousePress(Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { append(new QTestMouseEvent(QTest::MousePress, button, stateKey, pos, delay)); } + inline void addMouseRelease(Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { append(new QTestMouseEvent(QTest::MouseRelease, button, stateKey, pos, delay)); } + inline void addMouseClick(Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { append(new QTestMouseEvent(QTest::MouseClick, button, stateKey, pos, delay)); } + inline void addMouseDClick(Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { append(new QTestMouseEvent(QTest::MouseDClick, button, stateKey, pos, delay)); } + inline void addMouseMove(QPoint pos = QPoint(), int delay=-1) + { append(new QTestMouseEvent(QTest::MouseMove, Qt::NoButton, 0, pos, delay)); } + + inline void addDelay(int msecs) + { append(new QTestDelayEvent(msecs)); } + + inline void simulate(QWidget *w) + { + for (int i = 0; i < count(); ++i) + at(i)->simulate(w); + } +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QTestEventList) + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtesteventloop.h b/src/testlib/qtesteventloop.h new file mode 100644 index 0000000..2a675d2 --- /dev/null +++ b/src/testlib/qtesteventloop.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTEVENTLOOP_H +#define QTESTEVENTLOOP_H + +#include <QtTest/qtest_global.h> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qeventloop.h> +#include <QtCore/qobject.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +class Q_TESTLIB_EXPORT QTestEventLoop : public QObject +{ + Q_OBJECT + +public: + inline QTestEventLoop(QObject *aParent = 0) + : QObject(aParent), inLoop(false), _timeout(false), timerId(-1), loop(0) {} + inline void enterLoop(int secs); + + + inline void changeInterval(int secs) + { killTimer(timerId); timerId = startTimer(secs * 1000); } + + inline bool timeout() const + { return _timeout; } + + inline static QTestEventLoop &instance() + { + static QPointer<QTestEventLoop> testLoop; + if (testLoop.isNull()) + testLoop = new QTestEventLoop(QCoreApplication::instance()); + return *static_cast<QTestEventLoop *>(testLoop); + } + +public Q_SLOTS: + inline void exitLoop(); + +protected: + inline void timerEvent(QTimerEvent *e); + +private: + bool inLoop; + bool _timeout; + int timerId; + + QEventLoop *loop; +}; + +inline void QTestEventLoop::enterLoop(int secs) +{ + Q_ASSERT(!loop); + + QEventLoop l; + + inLoop = true; + _timeout = false; + + timerId = startTimer(secs * 1000); + + loop = &l; + l.exec(); + loop = 0; +} + +inline void QTestEventLoop::exitLoop() +{ + if (timerId != -1) + killTimer(timerId); + timerId = -1; + + if (loop) + loop->exit(); + + inLoop = false; +} + +inline void QTestEventLoop::timerEvent(QTimerEvent *e) +{ + if (e->timerId() != timerId) + return; + _timeout = true; + exitLoop(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestkeyboard.h b/src/testlib/qtestkeyboard.h new file mode 100644 index 0000000..68dc53c --- /dev/null +++ b/src/testlib/qtestkeyboard.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTKEYBOARD_H +#define QTESTKEYBOARD_H + +#if 0 +// inform syncqt +#pragma qt_no_master_include +#endif + +#include <QtTest/qtestassert.h> +#include <QtTest/qtest_global.h> +#include <QtTest/qtestsystem.h> +#include <QtTest/qtestspontaneevent.h> + +#include <QtCore/qpointer.h> +#include <QtGui/qapplication.h> +#include <QtGui/qevent.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + enum KeyAction { Press, Release, Click }; + + static void simulateEvent(QWidget *widget, bool press, int code, + Qt::KeyboardModifiers modifier, QString text, bool repeat, int delay=-1) + { + QTEST_ASSERT(widget); + extern int Q_TESTLIB_EXPORT defaultKeyDelay(); + + if (delay == -1 || delay < defaultKeyDelay()) + delay = defaultKeyDelay(); + if(delay > 0) + QTest::qWait(delay); + + QKeyEvent a(press ? QEvent::KeyPress : QEvent::KeyRelease, code, modifier, text, repeat); + QSpontaneKeyEvent::setSpontaneous(&a); + if (!qApp->notify(widget, &a)) + QTest::qWarn("Keyboard event not accepted by receiving widget"); + } + + static void sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, + QString text, Qt::KeyboardModifiers modifier, int delay=-1) + { + QTEST_ASSERT(qApp); + + if (!widget) + widget = QWidget::keyboardGrabber(); + if (!widget) { + if (QWidget *apw = QApplication::activePopupWidget()) + widget = apw->focusWidget() ? apw->focusWidget() : apw; + else + widget = QApplication::focusWidget(); + } + if (!widget) + widget = QApplication::activeWindow(); + + QTEST_ASSERT(widget); + + if (action == Click) { + QPointer<QWidget> ptr(widget); + sendKeyEvent(Press, widget, code, text, modifier, delay); + if (!ptr) { + // if we send key-events to embedded widgets, they might be destroyed + // when the user presses Return + return; + } + sendKeyEvent(Release, widget, code, text, modifier, delay); + return; + } + + bool repeat = false; + + if (action == Press) { + if (modifier & Qt::ShiftModifier) + simulateEvent(widget, true, Qt::Key_Shift, 0, QString(), false, delay); + + if (modifier & Qt::ControlModifier) + simulateEvent(widget, true, Qt::Key_Control, modifier & Qt::ShiftModifier, QString(), false, delay); + + if (modifier & Qt::AltModifier) + simulateEvent(widget, true, Qt::Key_Alt, + modifier & (Qt::ShiftModifier | Qt::ControlModifier), QString(), false, delay); + if (modifier & Qt::MetaModifier) + simulateEvent(widget, true, Qt::Key_Meta, modifier & (Qt::ShiftModifier + | Qt::ControlModifier | Qt::AltModifier), QString(), false, delay); + simulateEvent(widget, true, code, modifier, text, repeat, delay); + } else if (action == Release) { + simulateEvent(widget, false, code, modifier, text, repeat, delay); + + if (modifier & Qt::MetaModifier) + simulateEvent(widget, false, Qt::Key_Meta, modifier, QString(), false, delay); + if (modifier & Qt::AltModifier) + simulateEvent(widget, false, Qt::Key_Alt, modifier & + (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier), QString(), false, delay); + + if (modifier & Qt::ControlModifier) + simulateEvent(widget, false, Qt::Key_Control, + modifier & (Qt::ShiftModifier | Qt::ControlModifier), QString(), false, delay); + + if (modifier & Qt::ShiftModifier) + simulateEvent(widget, false, Qt::Key_Shift, modifier & Qt::ShiftModifier, QString(), false, delay); + } + } + + // Convenience function + static void sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code, + char ascii, Qt::KeyboardModifiers modifier, int delay=-1) + { + QString text; + if (ascii) + text = QString(QChar::fromLatin1(ascii)); + sendKeyEvent(action, widget, code, text, modifier, delay); + } + + inline static void keyEvent(KeyAction action, QWidget *widget, char ascii, + Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { sendKeyEvent(action, widget, asciiToKey(ascii), ascii, modifier, delay); } + inline static void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, + Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { sendKeyEvent(action, widget, key, keyToAscii(key), modifier, delay); } + + inline static void keyClicks(QWidget *widget, const QString &sequence, + Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { + for (int i=0; i < sequence.length(); i++) + keyEvent(Click, widget, sequence.at(i).toLatin1(), modifier, delay); + } + + inline static void keyPress(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { keyEvent(Press, widget, key, modifier, delay); } + inline static void keyRelease(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { keyEvent(Release, widget, key, modifier, delay); } + inline static void keyClick(QWidget *widget, char key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { keyEvent(Click, widget, key, modifier, delay); } + inline static void keyPress(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { keyEvent(Press, widget, key, modifier, delay); } + inline static void keyRelease(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { keyEvent(Release, widget, key, modifier, delay); } + inline static void keyClick(QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1) + { keyEvent(Click, widget, key, modifier, delay); } + +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTESTKEYBOARD_H diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp new file mode 100644 index 0000000..aa56e6c --- /dev/null +++ b/src/testlib/qtestlog.cpp @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/qtestassert.h" + +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qabstracttestlogger_p.h" +#include "QtTest/private/qplaintestlogger_p.h" +#include "QtTest/private/qxmltestlogger_p.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qbytearray.h> + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +QT_BEGIN_NAMESPACE + +namespace QTest { + + struct IgnoreResultList + { + inline IgnoreResultList(QtMsgType tp, const char *message) + : type(tp), next(0) + { msg = qstrdup(message); } + inline ~IgnoreResultList() + { delete [] msg; } + + static inline void clearList(IgnoreResultList *&list) + { + while (list) { + IgnoreResultList *current = list; + list = list->next; + delete current; + } + } + + QtMsgType type; + char *msg; + IgnoreResultList *next; + }; + + static IgnoreResultList *ignoreResultList = 0; + + static QTestLog::LogMode logMode = QTestLog::Plain; + static int verbosity = 0; + static int maxWarnings = 2002; + + static QAbstractTestLogger *testLogger = 0; + static const char *outFile = 0; + + static QtMsgHandler oldMessageHandler; + + static bool handleIgnoredMessage(QtMsgType type, const char *msg) + { + IgnoreResultList *last = 0; + IgnoreResultList *list = ignoreResultList; + while (list) { + if (list->type == type && strcmp(msg, list->msg) == 0) { + // remove the item from the list + if (last) + last->next = list->next; + else if (list->next) + ignoreResultList = list->next; + else + ignoreResultList = 0; + + delete list; + return true; + } + + last = list; + list = list->next; + } + return false; + } + + static void messageHandler(QtMsgType type, const char *msg) + { + static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(QTest::maxWarnings); + + if (!msg || !QTest::testLogger) { + // if this goes wrong, something is seriously broken. + qInstallMsgHandler(oldMessageHandler); + QTEST_ASSERT(msg); + QTEST_ASSERT(QTest::testLogger); + } + + if (handleIgnoredMessage(type, msg)) + // the message is expected, so just swallow it. + return; + + if (type != QtFatalMsg) { + if (counter <= 0) + return; + + if (!counter.deref()) { + QTest::testLogger->addMessage(QAbstractTestLogger::QSystem, + "Maximum amount of warnings exceeded."); + return; + } + } + + switch (type) { + case QtDebugMsg: + QTest::testLogger->addMessage(QAbstractTestLogger::QDebug, msg); + break; + case QtCriticalMsg: + QTest::testLogger->addMessage(QAbstractTestLogger::QSystem, msg); + break; + case QtWarningMsg: + QTest::testLogger->addMessage(QAbstractTestLogger::QWarning, msg); + break; + case QtFatalMsg: + QTest::testLogger->addMessage(QAbstractTestLogger::QFatal, msg); + /* Right now, we're inside the custom message handler and we're + * being qt_message_output in qglobal.cpp. After we return from + * this function, it will proceed with calling exit() and abort() + * and hence crash. Therefore, we call these logging functions such + * that we wrap up nicely, and in particular produce well-formed XML. */ + QTestResult::addFailure("Received a fatal error.", "Unknown file", 0); + QTestLog::leaveTestFunction(); + QTestLog::stopLogging(); + break; + } + } + +} + +QTestLog::QTestLog() +{ +} + +QTestLog::~QTestLog() +{ +} + +void QTestLog::enterTestFunction(const char* function) +{ + QTEST_ASSERT(QTest::testLogger); + QTEST_ASSERT(function); + + QTest::testLogger->enterTestFunction(function); +} + +int QTestLog::unhandledIgnoreMessages() +{ + int i = 0; + QTest::IgnoreResultList *list = QTest::ignoreResultList; + while (list) { + ++i; + list = list->next; + } + return i; +} + +void QTestLog::leaveTestFunction() +{ + QTEST_ASSERT(QTest::testLogger); + + QTest::IgnoreResultList::clearList(QTest::ignoreResultList); + QTest::testLogger->leaveTestFunction(); +} + +void QTestLog::printUnhandledIgnoreMessages() +{ + QTEST_ASSERT(QTest::testLogger); + + char msg[1024]; + QTest::IgnoreResultList *list = QTest::ignoreResultList; + while (list) { + QTest::qt_snprintf(msg, 1024, "Did not receive message: \"%s\"", list->msg); + QTest::testLogger->addMessage(QAbstractTestLogger::Info, msg); + + list = list->next; + } +} + +void QTestLog::addPass(const char *msg) +{ + QTEST_ASSERT(QTest::testLogger); + QTEST_ASSERT(msg); + + QTest::testLogger->addIncident(QAbstractTestLogger::Pass, msg); +} + +void QTestLog::addFail(const char *msg, const char *file, int line) +{ + QTEST_ASSERT(QTest::testLogger); + + QTest::testLogger->addIncident(QAbstractTestLogger::Fail, msg, file, line); +} + +void QTestLog::addXFail(const char *msg, const char *file, int line) +{ + QTEST_ASSERT(QTest::testLogger); + QTEST_ASSERT(msg); + QTEST_ASSERT(file); + + QTest::testLogger->addIncident(QAbstractTestLogger::XFail, msg, file, line); +} + +void QTestLog::addXPass(const char *msg, const char *file, int line) +{ + QTEST_ASSERT(QTest::testLogger); + QTEST_ASSERT(msg); + QTEST_ASSERT(file); + + QTest::testLogger->addIncident(QAbstractTestLogger::XPass, msg, file, line); +} + +void QTestLog::addSkip(const char *msg, QTest::SkipMode /*mode*/, + const char *file, int line) +{ + QTEST_ASSERT(QTest::testLogger); + QTEST_ASSERT(msg); + QTEST_ASSERT(file); + + QTest::testLogger->addMessage(QAbstractTestLogger::Skip, msg, file, line); +} + +void QTestLog::addBenchmarkResult(const QBenchmarkResult &result) +{ + QTEST_ASSERT(QTest::testLogger); + QTest::testLogger->addBenchmarkResult(result); +} + +void QTestLog::startLogging() +{ + QTEST_ASSERT(!QTest::testLogger); + + switch (QTest::logMode) { + case QTestLog::Plain: + QTest::testLogger = new QPlainTestLogger(); + break; + case QTestLog::XML: + QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Complete); + break; + case QTestLog::LightXML: + QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Light); + } + + QTest::testLogger->startLogging(); + + QTest::oldMessageHandler = qInstallMsgHandler(QTest::messageHandler); +} + +void QTestLog::stopLogging() +{ + qInstallMsgHandler(QTest::oldMessageHandler); + + QTEST_ASSERT(QTest::testLogger); + QTest::testLogger->stopLogging(); + delete QTest::testLogger; + QTest::testLogger = 0; +} + +void QTestLog::warn(const char *msg) +{ + QTEST_ASSERT(msg); + + QTest::testLogger->addMessage(QAbstractTestLogger::Warn, msg); +} + +void QTestLog::info(const char *msg, const char *file, int line) +{ + QTEST_ASSERT(msg); + + if (QTest::testLogger) + QTest::testLogger->addMessage(QAbstractTestLogger::Info, msg, file, line); +} + +void QTestLog::setLogMode(LogMode mode) +{ + QTest::logMode = mode; +} + +QTestLog::LogMode QTestLog::logMode() +{ + return QTest::logMode; +} + +void QTestLog::setVerboseLevel(int level) +{ + QTest::verbosity = level; +} + +int QTestLog::verboseLevel() +{ + return QTest::verbosity; +} + +void QTestLog::addIgnoreMessage(QtMsgType type, const char *msg) +{ + QTest::IgnoreResultList *item = new QTest::IgnoreResultList(type, msg); + + QTest::IgnoreResultList *list = QTest::ignoreResultList; + if (!list) { + QTest::ignoreResultList = item; + return; + } + while (list->next) + list = list->next; + list->next = item; +} + +void QTestLog::redirectOutput(const char *fileName) +{ + QTEST_ASSERT(fileName); + + QTest::outFile = fileName; +} + +const char *QTestLog::outputFileName() +{ + return QTest::outFile; +} + +void QTestLog::setMaxWarnings(int m) +{ + QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2; +} + +QT_END_NAMESPACE diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h new file mode 100644 index 0000000..fa49a38 --- /dev/null +++ b/src/testlib/qtestlog_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLOG_P_H +#define QTESTLOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtTest/qtest_global.h> + +QT_BEGIN_NAMESPACE + +class QBenchmarkResult; + +class QTestLog +{ +public: + enum LogMode { Plain = 0, XML, LightXML }; + + static void enterTestFunction(const char* function); + static void leaveTestFunction(); + + static void addPass(const char *msg); + static void addFail(const char *msg, const char *file, int line); + static void addXFail(const char *msg, const char *file, int line); + static void addXPass(const char *msg, const char *file, int line); + static void addSkip(const char *msg, QTest::SkipMode mode, + const char *file, int line); + static void addBenchmarkResult(const QBenchmarkResult &result); + static void addIgnoreMessage(QtMsgType type, const char *msg); + static int unhandledIgnoreMessages(); + static void printUnhandledIgnoreMessages(); + + static void warn(const char *msg); + static void info(const char *msg, const char *file, int line); + + static void startLogging(); + static void stopLogging(); + + static void setLogMode(LogMode mode); + static LogMode logMode(); + + static void setVerboseLevel(int level); + static int verboseLevel(); + + static void redirectOutput(const char *fileName); + static const char *outputFileName(); + + static void setMaxWarnings(int max); + +private: + QTestLog(); + ~QTestLog(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestmouse.h b/src/testlib/qtestmouse.h new file mode 100644 index 0000000..48b9a6c --- /dev/null +++ b/src/testlib/qtestmouse.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTMOUSE_H +#define QTESTMOUSE_H + +#if 0 +// inform syncqt +#pragma qt_no_master_include +#endif + +#include <QtTest/qtest_global.h> +#include <QtTest/qtestassert.h> +#include <QtTest/qtestsystem.h> +#include <QtTest/qtestspontaneevent.h> + +#include <QtCore/qpoint.h> +#include <QtCore/qstring.h> +#include <QtGui/qapplication.h> +#include <QtGui/qevent.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDClick, MouseMove }; + + static void mouseEvent(MouseAction action, QWidget *widget, Qt::MouseButton button, + Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1) + { + QTEST_ASSERT(widget); + extern int Q_TESTLIB_EXPORT defaultMouseDelay(); + + if (delay == -1 || delay < defaultMouseDelay()) + delay = defaultMouseDelay(); + if(delay > 0) + QTest::qWait(delay); + + if (pos.isNull()) + pos = widget->rect().center(); + + if (action == MouseClick) { + mouseEvent(MousePress, widget, button, stateKey, pos); + mouseEvent(MouseRelease, widget, button, stateKey, pos); + return; + } + + QTEST_ASSERT(button == Qt::NoButton || button & Qt::MouseButtonMask); + QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask); + + stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask); + + QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, button, stateKey); + switch (action) + { + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, widget->mapToGlobal(pos), button, button, stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, widget->mapToGlobal(pos), button, 0, stateKey); + break; + case MouseDClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, widget->mapToGlobal(pos), button, button, stateKey); + break; + case MouseMove: + QCursor::setPos(widget->mapToGlobal(pos)); + qApp->processEvents(); + return; + default: + QTEST_ASSERT(false); + } + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(widget, &me)) + QTest::qWarn("Mouse event not accepted by receiving widget"); + + } + + inline void mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { mouseEvent(MousePress, widget, button, stateKey, pos, delay); } + inline void mouseRelease(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { mouseEvent(MouseRelease, widget, button, stateKey, pos, delay); } + inline void mouseClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { mouseEvent(MouseClick, widget, button, stateKey, pos, delay); } + inline void mouseDClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, + QPoint pos = QPoint(), int delay=-1) + { mouseEvent(MouseDClick, widget, button, stateKey, pos, delay); } + inline void mouseMove(QWidget *widget, QPoint pos = QPoint(), int delay=-1) + { mouseEvent(MouseMove, widget, Qt::NoButton, 0, pos, delay); } + +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTESTMOUSE_H diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp new file mode 100644 index 0000000..39759b5 --- /dev/null +++ b/src/testlib/qtestresult.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qtestresult_p.h" +#include <QtCore/qglobal.h> + +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/qtestdata.h" +#include "QtTest/qtestassert.h" + +#include <stdio.h> +#include <string.h> + +QT_BEGIN_NAMESPACE + +namespace QTest +{ + static QTestData *currentTestData = 0; + static QTestData *currentGlobalTestData = 0; + static const char *currentTestFunc = 0; + static const char *currentTestObjectName = 0; + static bool failed = false; + static bool dataFailed = false; + static bool skipCurrentTest = false; + static QTestResult::TestLocation location = QTestResult::NoWhere; + + static int fails = 0; + static int passes = 0; + static int skips = 0; + + static const char *expectFailComment = 0; + static int expectFailMode = 0; +}; + +void QTestResult::reset() +{ + QTest::currentTestData = 0; + QTest::currentGlobalTestData = 0; + QTest::currentTestFunc = 0; + QTest::currentTestObjectName = 0; + QTest::failed = false; + QTest::dataFailed = false; + QTest::location = QTestResult::NoWhere; + + QTest::fails = 0; + QTest::passes = 0; + QTest::skips = 0; + + QTest::expectFailComment = 0; + QTest::expectFailMode = 0; +} + +bool QTestResult::allDataPassed() +{ + return !QTest::failed; +} + +bool QTestResult::currentTestFailed() +{ + return QTest::dataFailed; +} + +QTestData *QTestResult::currentGlobalTestData() +{ + return QTest::currentGlobalTestData; +} + +QTestData *QTestResult::currentTestData() +{ + return QTest::currentTestData; +} + +void QTestResult::setCurrentGlobalTestData(QTestData *data) +{ + QTest::currentGlobalTestData = data; +} + +void QTestResult::setCurrentTestData(QTestData *data) +{ + QTest::currentTestData = data; + QTest::dataFailed = false; +} + +void QTestResult::setCurrentTestFunction(const char *func) +{ + QTest::currentTestFunc = func; + QTest::failed = false; + if (!func) + QTest::location = NoWhere; + if (func) + QTestLog::enterTestFunction(func); +} + +static void clearExpectFail() +{ + QTest::expectFailMode = 0; + delete [] const_cast<char *>(QTest::expectFailComment); + QTest::expectFailComment = 0; +} + +void QTestResult::finishedCurrentTestFunction() +{ + if (!QTest::failed && QTestLog::unhandledIgnoreMessages()) { + QTestLog::printUnhandledIgnoreMessages(); + addFailure("Not all expected messages were received", 0, 0); + } + + if (!QTest::failed && !QTest::skipCurrentTest) { + QTestLog::addPass(""); + ++QTest::passes; + } + QTest::currentTestFunc = 0; + QTest::failed = false; + QTest::dataFailed = false; + QTest::location = NoWhere; + + QTestLog::leaveTestFunction(); + + clearExpectFail(); +} + +const char *QTestResult::currentTestFunction() +{ + return QTest::currentTestFunc; +} + +const char *QTestResult::currentDataTag() +{ + return QTest::currentTestData ? QTest::currentTestData->dataTag() + : static_cast<const char *>(0); +} + +const char *QTestResult::currentGlobalDataTag() +{ + return QTest::currentGlobalTestData ? QTest::currentGlobalTestData->dataTag() + : static_cast<const char *>(0); +} + +static bool isExpectFailData(const char *dataIndex) +{ + if (!dataIndex || dataIndex[0] == '\0') + return true; + if (!QTest::currentTestData) + return false; + if (strcmp(dataIndex, QTest::currentTestData->dataTag()) == 0) + return true; + return false; +} + +bool QTestResult::expectFail(const char *dataIndex, const char *comment, + QTest::TestFailMode mode, const char *file, int line) +{ + QTEST_ASSERT(comment); + QTEST_ASSERT(mode > 0); + + if (!isExpectFailData(dataIndex)) + return true; // we don't care + + if (QTest::expectFailMode) { + clearExpectFail(); + addFailure("Already expecting a fail", file, line); + return false; + } + + QTest::expectFailMode = mode; + QTest::expectFailComment = comment; + return true; +} + +static bool checkStatement(bool statement, const char *msg, const char *file, int line) +{ + if (statement) { + if (QTest::expectFailMode) { + QTestLog::addXPass(msg, file, line); + bool doContinue = (QTest::expectFailMode == QTest::Continue); + clearExpectFail(); + QTest::failed = true; + ++QTest::fails; + return doContinue; + } + return true; + } + + if (QTest::expectFailMode) { + QTestLog::addXFail(QTest::expectFailComment, file, line); + bool doContinue = (QTest::expectFailMode == QTest::Continue); + clearExpectFail(); + return doContinue; + } + + QTestResult::addFailure(msg, file, line); + return false; +} + +bool QTestResult::verify(bool statement, const char *statementStr, + const char *description, const char *file, int line) +{ + char msg[1024]; + + if (QTestLog::verboseLevel() >= 2) { + QTest::qt_snprintf(msg, 1024, "QVERIFY(%s)", statementStr); + QTestLog::info(msg, file, line); + } + + QTest::qt_snprintf(msg, 1024, "'%s' returned FALSE. (%s)", statementStr, description); + + return checkStatement(statement, msg, file, line); +} + +bool QTestResult::compare(bool success, const char *msg, const char *file, int line) +{ + if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(msg, file, line); + } + + return checkStatement(success, msg, file, line); +} + +bool QTestResult::compare(bool success, const char *msg, char *val1, char *val2, + const char *actual, const char *expected, const char *file, int line) +{ + QTEST_ASSERT(expected); + QTEST_ASSERT(actual); + + if (!val1 && !val2) + return compare(success, msg, file, line); + + char buf[1024]; + QTest::qt_snprintf(buf, 1024, "%s\n Actual (%s): %s\n Expected (%s): %s", msg, + actual, val1 ? val1 : "<null>", + expected, val2 ? val2 : "<null>"); + delete [] val1; + delete [] val2; + return compare(success, buf, file, line); +} + +void QTestResult::addFailure(const char *message, const char *file, int line) +{ + clearExpectFail(); + + QTestLog::addFail(message, file, line); + QTest::failed = true; + QTest::dataFailed = true; + ++QTest::fails; +} + +void QTestResult::addSkip(const char *message, QTest::SkipMode mode, + const char *file, int line) +{ + clearExpectFail(); + + QTestLog::addSkip(message, mode, file, line); + ++QTest::skips; +} + +QTestResult::TestLocation QTestResult::currentTestLocation() +{ + return QTest::location; +} + +void QTestResult::setCurrentTestLocation(TestLocation loc) +{ + QTest::location = loc; +} + +void QTestResult::setCurrentTestObject(const char *name) +{ + QTest::currentTestObjectName = name; +} + +const char *QTestResult::currentTestObjectName() +{ + return QTest::currentTestObjectName ? QTest::currentTestObjectName : ""; +} + +int QTestResult::passCount() +{ + return QTest::passes; +} + +int QTestResult::failCount() +{ + return QTest::fails; +} + +int QTestResult::skipCount() +{ + return QTest::skips; +} + +void QTestResult::ignoreMessage(QtMsgType type, const char *msg) +{ + QTestLog::addIgnoreMessage(type, msg); +} + +bool QTestResult::testFailed() +{ + return QTest::failed; +} + +void QTestResult::setSkipCurrentTest(bool value) +{ + QTest::skipCurrentTest = value; +} + +bool QTestResult::skipCurrentTest() +{ + return QTest::skipCurrentTest; +} + +QT_END_NAMESPACE diff --git a/src/testlib/qtestresult_p.h b/src/testlib/qtestresult_p.h new file mode 100644 index 0000000..0f1e64b --- /dev/null +++ b/src/testlib/qtestresult_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTRESULT_P_H +#define QTESTRESULT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtTest/qtest_global.h> + +QT_BEGIN_NAMESPACE + +class QTestResultPrivate; +class QTestData; + +class QTestResult +{ +public: + enum TestLocation { NoWhere = 0, DataFunc = 1, InitFunc = 2, Func = 3, CleanupFunc = 4 }; + + static const char *currentTestObjectName(); + static bool currentTestFailed(); + static bool allDataPassed(); + static QTestData *currentTestData(); + static QTestData *currentGlobalTestData(); + static const char *currentTestFunction(); + static TestLocation currentTestLocation(); + static const char *currentDataTag(); + static const char *currentGlobalDataTag(); + static void finishedCurrentTestFunction(); + static void reset(); + + static int passCount(); + static int failCount(); + static int skipCount(); + + static void ignoreMessage(QtMsgType type, const char *msg); + + static void addFailure(const char *message, const char *file, int line); + static bool compare(bool success, const char *msg, const char *file, int line); + static bool compare(bool success, const char *msg, char *val1, char *val2, + const char *actual, const char *expected, const char *file, int line); + + static void setCurrentGlobalTestData(QTestData *data); + static void setCurrentTestData(QTestData *data); + static void setCurrentTestFunction(const char *func); + static void setCurrentTestLocation(TestLocation loc); + static void setCurrentTestObject(const char *name); + static void addSkip(const char *message, QTest::SkipMode mode, + const char *file, int line); + static bool expectFail(const char *dataIndex, const char *comment, + QTest::TestFailMode mode, const char *file, int line); + static bool verify(bool statement, const char *statementStr, const char *extraInfo, + const char *file, int line); + static bool testFailed(); + static void setSkipCurrentTest(bool value); + static bool skipCurrentTest(); + +private: + Q_DISABLE_COPY(QTestResult) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestspontaneevent.h b/src/testlib/qtestspontaneevent.h new file mode 100644 index 0000000..e125329 --- /dev/null +++ b/src/testlib/qtestspontaneevent.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTSPONTANEEVENT_H +#define QTESTSPONTANEEVENT_H + +#include <QtCore/qcoreevent.h> + +#if 0 +// inform syncqt +#pragma qt_no_master_include +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +#ifndef QTEST_NO_SIZEOF_CHECK +template <int> +class QEventSizeOfChecker +{ +private: + QEventSizeOfChecker() {} +}; + +template <> +class QEventSizeOfChecker<sizeof(QEvent)> +{ +public: + QEventSizeOfChecker() {} +}; +#endif + +class QSpontaneKeyEvent +{ +public: + void setSpontaneous() { spont = 1; }; + bool spontaneous() { return spont; }; + virtual void dummyFunc() { }; + virtual ~QSpontaneKeyEvent() {} + +#ifndef QTEST_NO_SIZEOF_CHECK + inline void ifYouGetCompileErrorHereYouUseWrongQt() + { + // this is a static assert in case QEvent changed in Qt + QEventSizeOfChecker<sizeof(QSpontaneKeyEvent)> dummy; + } +#endif + + static inline void setSpontaneous(QEvent *ev) + { + // use a union instead of a reinterpret_cast to prevent alignment warnings + union + { + QSpontaneKeyEvent *skePtr; + QEvent *evPtr; + } helper; + + helper.evPtr = ev; + helper.skePtr->setSpontaneous(); + } + +protected: + void *d; + ushort t; + +private: + ushort posted : 1; + ushort spont : 1; + ushort m_accept : 1; + ushort reserved : 13; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtestsystem.h b/src/testlib/qtestsystem.h new file mode 100644 index 0000000..c1b5fc1 --- /dev/null +++ b/src/testlib/qtestsystem.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTSYSTEM_H +#define QTESTSYSTEM_H + +#include <QtTest/qtestcase.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdatetime.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest +{ + inline static void qWait(int ms) + { + Q_ASSERT(QCoreApplication::instance()); + + QTime timer; + timer.start(); + do { + QCoreApplication::processEvents(QEventLoop::AllEvents, ms); + QTest::qSleep(10); + } while (timer.elapsed() < ms); + } +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/testlib/qtesttable.cpp b/src/testlib/qtesttable.cpp new file mode 100644 index 0000000..7c1cce0 --- /dev/null +++ b/src/testlib/qtesttable.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtTest/private/qtesttable_p.h" +#include "QtTest/qtestdata.h" + +#include <QtCore/qmetaobject.h> + +#include <string.h> + +#include "QtTest/qtestassert.h" + +QT_BEGIN_NAMESPACE + +class QTestTablePrivate +{ +public: + struct ElementList + { + ElementList(): elementName(0), elementType(0), next(0) {} + const char *elementName; + int elementType; + ElementList *next; + }; + + struct DataList + { + DataList(): data(0), next(0) {} + QTestData *data; + DataList *next; + }; + + QTestTablePrivate(): list(0), dataList(0) {} + ~QTestTablePrivate(); + + ElementList *list; + DataList *dataList; + + void append(int elemType, const char *elemName); + void append(QTestData *data); + ElementList *elementAt(int index); + QTestData *dataAt(int index); + + static QTestTable *currentTestTable; + static QTestTable *gTable; +}; + +QTestTable *QTestTablePrivate::currentTestTable = 0; +QTestTable *QTestTablePrivate::gTable = 0; + +QTestTablePrivate::ElementList *QTestTablePrivate::elementAt(int index) +{ + ElementList *iter = list; + for (int i = 0; i < index; ++i) { + if (!iter) + return 0; + iter = iter->next; + } + return iter; +} + +QTestData *QTestTablePrivate::dataAt(int index) +{ + DataList *iter = dataList; + for (int i = 0; i < index; ++i) { + if (!iter) + return 0; + iter = iter->next; + } + return iter ? iter->data : 0; +} + +QTestTablePrivate::~QTestTablePrivate() +{ + DataList *dit = dataList; + while (dit) { + DataList *next = dit->next; + delete dit->data; + delete dit; + dit = next; + } + ElementList *iter = list; + while (iter) { + ElementList *next = iter->next; + delete iter; + iter = next; + } +} + +void QTestTablePrivate::append(int elemType, const char *elemName) +{ + ElementList *item = new ElementList; + item->elementName = elemName; + item->elementType = elemType; + if (!list) { + list = item; + return; + } + ElementList *last = list; + while (last->next != 0) + last = last->next; + last->next = item; +} + +void QTestTablePrivate::append(QTestData *data) +{ + DataList *item = new DataList; + item->data = data; + if (!dataList) { + dataList = item; + return; + } + DataList *last = dataList; + while (last->next != 0) + last = last->next; + last->next = item; +} + +void QTestTable::addColumn(int type, const char *name) +{ + QTEST_ASSERT(type); + QTEST_ASSERT(name); + + d->append(type, name); +} + +int QTestTable::elementCount() const +{ + QTestTablePrivate::ElementList *item = d->list; + int count = 0; + while (item) { + ++count; + item = item->next; + } + return count; +} + + +int QTestTable::dataCount() const +{ + QTestTablePrivate::DataList *item = d->dataList; + int count = 0; + while (item) { + ++count; + item = item->next; + } + return count; +} + +bool QTestTable::isEmpty() const +{ + return !d->list; +} + +QTestData *QTestTable::newData(const char *tag) +{ + QTestData *dt = new QTestData(tag, this); + d->append(dt); + return dt; +} + +QTestTable::QTestTable() +{ + d = new QTestTablePrivate; + QTestTablePrivate::currentTestTable = this; +} + +QTestTable::~QTestTable() +{ + QTestTablePrivate::currentTestTable = 0; + delete d; +} + +int QTestTable::elementTypeId(int index) const +{ + QTestTablePrivate::ElementList *item = d->elementAt(index); + if (!item) + return -1; + return item->elementType; +} + +const char *QTestTable::dataTag(int index) const +{ + QTestTablePrivate::ElementList *item = d->elementAt(index); + if (!item) + return 0; + return item->elementName; +} + +QTestData *QTestTable::testData(int index) const +{ + return d->dataAt(index); +} + +int QTestTable::indexOf(const char *elementName) const +{ + QTEST_ASSERT(elementName); + + QTestTablePrivate::ElementList *item = d->list; + int i = 0; + while (item) { + if (strcmp(elementName, item->elementName) == 0) + return i; + item = item->next; + ++i; + } + return -1; +} + +QTestTable *QTestTable::globalTestTable() +{ + if (!QTestTablePrivate::gTable) + QTestTablePrivate::gTable = new QTestTable(); + return QTestTablePrivate::gTable; +} + +void QTestTable::clearGlobalTestTable() +{ + delete QTestTablePrivate::gTable; + QTestTablePrivate::gTable = 0; +} + +QTestTable *QTestTable::currentTestTable() +{ + return QTestTablePrivate::currentTestTable; +} + +QT_END_NAMESPACE diff --git a/src/testlib/qtesttable_p.h b/src/testlib/qtesttable_p.h new file mode 100644 index 0000000..4b20170 --- /dev/null +++ b/src/testlib/qtesttable_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTTABLE_P_H +#define QTESTTABLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtTest/qtest_global.h> + +QT_BEGIN_NAMESPACE + +class QTestData; +class QTestTablePrivate; + +class QTestTable +{ +public: + QTestTable(); + ~QTestTable(); + + void addColumn(int elementType, const char *elementName); + QTestData *newData(const char *tag); + + int elementCount() const; + int dataCount() const; + + int elementTypeId(int index) const; + const char *dataTag(int index) const; + int indexOf(const char *elementName) const; + bool isEmpty() const; + QTestData *testData(int index) const; + + static QTestTable *globalTestTable(); + static QTestTable *currentTestTable(); + static void clearGlobalTestTable(); + +private: + Q_DISABLE_COPY(QTestTable) + + QTestTablePrivate *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp new file mode 100644 index 0000000..bba98da --- /dev/null +++ b/src/testlib/qxmltestlogger.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <QtCore/qglobal.h> + +#include "QtTest/private/qxmltestlogger_p.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qbenchmark_p.h" + +QT_BEGIN_NAMESPACE + +namespace QTest { + + static const char* xmlMessageType2String(QAbstractTestLogger::MessageTypes type) + { + switch (type) { + case QAbstractTestLogger::Warn: + return "warn"; + case QAbstractTestLogger::QSystem: + return "system"; + case QAbstractTestLogger::QDebug: + return "qdebug"; + case QAbstractTestLogger::QWarning: + return "qwarn"; + case QAbstractTestLogger::QFatal: + return "qfatal"; + case QAbstractTestLogger::Skip: + return "skip"; + case QAbstractTestLogger::Info: + return "info"; + } + return "??????"; + } + + static const char* xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type) + { + switch (type) { + case QAbstractTestLogger::Pass: + return "pass"; + case QAbstractTestLogger::XFail: + return "xfail"; + case QAbstractTestLogger::Fail: + return "fail"; + case QAbstractTestLogger::XPass: + return "xpass"; + } + return "??????"; + } + +} + + +QXmlTestLogger::QXmlTestLogger(XmlMode mode ): + xmlmode(mode) +{ + +} + +QXmlTestLogger::~QXmlTestLogger() +{ + +} + + +void QXmlTestLogger::startLogging() +{ + QAbstractTestLogger::startLogging(); + char buf[1024]; + + if (xmlmode == QXmlTestLogger::Complete) { + QTest::qt_snprintf(buf, sizeof(buf), + "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" + "<TestCase name=\"%s\">\n", QTestResult::currentTestObjectName()); + outputString(buf); + } + + QTest::qt_snprintf(buf, sizeof(buf), + "<Environment>\n" + " <QtVersion>%s</QtVersion>\n" + " <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n" + "</Environment>\n", qVersion()); + outputString(buf); +} + +void QXmlTestLogger::stopLogging() +{ + if (xmlmode == QXmlTestLogger::Complete) { + outputString("</TestCase>\n"); + } + + QAbstractTestLogger::stopLogging(); +} + +void QXmlTestLogger::enterTestFunction(const char *function) +{ + char buf[1024]; + QTest::qt_snprintf(buf, sizeof(buf), "<TestFunction name=\"%s\">\n", function); + outputString(buf); +} + +void QXmlTestLogger::leaveTestFunction() +{ + outputString("</TestFunction>\n"); +} + +namespace QTest +{ + +inline static bool isEmpty(const char *str) +{ + return !str || !str[0]; +} + +static const char *incidentFormatString(bool noDescription, bool noTag) +{ + if (noDescription) { + if (noTag) + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"; + else + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" + "</Incident>\n"; + } else { + if (noTag) + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <Description><![CDATA[%s%s%s%s]]></Description>\n" + "</Incident>\n"; + else + return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Incident>\n"; + } +} + +static const char *benchmarkResultFormatString() +{ + return "<BenchmarkResult metric=\"%s\" tag=\"%s\" value=\"%s\" iterations=\"%d\" />\n"; +} + +static const char *messageFormatString(bool noDescription, bool noTag) +{ + if (noDescription) { + if (noTag) + return "<Message type=\"%s\" file=\"%s\" line=\"%d\" />\n"; + else + return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" + "</Message>\n"; + } else { + if (noTag) + return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <Description><![CDATA[%s%s%s%s]]></Description>\n" + "</Message>\n"; + else + return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" + " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Message>\n"; + } +} + +} // namespace + +void QXmlTestLogger::addIncident(IncidentTypes type, const char *description, + const char *file, int line) +{ + char buf[1536]; + const char *tag = QTestResult::currentDataTag(); + const char *gtag = QTestResult::currentGlobalDataTag(); + const char *filler = (tag && gtag) ? ":" : ""; + const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); + + QTest::qt_snprintf(buf, sizeof(buf), + QTest::incidentFormatString(QTest::isEmpty(description), notag), + QTest::xmlIncidentType2String(type), + file ? file : "", line, + gtag ? gtag : "", + filler, + tag ? tag : "", + description ? description : ""); + + outputString(buf); +} + +void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result) +{ + char buf[1536]; + QTest::qt_snprintf( + buf, sizeof(buf), + QTest::benchmarkResultFormatString(), + QBenchmarkGlobalData::current->measurer->metricText().toAscii().data(), + result.context.tag.toAscii().data(), + QByteArray::number(result.value).constData(), //no 64-bit qt_snprintf support + result.iterations); + outputString(buf); +} + +void QXmlTestLogger::addMessage(MessageTypes type, const char *message, + const char *file, int line) +{ + char buf[1536]; + char msgbuf[1024]; + const char *tag = QTestResult::currentDataTag(); + const char *gtag = QTestResult::currentGlobalDataTag(); + const char *filler = (tag && gtag) ? ":" : ""; + const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); + + QTest::qt_snprintf(msgbuf, sizeof(msgbuf), "%s", + message ? message : ""); + + QTest::qt_snprintf(buf, sizeof(buf), + QTest::messageFormatString(QTest::isEmpty(message), notag), + QTest::xmlMessageType2String(type), + file ? file : "", line, + gtag ? gtag : "", + filler, + tag ? tag : "", + msgbuf); + + outputString(buf); +} + +QT_END_NAMESPACE diff --git a/src/testlib/qxmltestlogger_p.h b/src/testlib/qxmltestlogger_p.h new file mode 100644 index 0000000..3e78969 --- /dev/null +++ b/src/testlib/qxmltestlogger_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXMLTESTLOGGER_P_H +#define QXMLTESTLOGGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <QtTest/private/qabstracttestlogger_p.h> + +QT_BEGIN_NAMESPACE + +class QXmlTestLogger : public QAbstractTestLogger +{ +public: + enum XmlMode { Complete = 0, Light }; + + QXmlTestLogger(XmlMode mode = Complete); + ~QXmlTestLogger(); + + void startLogging(); + void stopLogging(); + + void enterTestFunction(const char *function); + void leaveTestFunction(); + + void addIncident(IncidentTypes type, const char *description, + const char *file = 0, int line = 0); + void addBenchmarkResult(const QBenchmarkResult &result); + + void addMessage(MessageTypes type, const char *message, + const char *file = 0, int line = 0); + +private: + XmlMode xmlmode; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro new file mode 100644 index 0000000..90bd92d --- /dev/null +++ b/src/testlib/testlib.pro @@ -0,0 +1,27 @@ +TARGET = QtTest +QPRO_PWD = $$PWD +QT = core +INCLUDEPATH += . + +unix:!embedded { + QMAKE_PKGCONFIG_DESCRIPTION = Qt Unit Testing Library + QMAKE_PKGCONFIG_REQUIRES = QtCore +} + +# Input +HEADERS = qtest_global.h qtestcase.h qtestdata.h qtesteventloop.h +SOURCES = qtestcase.cpp qtestlog.cpp qtesttable.cpp qtestdata.cpp qtestresult.cpp qasciikey.cpp qplaintestlogger.cpp qxmltestlogger.cpp qsignaldumper.cpp qabstracttestlogger.cpp qbenchmark.cpp qbenchmarkmeasurement.cpp qbenchmarkvalgrind.cpp qbenchmarkevent.cpp + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII QTESTLIB_MAKEDLL QT_NO_DATASTREAM + +wince*:{ + LIBS += libcmt.lib corelibc.lib ole32.lib oleaut32.lib uuid.lib commctrl.lib coredll.lib winsock.lib +} + +mac { + LIBS += -framework IOKit -framework Security +} + +include(../qbase.pri) +QMAKE_TARGET_PRODUCT = QTestLib +QMAKE_TARGET_DESCRIPTION = Qt Unit Testing Library |