/* $Id$ */
/** @file
* IPRT - Testcase Framework.
*/
/*
* Copyright (C) 2009-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/critsect.h>
#include <iprt/initterm.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Guarded memory allocation record.
*/
typedef struct RTTESTGUARDEDMEM
{
/** Pointer to the next record. */
/** The address we return to the user. */
void *pvUser;
/** The base address of the allocation. */
void *pvAlloc;
/** The size of the allocation. */
/** Guards. */
struct
{
/** The guard address. */
void *pv;
/** The guard size. */
/** Pointer to an guarded memory allocation. */
/**
* Test instance structure.
*/
typedef struct RTTESTINT
{
/** Magic. */
/** The number of errors. */
/** The test name. */
const char *pszTest;
/** The length of the test name. */
/** The size of a guard. Multiple of PAGE_SIZE. */
/** The verbosity level. */
/** The creation flags. */
/** Critical section serializing output. */
/** The output stream. */
/** Whether we're currently at a newline. */
bool fNewLine;
/** Critical section serializing access to the members following it. */
/** The list of guarded memory allocations. */
/** The current sub-test. */
const char *pszSubTest;
/** The length of the sub-test name. */
/** Whether the current subtest should figure as 'SKIPPED'. */
bool fSubTestSkipped;
/** Whether we've reported the sub-test result or not. */
bool fSubTestReported;
/** The start error count of the current subtest. */
/** The number of sub tests. */
/** The number of sub tests that failed. */
/** Set if XML output is enabled. */
bool fXmlEnabled;
/** Set if we omit the top level test in the XML report. */
bool fXmlOmitTopTest;
/** Set if we've reported the top test (for RTTEST_C_XML_DELAY_TOP_TEST). */
bool fXmlTopTestDone;
enum {
} eXmlState;
/** Test pipe for the XML output stream going to the server. */
/** File where the XML output stream might be directed. */
/** The number of XML elements on the stack. */
/** XML element stack. */
} RTTESTINT;
/** Pointer to a test instance. */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Validate a test instance. */
do { \
} while (0)
/** Gets and validates a test instance.
* If the handle is nil, we will try retrieve it from the test TLS entry.
*/
do { \
if (pTest == NIL_RTTEST) \
} while (0)
/** Gets and validates a test instance.
* If the handle is nil, we will try retrieve it from the test TLS entry.
*/
do { \
if (pTest == NIL_RTTEST) \
} while (0)
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static void rtTestXmlElemV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va);
static void rtTestXmlElemStartV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** For serializing TLS init. */
/** Our TLS entry. */
/**
* Init TLS index once.
*
* @returns IPRT status code.
* @param pvUser Ignored.
*/
{
}
{
/* RTTESTLVL_INVALID is valid! */
AssertReturn(enmMaxLevel >= RTTESTLVL_INVALID && enmMaxLevel < RTTESTLVL_END, VERR_INVALID_PARAMETER);
/*
* Global init.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Create the instance.
*/
if (!pTest)
return VERR_NO_MEMORY;
pTest->cchSubTest = 0;
pTest->fSubTestSkipped = false;
pTest->fSubTestReported = true;
pTest->cSubTestAtErrors = 0;
pTest->cSubTestsFailed = 0;
pTest->fXmlEnabled = false;
pTest->fXmlTopTestDone = false;
pTest->cXmlElements = 0;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Associate it with our TLS entry unless there is already
* an instance there.
*/
if ( !(fFlags & RTTEST_C_NO_TLS)
&& !RTTlsGet(g_iTestTls))
if (RT_SUCCESS(rc))
{
/*
* Output level override?
*/
{
if (RT_SUCCESS(rc))
{
}
else if (rc != VERR_ENV_VAR_NOT_FOUND)
RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTEnvGetEx(IPRT_TEST_MAX_LEVEL) -> %Rrc\n", pszTest, rc);
}
/*
* Any test driver we are connected or should connect to?
*/
{
if (RT_SUCCESS(rc))
{
#if ARCH_BITS == 64
#else
#endif
if (RT_FAILURE(rc))
{
iNativeTestPipe = -1;
}
}
else if (rc != VERR_ENV_VAR_NOT_FOUND)
}
if (iNativeTestPipe != -1)
{
if (RT_SUCCESS(rc))
pTest->fXmlEnabled = true;
else
{
}
}
/*
* Any test file we should write the test report to?
*/
{
if (RT_SUCCESS(rc))
else if (rc != VERR_ENV_VAR_NOT_FOUND)
RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTEnvGetEx(IPRT_TEST_MAX_LEVEL) -> %Rrc\n", pszTest, rc);
}
if (pszXmlFile && *pszXmlFile)
{
if (RT_SUCCESS(rc))
pTest->fXmlEnabled = true;
else
{
RTStrmPrintf(g_pStdErr, "%s: test file error: RTFileOpen(,\"%s\",) -> %Rrc\n", pszTest, pszXmlFile, rc);
}
}
else if (rc != VERR_ENV_VAR_NOT_FOUND)
/*
*/
|| ( (fFlags & RTTEST_C_USE_ENV)
/*
* Tell the test driver that we're up to.
*/
return VINF_SUCCESS;
}
/* bail out. */
}
}
return rc;
}
{
return RTTestCreateEx(pszTest, RTTEST_C_USE_ENV, RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, NULL /*pszXmlFile*/, phTest);
}
{
if (RT_FAILURE(rc))
{
RTStrmPrintf(g_pStdErr, "%s: fatal error: RTR3InitExeNoArguments failed with rc=%Rrc\n", pszTest, rc);
return RTEXITCODE_INIT;
}
if (RT_FAILURE(rc))
{
return RTEXITCODE_INIT;
}
return RTEXITCODE_SUCCESS;
}
RTR3DECL(RTEXITCODE) RTTestInitExAndCreate(int cArgs, char ***papszArgs, uint32_t fRtInit, const char *pszTest, PRTTEST phTest)
{
int rc;
else
if (RT_FAILURE(rc))
{
RTStrmPrintf(g_pStdErr, "%s: fatal error: RTR3InitExe(,,%#x) failed with rc=%Rrc\n", pszTest, fRtInit, rc);
return RTEXITCODE_INIT;
}
if (RT_FAILURE(rc))
{
return RTEXITCODE_INIT;
}
return RTEXITCODE_SUCCESS;
}
/**
* Destroys a test instance previously created by RTTestCreate.
*
* @returns IPRT status code.
* @param hTest The test handle. NIL_RTTEST is ignored.
*/
{
/*
* Validate
*/
if (hTest == NIL_RTTEST)
return VINF_SUCCESS;
/*
* Make sure we end with a new line and have finished up the XML.
*/
/*
* Clean up.
*/
/* free guarded memory. */
while (pMem)
{
}
return VINF_SUCCESS;
}
/**
* Changes the default test instance for the calling thread.
*
* @returns IPRT status code.
*
* @param hNewDefaultTest The new default test. NIL_RTTEST is fine.
* @param phOldTest Where to store the old test handle. Optional.
*/
{
if (phOldTest)
}
{
if (!pszDupName)
return VERR_NO_STR_MEMORY;
return VINF_SUCCESS;
}
/**
* Allocate a block of guarded memory.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param cb The amount of memory to allocate.
* @param cbAlign The alignment of the returned block.
* @param fHead Head or tail optimized guard.
* @param ppvUser Where to return the pointer to the block.
*/
RTR3DECL(int) RTTestGuardedAlloc(RTTEST hTest, size_t cb, uint32_t cbAlign, bool fHead, void **ppvUser)
{
if (cbAlign == 0)
cbAlign = 1;
/*
* Allocate the record and block and initialize them.
*/
{
{
if (!fHead)
{
if (off)
{
}
}
/*
* Set up the guards and link the record.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
}
}
}
return rc;
}
/**
* Allocates a block of guarded memory where the guarded is immediately after
* the user memory.
*
* @returns Pointer to the allocated memory. NULL on failure.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param cb The amount of memory to allocate.
*/
{
void *pvUser;
if (RT_SUCCESS(rc))
return pvUser;
return NULL;
}
/**
* Allocates a block of guarded memory where the guarded is right in front of
* the user memory.
*
* @returns Pointer to the allocated memory. NULL on failure.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param cb The amount of memory to allocate.
*/
{
void *pvUser;
if (RT_SUCCESS(rc))
return pvUser;
return NULL;
}
/**
* Frees one block of guarded memory.
*
* The caller is responsible for unlinking it.
*
* @param pMem The memory record.
*/
{
int rc;
rc = RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ); AssertRC(rc);
rc = RTMemProtect(pMem->aGuards[1].pv, pMem->aGuards[1].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ); AssertRC(rc);
}
/**
* Frees a block of guarded memory.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pv The memory. NULL is ignored.
*/
{
if (!pv)
return VINF_SUCCESS;
/*
* Find it.
*/
{
{
if (pPrev)
else
rc = VINF_SUCCESS;
break;
}
}
return rc;
}
/**
* Outputs the formatted XML.
*
* @param pTest The test instance.
* @param pszFormat The format string.
* @param va The format arguments.
*/
{
if (pTest->fXmlEnabled)
{
char *pszStr;
if (pszStr)
{
}
}
}
/**
* Outputs the formatted XML.
*
* @param pTest The test instance.
* @param pszFormat The format string.
* @param ... The format arguments.
*/
{
}
/**
* Starts the XML stream.
*
* @param pTest The test instance.
* @param pszTest The test name.
*/
{
pTest->cXmlElements = 0;
if (pTest->fXmlEnabled)
{
}
}
/**
* Emit an XML element that doesn't have any value and instead ends immediately.
*
* The caller must own the instance lock.
*
* @param pTest The test instance.
* @param pszTag The element tag.
* @param pszAttrFmt The element attributes as a format string. Use
* NULL if none.
* @param va Format string arguments.
*/
static void rtTestXmlElemV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va)
{
if (pTest->fXmlEnabled)
{
if (!pszAttrFmt || !*pszAttrFmt)
else
{
}
}
}
/**
* Wrapper around rtTestXmlElemV.
*/
{
}
/**
* Starts a new XML element.
*
* The caller must own the instance lock.
*
* @param pTest The test instance.
* @param pszTag The element tag.
* @param pszAttrFmt The element attributes as a format string. Use
* NULL if none.
* @param va Format string arguments.
*/
static void rtTestXmlElemStartV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va)
{
/* Push it onto the stack. */
if (pTest->fXmlEnabled)
{
if (!pszAttrFmt || !*pszAttrFmt)
else
{
}
}
}
/**
* Wrapper around rtTestXmlElemStartV.
*/
{
}
/**
* Ends the current element.
*
* The caller must own the instance lock.
*
* @param pTest The test instance.
* @param pszTag The tag we're ending (chiefly for sanity
* checking).
*/
{
/* pop the element */
AssertReturnVoid(i > 0);
i--;
pTest->cXmlElements = i;
/* Do the closing. */
if (pTest->fXmlEnabled)
{
else
}
}
/**
* Ends the XML stream, closing all open elements.
*
* The caller must own the instance lock.
*
* @param pTest The test instance.
*/
{
if (pTest->fXmlEnabled)
{
/*
* Close all the elements and add the final TestEnd one to get a
* final timestamp and some certainty that the XML is valid.
*/
while (i-- > 1)
{
else
}
{
}
/*
* Close the XML outputs.
*/
{
}
{
}
pTest->fXmlEnabled = false;
}
pTest->cXmlElements = 0;
}
/**
* Output callback.
*
* @returns number of bytes written.
* @param pvArg User argument.
* @param pachChars Pointer to an array of utf-8 characters.
* @param cbChars Number of bytes in the character array pointed to by pachChars.
*/
{
if (cbChars)
{
do
{
/* insert prefix if at a newline. */
{
}
/* look for newline and write the stuff. */
if (!pchEnd)
{
break;
}
} while (cbChars);
}
else
return cch;
}
/**
* Internal output worker.
*
* Caller takes the lock.
*
* @returns Number of chars printed.
* @param pTest The test instance.
* @param pszFormat The message.
* @param va The arguments.
*/
{
}
/**
* Internal output worker.
*
* Caller takes the lock.
*
* @returns Number of chars printed.
* @param pTest The test instance.
* @param pszFormat The message.
* @param ... The arguments.
*/
{
return cch;
}
/**
* Test vprintf making sure the output starts on a new line.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param enmLevel Message importance level.
* @param pszFormat The message.
* @param va Arguments.
*/
{
int cch = 0;
{
}
return cch;
}
/**
* Test printf making sure the output starts on a new line.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param enmLevel Message importance level.
* @param pszFormat The message.
* @param ... Arguments.
*/
{
return cch;
}
/**
* Test vprintf, makes sure lines are prefixed and so forth.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param enmLevel Message importance level.
* @param pszFormat The message.
* @param va Arguments.
*/
{
int cch = 0;
return cch;
}
/**
* Test printf, makes sure lines are prefixed and so forth.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param enmLevel Message importance level.
* @param pszFormat The message.
* @param ... Arguments.
*/
{
return cch;
}
/**
* Prints the test banner.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
*/
{
}
/**
* Prints the result of a sub-test if necessary.
*
* @returns Number of chars printed.
* @param pTest The test instance.
* @remarks Caller own the test Lock.
*/
{
int cch = 0;
if ( !pTest->fSubTestReported
&& pTest->pszSubTest)
{
pTest->fSubTestReported = true;
if (!cErrors)
{
if (!pTest->fSubTestSkipped)
{
}
else
{
}
}
else
{
pTest->cSubTestsFailed++;
}
}
return cch;
}
/**
* RTTestSub and RTTestSubDone worker that cleans up the current (if any)
* sub test.
*
* @returns Number of chars printed.
* @param pTest The test instance.
* @remarks Caller own the test Lock.
*/
{
int cch = 0;
if (pTest->pszSubTest)
{
pTest->fSubTestReported = true;
}
return cch;
}
/**
* Summaries the test, destroys the test instance and return an exit code.
*
* @returns Test program exit code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
*/
{
{
}
else
{
}
return enmExitCode;
}
{
{
if (pszReasonFmt)
}
else
{
}
return enmExitCode;
}
{
return enmExitCode;
}
/**
* Starts a sub-test.
*
* This will perform an implicit RTTestSubDone() call if that has not been done
* since the last RTTestSub call.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszSubTest The sub-test name
*/
{
/* Cleanup, reporting if necessary previous sub test. */
/* Start new sub test. */
pTest->fSubTestSkipped = false;
pTest->fSubTestReported = false;
int cch = 0;
if (!pTest->fXmlTopTestDone)
{
pTest->fXmlTopTestDone = true;
}
return cch;
}
/**
* Format string version of RTTestSub.
*
* See RTTestSub for details.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszSubTestFmt The sub-test name format string.
* @param ... Arguments.
*/
{
return cch;
}
/**
* Format string version of RTTestSub.
*
* See RTTestSub for details.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszSubTestFmt The sub-test name format string.
* @param ... Arguments.
*/
{
char *pszSubTest;
if (pszSubTest)
{
return cch;
}
return 0;
}
/**
* Completes a sub-test.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
*/
{
return cch;
}
/**
* Prints an extended PASSED message, optional.
*
* This does not conclude the sub-test, it could be used to report the passing
* of a sub-sub-to-the-power-of-N-test.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszFormat The message. No trailing newline.
* @param va The arguments.
*/
{
int cch = 0;
{
}
return cch;
}
/**
* Prints an extended PASSED message, optional.
*
* This does not conclude the sub-test, it could be used to report the passing
* of a sub-sub-to-the-power-of-N-test.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszFormat The message. No trailing newline.
* @param ... The arguments.
*/
{
return cch;
}
{
pTest->fSubTestSkipped = true;
int cch = 0;
{
}
return cch;
}
{
return cch;
}
/**
* Gets the unit name.
*
* @returns Unit name.
* @param enmUnit The unit.
*/
{
switch (enmUnit)
{
case RTTESTUNIT_PCT: return "%";
case RTTESTUNIT_BYTES: return "bytes";
case RTTESTUNIT_BYTES_PER_SEC: return "bytes/s";
case RTTESTUNIT_KILOBYTES: return "KB";
case RTTESTUNIT_KILOBYTES_PER_SEC: return "KB/s";
case RTTESTUNIT_MEGABYTES: return "MB";
case RTTESTUNIT_MEGABYTES_PER_SEC: return "MB/s";
case RTTESTUNIT_PACKETS: return "packets";
case RTTESTUNIT_PACKETS_PER_SEC: return "packets/s";
case RTTESTUNIT_FRAMES: return "frames";
case RTTESTUNIT_FRAMES_PER_SEC: return "frames/";
case RTTESTUNIT_OCCURRENCES: return "occurrences";
case RTTESTUNIT_OCCURRENCES_PER_SEC: return "occurrences/s";
case RTTESTUNIT_ROUND_TRIP: return "roundtrips";
case RTTESTUNIT_CALLS: return "calls";
case RTTESTUNIT_CALLS_PER_SEC: return "calls/s";
case RTTESTUNIT_SECS: return "s";
case RTTESTUNIT_MS: return "ms";
case RTTESTUNIT_NS: return "ns";
case RTTESTUNIT_NS_PER_CALL: return "ns/call";
case RTTESTUNIT_NS_PER_FRAME: return "ns/frame";
case RTTESTUNIT_NS_PER_OCCURRENCE: return "ns/occurrences";
case RTTESTUNIT_NS_PER_PACKET: return "ns/packet";
case RTTESTUNIT_NS_PER_ROUND_TRIP: return "ns/roundtrips";
case RTTESTUNIT_INSTRS: return "ins";
case RTTESTUNIT_INSTRS_PER_SEC: return "ins/sec";
case RTTESTUNIT_NONE: return "";
case RTTESTUNIT_PP1K: return "pp1k";
case RTTESTUNIT_PP10K: return "pp10k";
case RTTESTUNIT_PPM: return "ppm";
case RTTESTUNIT_PPB: return "ppb";
/* No default so gcc helps us keep this up to date. */
case RTTESTUNIT_INVALID:
case RTTESTUNIT_END:
break;
}
return "unknown";
}
{
return VINF_SUCCESS;
}
RTR3DECL(int) RTTestValueF(RTTEST hTest, uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, ...)
{
return rc;
}
RTR3DECL(int) RTTestValueV(RTTEST hTest, uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, va_list va)
{
char *pszName;
if (!pszName)
return VERR_NO_MEMORY;
return rc;
}
/**
* Increments the error counter.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
*/
{
return VINF_SUCCESS;
}
/**
* Get the current error count.
*
* @returns The error counter, UINT32_MAX if no valid test handle.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
*/
{
}
{
}
/**
* Increments the error counter and prints a failure message.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszFormat The message. No trailing newline.
* @param va The arguments.
*/
{
int cch = 0;
{
}
return cch;
}
/**
* Increments the error counter and prints a failure message.
*
* @returns IPRT status code.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszFormat The message. No trailing newline.
* @param ... The arguments.
*/
{
return cch;
}
/**
* Same as RTTestPrintfV with RTTESTLVL_FAILURE.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszFormat The message.
* @param va Arguments.
*/
{
}
/**
* Same as RTTestPrintf with RTTESTLVL_FAILURE.
*
* @returns Number of chars printed.
* @param hTest The test handle. If NIL_RTTEST we'll use the one
* associated with the calling thread.
* @param pszFormat The message.
* @param ... Arguments.
*/
{
return cch;
}