VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/test.cpp@ 103141

Last change on this file since 103141 was 103005, checked in by vboxsync, 8 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.4 KB
Line 
1/* $Id: test.cpp 103005 2024-01-23 23:55:58Z vboxsync $ */
2/** @file
3 * IPRT - Testcase Framework.
4 */
5
6/*
7 * Copyright (C) 2009-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/test.h>
42
43#include <iprt/asm.h>
44#include <iprt/asm-mem.h>
45#include <iprt/critsect.h>
46#include <iprt/env.h>
47#include <iprt/err.h>
48#include <iprt/file.h>
49#include <iprt/initterm.h>
50#include <iprt/mem.h>
51#include <iprt/once.h>
52#include <iprt/param.h>
53#include <iprt/pipe.h>
54#include <iprt/string.h>
55#include <iprt/stream.h>
56#include <iprt/system.h>
57
58#include "internal/magics.h"
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64/**
65 * Guarded memory allocation record.
66 */
67typedef struct RTTESTGUARDEDMEM
68{
69 /** Pointer to the next record. */
70 struct RTTESTGUARDEDMEM *pNext;
71 /** The address we return to the user. */
72 void *pvUser;
73 /** The base address of the allocation. */
74 void *pvAlloc;
75 /** The size of the allocation. */
76 size_t cbAlloc;
77 /** Guards. */
78 struct
79 {
80 /** The guard address. */
81 void *pv;
82 /** The guard size. */
83 size_t cb;
84 } aGuards[2];
85} RTTESTGUARDEDMEM;
86/** Pointer to an guarded memory allocation. */
87typedef RTTESTGUARDEDMEM *PRTTESTGUARDEDMEM;
88
89/**
90 * Test instance structure.
91 */
92typedef struct RTTESTINT
93{
94 /** Magic. */
95 uint32_t u32Magic;
96 /** The number of errors. */
97 volatile uint32_t cErrors;
98 /** The test name. */
99 const char *pszTest;
100 /** The length of the test name. */
101 size_t cchTest;
102 /** The size of a guard. Multiple of system page size. */
103 uint32_t cbGuard;
104 /** The verbosity level. */
105 RTTESTLVL enmMaxLevel;
106 /** The creation flags. */
107 uint32_t fFlags;
108
109
110 /** Critical section serializing output. */
111 RTCRITSECT OutputLock;
112 /** The output stream. */
113 PRTSTREAM pOutStrm;
114 /** Whether we're currently at a newline. */
115 bool fNewLine;
116
117
118 /** Critical section serializing access to the members following it. */
119 RTCRITSECT Lock;
120
121 /** The list of guarded memory allocations. */
122 PRTTESTGUARDEDMEM pGuardedMem;
123
124 /** The current sub-test. */
125 const char *pszSubTest;
126 /** The length of the sub-test name. */
127 size_t cchSubTest;
128 /** Whether the current subtest should figure as 'SKIPPED'. */
129 bool fSubTestSkipped;
130 /** Whether we've reported the sub-test result or not. */
131 bool fSubTestReported;
132 /** The start error count of the current subtest. */
133 uint32_t cSubTestAtErrors;
134
135 /** The number of sub tests. */
136 uint32_t cSubTests;
137 /** The number of sub tests that failed. */
138 uint32_t cSubTestsFailed;
139
140 /** Error context message. */
141 char *pszErrCtx;
142
143 /** Set if XML output is enabled. */
144 bool fXmlEnabled;
145 /** Set if we omit the top level test in the XML report. */
146 bool fXmlOmitTopTest;
147 /** Set if we've reported the top test (for RTTEST_C_XML_DELAY_TOP_TEST). */
148 bool fXmlTopTestDone;
149 enum {
150 kXmlPos_ValueStart,
151 kXmlPos_Value,
152 kXmlPos_ElementEnd
153 } eXmlState;
154 /** Test pipe for the XML output stream going to the server. */
155 RTPIPE hXmlPipe;
156 /** File where the XML output stream might be directed. */
157 RTFILE hXmlFile;
158 /** The number of XML elements on the stack. */
159 size_t cXmlElements;
160 /** XML element stack. */
161 const char *apszXmlElements[10];
162
163 /** Number of times assertions has been disabled and quieted. */
164 uint32_t volatile cAssertionsDisabledAndQuieted;
165 /** Saved RTAssertSetQuiet return code. */
166 bool fAssertSavedQuiet;
167 /** Saved RTAssertSetMayPanic return code. */
168 bool fAssertSavedMayPanic;
169} RTTESTINT;
170/** Pointer to a test instance. */
171typedef RTTESTINT *PRTTESTINT;
172
173
174/*********************************************************************************************************************************
175* Defined Constants And Macros *
176*********************************************************************************************************************************/
177/** Validate a test instance. */
178#define RTTEST_VALID_RETURN(pTest) \
179 do { \
180 AssertPtrReturn(pTest, VERR_INVALID_HANDLE); \
181 AssertReturn(pTest->u32Magic == RTTESTINT_MAGIC, VERR_INVALID_HANDLE); \
182 } while (0)
183
184/** Gets and validates a test instance.
185 * If the handle is nil, we will try retrieve it from the test TLS entry.
186 */
187#define RTTEST_GET_VALID_RETURN(pTest) \
188 do { \
189 if (pTest == NIL_RTTEST) \
190 pTest = (PRTTESTINT)RTTlsGet(g_iTestTls); \
191 AssertPtrReturn(pTest, VERR_INVALID_HANDLE); \
192 AssertReturn(pTest->u32Magic == RTTESTINT_MAGIC, VERR_INVALID_MAGIC); \
193 } while (0)
194
195
196/** Gets and validates a test instance.
197 * If the handle is nil, we will try retrieve it from the test TLS entry.
198 */
199#define RTTEST_GET_VALID_RETURN_RC(pTest, rc) \
200 do { \
201 if (pTest == NIL_RTTEST) \
202 pTest = (PRTTESTINT)RTTlsGet(g_iTestTls); \
203 AssertPtrReturn(pTest, (rc)); \
204 AssertReturn(pTest->u32Magic == RTTESTINT_MAGIC, (rc)); \
205 } while (0)
206
207
208/*********************************************************************************************************************************
209* Internal Functions *
210*********************************************************************************************************************************/
211static void rtTestGuardedFreeOne(PRTTESTGUARDEDMEM pMem);
212static int rtTestPrintf(PRTTESTINT pTest, const char *pszFormat, ...);
213static void rtTestXmlStart(PRTTESTINT pTest, const char *pszTest);
214static void rtTestXmlElemV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va);
215static void rtTestXmlElem(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...);
216static void rtTestXmlElemStartV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va);
217static void rtTestXmlElemStart(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...);
218static void rtTestXmlElemEnd(PRTTESTINT pTest, const char *pszTag);
219static void rtTestXmlEnd(PRTTESTINT pTest);
220
221
222/*********************************************************************************************************************************
223* Global Variables *
224*********************************************************************************************************************************/
225/** For serializing TLS init. */
226static RTONCE g_TestInitOnce = RTONCE_INITIALIZER;
227/** Our TLS entry. */
228static RTTLS g_iTestTls = NIL_RTTLS;
229
230
231
232/**
233 * Init TLS index once.
234 *
235 * @returns IPRT status code.
236 * @param pvUser Ignored.
237 */
238static DECLCALLBACK(int32_t) rtTestInitOnce(void *pvUser)
239{
240 NOREF(pvUser);
241 return RTTlsAllocEx(&g_iTestTls, NULL);
242}
243
244
245RTR3DECL(int) RTTestCreateEx(const char *pszTest, uint32_t fFlags, RTTESTLVL enmMaxLevel,
246 RTHCINTPTR iNativeTestPipe, const char *pszXmlFile, PRTTEST phTest)
247{
248 AssertReturn(!(fFlags & ~RTTEST_C_VALID_MASK), VERR_INVALID_PARAMETER);
249 AssertPtrNull(phTest);
250 AssertPtrNull(pszXmlFile);
251 /* RTTESTLVL_INVALID is valid! */
252 AssertReturn(enmMaxLevel >= RTTESTLVL_INVALID && enmMaxLevel < RTTESTLVL_END, VERR_INVALID_PARAMETER);
253
254 /*
255 * Global init.
256 */
257 int rc = RTOnce(&g_TestInitOnce, rtTestInitOnce, NULL);
258 if (RT_FAILURE(rc))
259 return rc;
260
261 /*
262 * Create the instance.
263 */
264 PRTTESTINT pTest = (PRTTESTINT)RTMemAllocZ(sizeof(*pTest));
265 if (!pTest)
266 return VERR_NO_MEMORY;
267 pTest->u32Magic = RTTESTINT_MAGIC;
268 pTest->pszTest = RTStrDup(pszTest);
269 pTest->cchTest = strlen(pszTest);
270 pTest->cbGuard = RTSystemGetPageSize() * 7;
271 pTest->enmMaxLevel = enmMaxLevel == RTTESTLVL_INVALID ? RTTESTLVL_INFO : enmMaxLevel;
272 pTest->fFlags = fFlags;
273
274 pTest->pOutStrm = g_pStdOut;
275 pTest->fNewLine = true;
276
277 pTest->pGuardedMem = NULL;
278
279 pTest->pszSubTest = NULL;
280 pTest->cchSubTest = 0;
281 pTest->fSubTestSkipped = false;
282 pTest->fSubTestReported = true;
283 pTest->cSubTestAtErrors = 0;
284 pTest->cSubTests = 0;
285 pTest->cSubTestsFailed = 0;
286
287 pTest->fXmlEnabled = false;
288 pTest->fXmlTopTestDone = false;
289 pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
290 pTest->hXmlPipe = NIL_RTPIPE;
291 pTest->hXmlFile = NIL_RTFILE;
292 pTest->cXmlElements = 0;
293 pTest->cAssertionsDisabledAndQuieted = 0;
294 pTest->fAssertSavedMayPanic = true;
295 pTest->fAssertSavedQuiet = false;
296
297 rc = RTCritSectInit(&pTest->Lock);
298 if (RT_SUCCESS(rc))
299 {
300 rc = RTCritSectInit(&pTest->OutputLock);
301 if (RT_SUCCESS(rc))
302 {
303 /*
304 * Associate it with our TLS entry unless there is already
305 * an instance there.
306 */
307 if ( !(fFlags & RTTEST_C_NO_TLS)
308 && !RTTlsGet(g_iTestTls))
309 rc = RTTlsSet(g_iTestTls, pTest);
310 if (RT_SUCCESS(rc))
311 {
312 /*
313 * Output level override?
314 */
315 char szEnvVal[RTPATH_MAX];
316 if ((fFlags & RTTEST_C_USE_ENV) && enmMaxLevel == RTTESTLVL_INVALID)
317 {
318 rc = RTEnvGetEx(RTENV_DEFAULT, "IPRT_TEST_MAX_LEVEL", szEnvVal, sizeof(szEnvVal), NULL);
319 if (RT_SUCCESS(rc))
320 {
321 char *pszMaxLevel = RTStrStrip(szEnvVal);
322 if (!strcmp(pszMaxLevel, "all"))
323 pTest->enmMaxLevel = RTTESTLVL_DEBUG;
324 if (!strcmp(pszMaxLevel, "quiet"))
325 pTest->enmMaxLevel = RTTESTLVL_FAILURE;
326 else if (!strcmp(pszMaxLevel, "debug"))
327 pTest->enmMaxLevel = RTTESTLVL_DEBUG;
328 else if (!strcmp(pszMaxLevel, "info"))
329 pTest->enmMaxLevel = RTTESTLVL_INFO;
330 else if (!strcmp(pszMaxLevel, "sub_test"))
331 pTest->enmMaxLevel = RTTESTLVL_SUB_TEST;
332 else if (!strcmp(pszMaxLevel, "failure"))
333 pTest->enmMaxLevel = RTTESTLVL_FAILURE;
334 }
335 else if (rc != VERR_ENV_VAR_NOT_FOUND)
336 RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTEnvGetEx(IPRT_TEST_MAX_LEVEL) -> %Rrc\n", pszTest, rc);
337 }
338
339 /*
340 * Any test driver we are connected or should connect to?
341 */
342 if (!(fFlags & RTTEST_C_NO_XML_REPORTING_PIPE))
343 {
344 if ( (fFlags & RTTEST_C_USE_ENV)
345 && iNativeTestPipe == -1)
346 {
347 rc = RTEnvGetEx(RTENV_DEFAULT, "IPRT_TEST_PIPE", szEnvVal, sizeof(szEnvVal), NULL);
348 if (RT_SUCCESS(rc))
349 {
350#if ARCH_BITS == 64
351 rc = RTStrToInt64Full(szEnvVal, 0, &iNativeTestPipe);
352#else
353 rc = RTStrToInt32Full(szEnvVal, 0, &iNativeTestPipe);
354#endif
355 if (RT_FAILURE(rc))
356 {
357 RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTStrToInt32Full(\"%s\") -> %Rrc\n",
358 pszTest, szEnvVal, rc);
359 iNativeTestPipe = -1;
360 }
361 }
362 else if (rc != VERR_ENV_VAR_NOT_FOUND)
363 RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTEnvGetEx(IPRT_TEST_PIPE) -> %Rrc\n", pszTest, rc);
364 }
365 if (iNativeTestPipe != -1)
366 {
367 rc = RTPipeFromNative(&pTest->hXmlPipe, iNativeTestPipe, RTPIPE_N_WRITE);
368 if (RT_SUCCESS(rc))
369 pTest->fXmlEnabled = true;
370 else
371 {
372 RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTPipeFromNative(,%p,WRITE) -> %Rrc\n",
373 pszTest, iNativeTestPipe, rc);
374 pTest->hXmlPipe = NIL_RTPIPE;
375 }
376 }
377 }
378
379 /*
380 * Any test file we should write the test report to?
381 */
382 if (!(fFlags & RTTEST_C_NO_XML_REPORTING_FILE))
383 {
384 if ((fFlags & RTTEST_C_USE_ENV) && pszXmlFile == NULL)
385 {
386 rc = RTEnvGetEx(RTENV_DEFAULT, "IPRT_TEST_FILE", szEnvVal, sizeof(szEnvVal), NULL);
387 if (RT_SUCCESS(rc))
388 pszXmlFile = szEnvVal;
389 else if (rc != VERR_ENV_VAR_NOT_FOUND)
390 RTStrmPrintf(g_pStdErr, "%s: test file error: RTEnvGetEx(IPRT_TEST_MAX_LEVEL) -> %Rrc\n", pszTest, rc);
391 }
392 if (pszXmlFile && *pszXmlFile)
393 {
394 rc = RTFileOpen(&pTest->hXmlFile, pszXmlFile,
395 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE);
396 if (RT_SUCCESS(rc))
397 pTest->fXmlEnabled = true;
398 else
399 {
400 RTStrmPrintf(g_pStdErr, "%s: test file error: RTFileOpen(,\"%s\",) -> %Rrc\n",
401 pszTest, pszXmlFile, rc);
402 pTest->hXmlFile = NIL_RTFILE;
403 }
404 }
405 }
406
407 /*
408 * What do we report in the XML stream/file.?
409 */
410 pTest->fXmlOmitTopTest = (fFlags & RTTEST_C_XML_OMIT_TOP_TEST)
411 || ( (fFlags & RTTEST_C_USE_ENV)
412 && RTEnvExistEx(RTENV_DEFAULT, "IPRT_TEST_OMIT_TOP_TEST"));
413
414 /*
415 * Tell the test driver that we're up to.
416 */
417 rtTestXmlStart(pTest, pszTest);
418
419 *phTest = pTest;
420 return VINF_SUCCESS;
421 }
422
423 /* bail out. */
424 RTCritSectDelete(&pTest->OutputLock);
425 }
426 RTCritSectDelete(&pTest->Lock);
427 }
428 pTest->u32Magic = 0;
429 RTStrFree((char *)pTest->pszTest);
430 RTMemFree(pTest);
431 return rc;
432}
433
434
435RTR3DECL(int) RTTestCreate(const char *pszTest, PRTTEST phTest)
436{
437 return RTTestCreateEx(pszTest, RTTEST_C_USE_ENV, RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, NULL /*pszXmlFile*/, phTest);
438}
439
440
441RTR3DECL(int) RTTestCreateChild(const char *pszTest, PRTTEST phTest)
442{
443 return RTTestCreateEx(pszTest, RTTEST_C_USE_ENV | RTTEST_C_NO_XML_REPORTING,
444 RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, NULL /*pszXmlFile*/, phTest);
445}
446
447
448RTR3DECL(RTEXITCODE) RTTestInitAndCreate(const char *pszTest, PRTTEST phTest)
449{
450 int rc = RTR3InitExeNoArguments(0);
451 if (RT_FAILURE(rc))
452 {
453 RTStrmPrintf(g_pStdErr, "%s: fatal error: RTR3InitExeNoArguments failed with rc=%Rrc\n", pszTest, rc);
454 return RTEXITCODE_INIT;
455 }
456
457 rc = RTTestCreate(pszTest, phTest);
458 if (RT_FAILURE(rc))
459 {
460 RTStrmPrintf(g_pStdErr, "%s: fatal error: RTTestCreate failed with rc=%Rrc\n", pszTest, rc);
461 return RTEXITCODE_INIT;
462 }
463 return RTEXITCODE_SUCCESS;
464}
465
466
467RTR3DECL(RTEXITCODE) RTTestInitExAndCreate(int cArgs, char ***ppapszArgs, uint32_t fRtInit, const char *pszTest, PRTTEST phTest)
468{
469 int rc;
470 if (cArgs <= 0 && ppapszArgs == NULL)
471 rc = RTR3InitExeNoArguments(fRtInit);
472 else
473 rc = RTR3InitExe(cArgs, ppapszArgs, fRtInit);
474 if (RT_FAILURE(rc))
475 {
476 RTStrmPrintf(g_pStdErr, "%s: fatal error: RTR3InitExe(,,%#x) failed with rc=%Rrc\n", pszTest, fRtInit, rc);
477 return RTEXITCODE_INIT;
478 }
479
480 rc = RTTestCreate(pszTest, phTest);
481 if (RT_FAILURE(rc))
482 {
483 RTStrmPrintf(g_pStdErr, "%s: fatal error: RTTestCreate failed with rc=%Rrc\n", pszTest, rc);
484 return RTEXITCODE_INIT;
485 }
486 return RTEXITCODE_SUCCESS;
487}
488
489
490RTR3DECL(int) RTTestDestroy(RTTEST hTest)
491{
492 /*
493 * Validate
494 */
495 if (hTest == NIL_RTTEST)
496 return VINF_SUCCESS;
497 RTTESTINT *pTest = hTest;
498 RTTEST_VALID_RETURN(pTest);
499
500 /*
501 * Make sure we end with a new line and have finished up the XML.
502 */
503 if (!pTest->fNewLine)
504 rtTestPrintf(pTest, "\n");
505 rtTestXmlEnd(pTest);
506
507 /*
508 * Clean up.
509 */
510 if ((RTTESTINT *)RTTlsGet(g_iTestTls) == pTest)
511 RTTlsSet(g_iTestTls, NULL);
512
513 ASMAtomicWriteU32(&pTest->u32Magic, ~RTTESTINT_MAGIC);
514 RTCritSectDelete(&pTest->Lock);
515 RTCritSectDelete(&pTest->OutputLock);
516
517 /* free guarded memory. */
518 PRTTESTGUARDEDMEM pMem = pTest->pGuardedMem;
519 pTest->pGuardedMem = NULL;
520 while (pMem)
521 {
522 PRTTESTGUARDEDMEM pFree = pMem;
523 pMem = pMem->pNext;
524 rtTestGuardedFreeOne(pFree);
525 }
526
527 RTStrFree((char *)pTest->pszSubTest);
528 pTest->pszSubTest = NULL;
529 RTStrFree((char *)pTest->pszTest);
530 pTest->pszTest = NULL;
531 RTStrFree(pTest->pszErrCtx);
532 pTest->pszErrCtx = NULL;
533 RTMemFree(pTest);
534 return VINF_SUCCESS;
535}
536
537
538RTR3DECL(int) RTTestSetDefault(RTTEST hNewDefaultTest, PRTTEST phOldTest)
539{
540 if (phOldTest)
541 *phOldTest = (RTTEST)RTTlsGet(g_iTestTls);
542 return RTTlsSet(g_iTestTls, hNewDefaultTest);
543}
544
545
546RTR3DECL(int) RTTestChangeName(RTTEST hTest, const char *pszName)
547{
548 PRTTESTINT pTest = hTest;
549 RTTEST_GET_VALID_RETURN(pTest);
550 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
551 AssertReturn(*pszName, VERR_INVALID_PARAMETER);
552
553 size_t cchName = strlen(pszName);
554 AssertReturn(cchName < 128, VERR_INVALID_PARAMETER);
555 char *pszDupName = RTStrDup(pszName);
556 if (!pszDupName)
557 return VERR_NO_STR_MEMORY;
558
559 RTCritSectEnter(&pTest->Lock);
560 RTCritSectEnter(&pTest->OutputLock);
561
562 char *pszOldName = (char *)pTest->pszTest;
563 pTest->pszTest = pszDupName;
564 pTest->cchTest = cchName;
565
566 RTCritSectLeave(&pTest->OutputLock);
567 RTCritSectLeave(&pTest->Lock);
568
569 RTStrFree(pszOldName);
570 return VINF_SUCCESS;
571}
572
573
574RTR3DECL(int) RTTestGuardedAlloc(RTTEST hTest, size_t cb, uint32_t cbAlign, bool fHead, void **ppvUser)
575{
576 PRTTESTINT pTest = hTest;
577 RTTEST_GET_VALID_RETURN(pTest);
578 if (cbAlign == 0)
579 cbAlign = 1;
580 uint32_t const cbPage = RTSystemGetPageSize();
581 AssertReturn(cbAlign <= cbPage, VERR_INVALID_PARAMETER);
582 AssertReturn(cbAlign == (UINT32_C(1) << (ASMBitFirstSetU32(cbAlign) - 1)), VERR_INVALID_PARAMETER);
583
584 /*
585 * Allocate the record and block and initialize them.
586 */
587 int rc = VERR_NO_MEMORY;
588 PRTTESTGUARDEDMEM pMem = (PRTTESTGUARDEDMEM)RTMemAlloc(sizeof(*pMem));
589 if (RT_LIKELY(pMem))
590 {
591 size_t const cbAligned = RT_ALIGN_Z(cb, cbPage);
592 pMem->aGuards[0].cb = pMem->aGuards[1].cb = pTest->cbGuard;
593 pMem->cbAlloc = pMem->aGuards[0].cb + pMem->aGuards[1].cb + cbAligned;
594 pMem->pvAlloc = RTMemPageAlloc(pMem->cbAlloc);
595 if (pMem->pvAlloc)
596 {
597 pMem->aGuards[0].pv = pMem->pvAlloc;
598 pMem->pvUser = (uint8_t *)pMem->pvAlloc + pMem->aGuards[0].cb;
599 pMem->aGuards[1].pv = (uint8_t *)pMem->pvUser + cbAligned;
600 if (!fHead)
601 {
602 size_t off = cb & RTSystemGetPageOffsetMask();
603 if (off)
604 {
605 off = cbPage - RT_ALIGN_Z(off, cbAlign);
606 pMem->pvUser = (uint8_t *)pMem->pvUser + off;
607 }
608 }
609
610 /*
611 * Set up the guards and link the record.
612 */
613 ASMMemFill32(pMem->aGuards[0].pv, pMem->aGuards[0].cb, 0xdeadbeef);
614 ASMMemFill32(pMem->aGuards[1].pv, pMem->aGuards[1].cb, 0xdeadbeef);
615 rc = RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_NONE);
616 if (RT_SUCCESS(rc))
617 {
618 rc = RTMemProtect(pMem->aGuards[1].pv, pMem->aGuards[1].cb, RTMEM_PROT_NONE);
619 if (RT_SUCCESS(rc))
620 {
621 *ppvUser = pMem->pvUser;
622
623 RTCritSectEnter(&pTest->Lock);
624 pMem->pNext = pTest->pGuardedMem;
625 pTest->pGuardedMem = pMem;
626 RTCritSectLeave(&pTest->Lock);
627
628 return VINF_SUCCESS;
629 }
630
631 RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ);
632 }
633
634 RTMemPageFree(pMem->pvAlloc, pMem->cbAlloc);
635 }
636 RTMemFree(pMem);
637 }
638 return rc;
639}
640
641
642RTR3DECL(void *) RTTestGuardedAllocTail(RTTEST hTest, size_t cb)
643{
644 void *pvUser;
645 int rc = RTTestGuardedAlloc(hTest, cb, 1 /* cbAlign */, false /* fHead */, &pvUser);
646 if (RT_SUCCESS(rc))
647 return pvUser;
648 return NULL;
649}
650
651
652RTR3DECL(void *) RTTestGuardedAllocHead(RTTEST hTest, size_t cb)
653{
654 void *pvUser;
655 int rc = RTTestGuardedAlloc(hTest, cb, 1 /* cbAlign */, true /* fHead */, &pvUser);
656 if (RT_SUCCESS(rc))
657 return pvUser;
658 return NULL;
659}
660
661
662/**
663 * Frees one block of guarded memory.
664 *
665 * The caller is responsible for unlinking it.
666 *
667 * @param pMem The memory record.
668 */
669static void rtTestGuardedFreeOne(PRTTESTGUARDEDMEM pMem)
670{
671 int rc;
672 rc = RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ); AssertRC(rc);
673 rc = RTMemProtect(pMem->aGuards[1].pv, pMem->aGuards[1].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ); AssertRC(rc);
674 RTMemPageFree(pMem->pvAlloc, pMem->cbAlloc);
675 RTMemFree(pMem);
676}
677
678
679RTR3DECL(int) RTTestGuardedFree(RTTEST hTest, void *pv)
680{
681 PRTTESTINT pTest = hTest;
682 RTTEST_GET_VALID_RETURN(pTest);
683 if (!pv)
684 return VINF_SUCCESS;
685
686 /*
687 * Find it.
688 */
689 int rc = VERR_INVALID_POINTER;
690 PRTTESTGUARDEDMEM pPrev = NULL;
691
692 RTCritSectEnter(&pTest->Lock);
693 for (PRTTESTGUARDEDMEM pMem = pTest->pGuardedMem; pMem; pMem = pMem->pNext)
694 {
695 if (pMem->pvUser == pv)
696 {
697 if (pPrev)
698 pPrev->pNext = pMem->pNext;
699 else
700 pTest->pGuardedMem = pMem->pNext;
701 rtTestGuardedFreeOne(pMem);
702 rc = VINF_SUCCESS;
703 break;
704 }
705 pPrev = pMem;
706 }
707 RTCritSectLeave(&pTest->Lock);
708
709 return rc;
710}
711
712
713/**
714 * Outputs the formatted XML.
715 *
716 * @param pTest The test instance.
717 * @param pszFormat The format string.
718 * @param va The format arguments.
719 */
720static void rtTestXmlOutputV(PRTTESTINT pTest, const char *pszFormat, va_list va)
721{
722 if (pTest->fXmlEnabled)
723 {
724 char *pszStr;
725 ssize_t cchStr = RTStrAPrintfV(&pszStr, pszFormat, va);
726 if (pszStr)
727 {
728 if (pTest->hXmlPipe != NIL_RTPIPE)
729 RTPipeWriteBlocking(pTest->hXmlPipe, pszStr, cchStr, NULL);
730 if (pTest->hXmlFile != NIL_RTFILE)
731 RTFileWrite(pTest->hXmlFile, pszStr, cchStr, NULL);
732 RTStrFree(pszStr);
733 }
734 }
735}
736
737
738/**
739 * Outputs the formatted XML.
740 *
741 * @param pTest The test instance.
742 * @param pszFormat The format string.
743 * @param ... The format arguments.
744 */
745static void rtTestXmlOutput(PRTTESTINT pTest, const char *pszFormat, ...)
746{
747 va_list va;
748 va_start(va, pszFormat);
749 rtTestXmlOutputV(pTest, pszFormat, va);
750 va_end(va);
751}
752
753
754/**
755 * Starts the XML stream.
756 *
757 * @param pTest The test instance.
758 * @param pszTest The test name.
759 */
760static void rtTestXmlStart(PRTTESTINT pTest, const char *pszTest)
761{
762 pTest->cXmlElements = 0;
763 if (pTest->fXmlEnabled)
764 {
765 rtTestXmlOutput(pTest, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
766 pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
767 pTest->fXmlTopTestDone = !(pTest->fFlags & RTTEST_C_XML_DELAY_TOP_TEST) || pTest->fXmlOmitTopTest;
768 if (pTest->fXmlTopTestDone && !pTest->fXmlOmitTopTest)
769 rtTestXmlElemStart(pTest, "Test", "name=%RMas", pszTest);
770 }
771}
772
773
774/**
775 * Emit an XML element that doesn't have any value and instead ends immediately.
776 *
777 * The caller must own the instance lock.
778 *
779 * @param pTest The test instance.
780 * @param pszTag The element tag.
781 * @param pszAttrFmt The element attributes as a format string. Use
782 * NULL if none.
783 * @param va Format string arguments.
784 */
785static void rtTestXmlElemV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va)
786{
787 if (pTest->fXmlEnabled)
788 {
789 RTTIMESPEC TimeSpec;
790 RTTIME Time;
791 char szTS[80];
792 RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&TimeSpec)), szTS, sizeof(szTS));
793
794 if (pTest->eXmlState != RTTESTINT::kXmlPos_ElementEnd)
795 rtTestXmlOutput(pTest, "\n");
796
797 if (!pszAttrFmt || !*pszAttrFmt)
798 rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas/>\n",
799 pTest->cXmlElements * 2, "", pszTag, szTS);
800 else
801 {
802 va_list va2;
803 va_copy(va2, va);
804 rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas %N/>\n",
805 pTest->cXmlElements * 2, "", pszTag, szTS, pszAttrFmt, &va2);
806 va_end(va2);
807 }
808 pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
809 }
810}
811
812
813/**
814 * Wrapper around rtTestXmlElemV.
815 */
816static void rtTestXmlElem(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...)
817{
818 va_list va;
819 va_start(va, pszAttrFmt);
820 rtTestXmlElemV(pTest, pszTag, pszAttrFmt, va);
821 va_end(va);
822}
823
824
825/**
826 * Starts a new XML element.
827 *
828 * The caller must own the instance lock.
829 *
830 * @param pTest The test instance.
831 * @param pszTag The element tag.
832 * @param pszAttrFmt The element attributes as a format string. Use
833 * NULL if none.
834 * @param va Format string arguments.
835 */
836static void rtTestXmlElemStartV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va)
837{
838 /* Push it onto the stack. */
839 size_t i = pTest->cXmlElements;
840 AssertReturnVoid(i < RT_ELEMENTS(pTest->apszXmlElements));
841 pTest->apszXmlElements[i] = pszTag;
842 pTest->cXmlElements = i + 1;
843
844 if (pTest->fXmlEnabled)
845 {
846 RTTIMESPEC TimeSpec;
847 RTTIME Time;
848 char szTS[80];
849 RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&TimeSpec)), szTS, sizeof(szTS));
850
851 if (pTest->eXmlState != RTTESTINT::kXmlPos_ElementEnd)
852 rtTestXmlOutput(pTest, "\n");
853
854 if (!pszAttrFmt || !*pszAttrFmt)
855 rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas>",
856 i * 2, "", pszTag, szTS);
857 else
858 {
859 va_list va2;
860 va_copy(va2, va);
861 rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas %N>",
862 i * 2, "", pszTag, szTS, pszAttrFmt, &va2);
863 va_end(va2);
864 }
865 pTest->eXmlState = RTTESTINT::kXmlPos_ValueStart;
866 }
867}
868
869
870/**
871 * Wrapper around rtTestXmlElemStartV.
872 */
873static void rtTestXmlElemStart(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...)
874{
875 va_list va;
876 va_start(va, pszAttrFmt);
877 rtTestXmlElemStartV(pTest, pszTag, pszAttrFmt, va);
878 va_end(va);
879}
880
881
882/**
883 * Ends the current element.
884 *
885 * The caller must own the instance lock.
886 *
887 * @param pTest The test instance.
888 * @param pszTag The tag we're ending (chiefly for sanity
889 * checking).
890 */
891static void rtTestXmlElemEnd(PRTTESTINT pTest, const char *pszTag)
892{
893 /* pop the element */
894 size_t i = pTest->cXmlElements;
895 AssertReturnVoid(i > 0);
896 i--;
897 AssertReturnVoid(!strcmp(pszTag, pTest->apszXmlElements[i]));
898 pTest->cXmlElements = i;
899
900 /* Do the closing. */
901 if (pTest->fXmlEnabled)
902 {
903 if (pTest->eXmlState == RTTESTINT::kXmlPos_ValueStart)
904 rtTestXmlOutput(pTest, "\n%*s</%s>\n", i * 2, "", pszTag);
905 else if (pTest->eXmlState == RTTESTINT::kXmlPos_ElementEnd)
906 rtTestXmlOutput(pTest, "%*s</%s>\n", i * 2, "", pszTag);
907 else
908 rtTestXmlOutput(pTest, "</%s>\n", pszTag);
909 pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
910 }
911}
912
913
914/**
915 * Ends the XML stream, closing all open elements.
916 *
917 * The caller must own the instance lock.
918 *
919 * @param pTest The test instance.
920 */
921static void rtTestXmlEnd(PRTTESTINT pTest)
922{
923 if (pTest->fXmlEnabled)
924 {
925 /*
926 * Close all the elements and add the final TestEnd one to get a
927 * final timestamp and some certainty that the XML is valid.
928 */
929 size_t i = pTest->cXmlElements;
930 AssertReturnVoid(i > 0 || pTest->fXmlOmitTopTest || !pTest->fXmlTopTestDone);
931 while (i-- > 1)
932 {
933 const char *pszTag = pTest->apszXmlElements[pTest->cXmlElements];
934 if (pTest->eXmlState == RTTESTINT::kXmlPos_ValueStart)
935 rtTestXmlOutput(pTest, "\n%*s</%s>\n", i * 2, "", pszTag);
936 else if (pTest->eXmlState == RTTESTINT::kXmlPos_ElementEnd)
937 rtTestXmlOutput(pTest, "%*s</%s>\n", i * 2, "", pszTag);
938 else
939 rtTestXmlOutput(pTest, "</%s>\n", pszTag);
940 pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
941 }
942
943 if (!pTest->fXmlOmitTopTest && pTest->fXmlTopTestDone)
944 {
945 rtTestXmlElem(pTest, "End", "SubTests=\"%u\" SubTestsFailed=\"%u\" errors=\"%u\"",
946 pTest->cSubTests, pTest->cSubTestsFailed, pTest->cErrors);
947 rtTestXmlOutput(pTest, "</Test>\n");
948 }
949
950 /*
951 * Close the XML outputs.
952 */
953 if (pTest->hXmlPipe != NIL_RTPIPE)
954 {
955 RTPipeClose(pTest->hXmlPipe);
956 pTest->hXmlPipe = NIL_RTPIPE;
957 }
958 if (pTest->hXmlFile != NIL_RTFILE)
959 {
960 RTFileClose(pTest->hXmlFile);
961 pTest->hXmlFile = NIL_RTFILE;
962 }
963 pTest->fXmlEnabled = false;
964 pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
965 }
966 pTest->cXmlElements = 0;
967}
968
969/**
970 * Output callback.
971 *
972 * @returns number of bytes written.
973 * @param pvArg User argument.
974 * @param pachChars Pointer to an array of utf-8 characters.
975 * @param cbChars Number of bytes in the character array pointed to by pachChars.
976 */
977static DECLCALLBACK(size_t) rtTestPrintfOutput(void *pvArg, const char *pachChars, size_t cbChars)
978{
979 size_t cch = 0;
980 PRTTESTINT pTest = (PRTTESTINT)pvArg;
981 if (cbChars)
982 {
983 do
984 {
985 /* insert prefix if at a newline. */
986 if (pTest->fNewLine)
987 {
988 RTStrmWrite(pTest->pOutStrm, pTest->pszTest, pTest->cchTest);
989 RTStrmWrite(pTest->pOutStrm, ": ", 2);
990 cch += 2 + pTest->cchTest;
991 }
992
993 /* look for newline and write the stuff. */
994 const char *pchEnd = (const char *)memchr(pachChars, '\n', cbChars);
995 if (!pchEnd)
996 {
997 pTest->fNewLine = false;
998 RTStrmWrite(pTest->pOutStrm, pachChars, cbChars);
999 cch += cbChars;
1000 break;
1001 }
1002
1003 pTest->fNewLine = true;
1004 size_t const cchPart = pchEnd - pachChars + 1;
1005 RTStrmWrite(pTest->pOutStrm, pachChars, cchPart);
1006 cch += cchPart;
1007 pachChars += cchPart;
1008 cbChars -= cchPart;
1009 } while (cbChars);
1010 }
1011 else
1012 RTStrmFlush(pTest->pOutStrm);
1013 return cch;
1014}
1015
1016
1017/**
1018 * Internal output worker.
1019 *
1020 * Caller takes the lock.
1021 *
1022 * @returns Number of chars printed.
1023 * @param pTest The test instance.
1024 * @param pszFormat The message.
1025 * @param va The arguments.
1026 */
1027static int rtTestPrintfV(PRTTESTINT pTest, const char *pszFormat, va_list va)
1028{
1029 return (int)RTStrFormatV(rtTestPrintfOutput, pTest, NULL, NULL, pszFormat, va);
1030}
1031
1032
1033/**
1034 * Internal output worker.
1035 *
1036 * Caller takes the lock.
1037 *
1038 * @returns Number of chars printed.
1039 * @param pTest The test instance.
1040 * @param pszFormat The message.
1041 * @param ... The arguments.
1042 */
1043static int rtTestPrintf(PRTTESTINT pTest, const char *pszFormat, ...)
1044{
1045 va_list va;
1046
1047 va_start(va, pszFormat);
1048 int cch = rtTestPrintfV(pTest, pszFormat, va);
1049 va_end(va);
1050
1051 return cch;
1052}
1053
1054
1055RTR3DECL(int) RTTestPrintfNlV(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, va_list va)
1056{
1057 PRTTESTINT pTest = hTest;
1058 RTTEST_GET_VALID_RETURN_RC(pTest, -1);
1059
1060 RTCritSectEnter(&pTest->OutputLock);
1061
1062 int cch = 0;
1063 if (enmLevel <= pTest->enmMaxLevel)
1064 {
1065 if (!pTest->fNewLine)
1066 cch += rtTestPrintf(pTest, "\n");
1067 cch += rtTestPrintfV(pTest, pszFormat, va);
1068 }
1069
1070 RTCritSectLeave(&pTest->OutputLock);
1071
1072 return cch;
1073}
1074
1075
1076RTR3DECL(int) RTTestPrintfNl(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, ...)
1077{
1078 va_list va;
1079
1080 va_start(va, pszFormat);
1081 int cch = RTTestPrintfNlV(hTest, enmLevel, pszFormat, va);
1082 va_end(va);
1083
1084 return cch;
1085}
1086
1087
1088RTR3DECL(int) RTTestPrintfV(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, va_list va)
1089{
1090 PRTTESTINT pTest = hTest;
1091 RTTEST_GET_VALID_RETURN_RC(pTest, -1);
1092
1093 RTCritSectEnter(&pTest->OutputLock);
1094 int cch = 0;
1095 if (enmLevel <= pTest->enmMaxLevel)
1096 cch += rtTestPrintfV(pTest, pszFormat, va);
1097 RTCritSectLeave(&pTest->OutputLock);
1098
1099 return cch;
1100}
1101
1102
1103RTR3DECL(int) RTTestPrintf(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, ...)
1104{
1105 va_list va;
1106
1107 va_start(va, pszFormat);
1108 int cch = RTTestPrintfV(hTest, enmLevel, pszFormat, va);
1109 va_end(va);
1110
1111 return cch;
1112}
1113
1114
1115RTR3DECL(int) RTTestBanner(RTTEST hTest)
1116{
1117 return RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "TESTING...\n");
1118}
1119
1120
1121/**
1122 * Prints the result of a sub-test if necessary.
1123 *
1124 * @returns Number of chars printed.
1125 * @param pTest The test instance.
1126 * @remarks Caller own the test Lock.
1127 */
1128static int rtTestSubTestReport(PRTTESTINT pTest)
1129{
1130 int cch = 0;
1131 if ( !pTest->fSubTestReported
1132 && pTest->pszSubTest)
1133 {
1134 pTest->fSubTestReported = true;
1135 uint32_t cErrors = ASMAtomicUoReadU32(&pTest->cErrors) - pTest->cSubTestAtErrors;
1136 if (!cErrors)
1137 {
1138 if (!pTest->fSubTestSkipped)
1139 {
1140 rtTestXmlElem(pTest, "Passed", NULL);
1141 rtTestXmlElemEnd(pTest, "Test");
1142 cch += RTTestPrintfNl(pTest, RTTESTLVL_SUB_TEST, "%-60s: PASSED\n", pTest->pszSubTest);
1143 }
1144 else
1145 {
1146 rtTestXmlElem(pTest, "Skipped", NULL);
1147 rtTestXmlElemEnd(pTest, "Test");
1148 cch += RTTestPrintfNl(pTest, RTTESTLVL_SUB_TEST, "%-60s: SKIPPED\n", pTest->pszSubTest);
1149 }
1150 }
1151 else
1152 {
1153 pTest->cSubTestsFailed++;
1154 rtTestXmlElem(pTest, "Failed", "errors=\"%u\"", cErrors);
1155 rtTestXmlElemEnd(pTest, "Test");
1156 cch += RTTestPrintfNl(pTest, RTTESTLVL_SUB_TEST, "%-60s: FAILED (%u errors)\n",
1157 pTest->pszSubTest, cErrors);
1158 }
1159 }
1160 return cch;
1161}
1162
1163
1164/**
1165 * RTTestSub and RTTestSubDone worker that cleans up the current (if any)
1166 * sub test.
1167 *
1168 * @returns Number of chars printed.
1169 * @param pTest The test instance.
1170 * @remarks Caller own the test Lock.
1171 */
1172static int rtTestSubCleanup(PRTTESTINT pTest)
1173{
1174 int cch = 0;
1175 if (pTest->pszSubTest)
1176 {
1177 cch += rtTestSubTestReport(pTest);
1178
1179 RTStrFree((char *)pTest->pszSubTest);
1180 pTest->pszSubTest = NULL;
1181 pTest->fSubTestReported = true;
1182 }
1183 RTStrFree(pTest->pszErrCtx);
1184 pTest->pszErrCtx = NULL;
1185 return cch;
1186}
1187
1188
1189RTR3DECL(RTEXITCODE) RTTestSummaryAndDestroy(RTTEST hTest)
1190{
1191 PRTTESTINT pTest = hTest;
1192 RTTEST_GET_VALID_RETURN_RC(pTest, RTEXITCODE_FAILURE);
1193
1194 RTCritSectEnter(&pTest->Lock);
1195 rtTestSubTestReport(pTest);
1196 RTCritSectLeave(&pTest->Lock);
1197
1198 RTEXITCODE enmExitCode;
1199 if (!pTest->cErrors)
1200 {
1201 RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "SUCCESS\n");
1202 enmExitCode = RTEXITCODE_SUCCESS;
1203 }
1204 else
1205 {
1206 RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "FAILURE - %u errors\n", pTest->cErrors);
1207 enmExitCode = RTEXITCODE_FAILURE;
1208 }
1209
1210 RTTestDestroy(pTest);
1211 return enmExitCode;
1212}
1213
1214
1215RTR3DECL(RTEXITCODE) RTTestSkipAndDestroyV(RTTEST hTest, const char *pszReasonFmt, va_list va)
1216{
1217 PRTTESTINT pTest = hTest;
1218 RTTEST_GET_VALID_RETURN_RC(pTest, RTEXITCODE_SKIPPED);
1219
1220 RTCritSectEnter(&pTest->Lock);
1221 rtTestSubTestReport(pTest);
1222 RTCritSectLeave(&pTest->Lock);
1223
1224 RTEXITCODE enmExitCode;
1225 if (!pTest->cErrors)
1226 {
1227 if (pszReasonFmt)
1228 RTTestPrintfNlV(hTest, RTTESTLVL_FAILURE, pszReasonFmt, va);
1229 RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "SKIPPED\n");
1230 enmExitCode = RTEXITCODE_SKIPPED;
1231 }
1232 else
1233 {
1234 RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "FAILURE - %u errors\n", pTest->cErrors);
1235 enmExitCode = RTEXITCODE_FAILURE;
1236 }
1237
1238 RTTestDestroy(pTest);
1239 return enmExitCode;
1240}
1241
1242
1243RTR3DECL(RTEXITCODE) RTTestSkipAndDestroy(RTTEST hTest, const char *pszReasonFmt, ...)
1244{
1245 va_list va;
1246 va_start(va, pszReasonFmt);
1247 RTEXITCODE enmExitCode = RTTestSkipAndDestroyV(hTest, pszReasonFmt, va);
1248 va_end(va);
1249 return enmExitCode;
1250}
1251
1252
1253RTR3DECL(int) RTTestSub(RTTEST hTest, const char *pszSubTest)
1254{
1255 PRTTESTINT pTest = hTest;
1256 RTTEST_GET_VALID_RETURN_RC(pTest, -1);
1257
1258 RTCritSectEnter(&pTest->Lock);
1259
1260 /* Cleanup, reporting if necessary previous sub test. */
1261 rtTestSubCleanup(pTest);
1262
1263 /* Start new sub test. */
1264 pTest->cSubTests++;
1265 pTest->cSubTestAtErrors = ASMAtomicUoReadU32(&pTest->cErrors);
1266 pTest->pszSubTest = RTStrDup(pszSubTest);
1267 pTest->cchSubTest = strlen(pszSubTest);
1268 AssertMsg(pTest->cchSubTest < 64 /* See g_kcchMaxTestResultName in testmanager/config.py. */,
1269 ("cchSubTest=%u: '%s'\n", pTest->cchSubTest, pTest->pszSubTest));
1270 pTest->fSubTestSkipped = false;
1271 pTest->fSubTestReported = false;
1272
1273 int cch = 0;
1274 if (pTest->enmMaxLevel >= RTTESTLVL_DEBUG)
1275 cch = RTTestPrintfNl(hTest, RTTESTLVL_DEBUG, "debug: Starting sub-test '%s'\n", pszSubTest);
1276
1277 if (!pTest->fXmlTopTestDone)
1278 {
1279 pTest->fXmlTopTestDone = true;
1280 rtTestXmlElemStart(pTest, "Test", "name=%RMas", pTest->pszTest);
1281 }
1282
1283 rtTestXmlElemStart(pTest, "Test", "name=%RMas", pszSubTest);
1284
1285 RTCritSectLeave(&pTest->Lock);
1286
1287 return cch;
1288}
1289
1290
1291RTR3DECL(int) RTTestSubF(RTTEST hTest, const char *pszSubTestFmt, ...)
1292{
1293 va_list va;
1294 va_start(va, pszSubTestFmt);
1295 int cch = RTTestSubV(hTest, pszSubTestFmt, va);
1296 va_end(va);
1297 return cch;
1298}
1299
1300
1301RTR3DECL(int) RTTestSubV(RTTEST hTest, const char *pszSubTestFmt, va_list va)
1302{
1303 char *pszSubTest;
1304 RTStrAPrintfV(&pszSubTest, pszSubTestFmt, va);
1305 if (pszSubTest)
1306 {
1307 int cch = RTTestSub(hTest, pszSubTest);
1308 RTStrFree(pszSubTest);
1309 return cch;
1310 }
1311 return 0;
1312}
1313
1314
1315RTR3DECL(int) RTTestSubDone(RTTEST hTest)
1316{
1317 PRTTESTINT pTest = hTest;
1318 RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
1319
1320 RTCritSectEnter(&pTest->Lock);
1321 int cch = rtTestSubCleanup(pTest);
1322 RTCritSectLeave(&pTest->Lock);
1323
1324 return cch;
1325}
1326
1327
1328RTR3DECL(int) RTTestPassedV(RTTEST hTest, const char *pszFormat, va_list va)
1329{
1330 PRTTESTINT pTest = hTest;
1331 AssertPtr(pszFormat);
1332 RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
1333
1334 int cch = 0;
1335 if (pTest->enmMaxLevel >= RTTESTLVL_INFO)
1336 {
1337 va_list va2;
1338 va_copy(va2, va);
1339
1340 RTCritSectEnter(&pTest->OutputLock);
1341 cch += rtTestPrintf(pTest, "%N\n", pszFormat, &va2);
1342 RTCritSectLeave(&pTest->OutputLock);
1343
1344 va_end(va2);
1345 }
1346
1347 return cch;
1348}
1349
1350
1351RTR3DECL(int) RTTestPassed(RTTEST hTest, const char *pszFormat, ...)
1352{
1353 va_list va;
1354
1355 va_start(va, pszFormat);
1356 int cch = RTTestPassedV(hTest, pszFormat, va);
1357 va_end(va);
1358
1359 return cch;
1360}
1361
1362
1363RTR3DECL(int) RTTestSkippedV(RTTEST hTest, const char *pszFormat, va_list va)
1364{
1365 PRTTESTINT pTest = hTest;
1366 AssertPtrNull(pszFormat);
1367 RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
1368
1369 pTest->fSubTestSkipped = true;
1370
1371 int cch = 0;
1372 if (pszFormat && *pszFormat && pTest->enmMaxLevel >= RTTESTLVL_INFO)
1373 {
1374 va_list va2;
1375 va_copy(va2, va);
1376
1377 RTCritSectEnter(&pTest->OutputLock);
1378 cch += rtTestPrintf(pTest, "%N\n", pszFormat, &va2);
1379 RTCritSectLeave(&pTest->OutputLock);
1380
1381 va_end(va2);
1382 }
1383
1384 return cch;
1385}
1386
1387
1388RTR3DECL(int) RTTestSkipped(RTTEST hTest, const char *pszFormat, ...)
1389{
1390 va_list va;
1391
1392 va_start(va, pszFormat);
1393 int cch = RTTestSkippedV(hTest, pszFormat, va);
1394 va_end(va);
1395
1396 return cch;
1397}
1398
1399
1400
1401/**
1402 * Gets the unit name.
1403 *
1404 * @returns Unit name.
1405 * @param enmUnit The unit.
1406 */
1407static const char *rtTestUnitName(RTTESTUNIT enmUnit)
1408{
1409 switch (enmUnit)
1410 {
1411 case RTTESTUNIT_PCT: return "%";
1412 case RTTESTUNIT_BYTES: return "bytes";
1413 case RTTESTUNIT_BYTES_PER_SEC: return "bytes/s";
1414 case RTTESTUNIT_KILOBYTES: return "KB";
1415 case RTTESTUNIT_KILOBYTES_PER_SEC: return "KB/s";
1416 case RTTESTUNIT_MEGABYTES: return "MB";
1417 case RTTESTUNIT_MEGABYTES_PER_SEC: return "MB/s";
1418 case RTTESTUNIT_PACKETS: return "packets";
1419 case RTTESTUNIT_PACKETS_PER_SEC: return "packets/s";
1420 case RTTESTUNIT_FRAMES: return "frames";
1421 case RTTESTUNIT_FRAMES_PER_SEC: return "frames/s";
1422 case RTTESTUNIT_OCCURRENCES: return "occurrences";
1423 case RTTESTUNIT_OCCURRENCES_PER_SEC: return "occurrences/s";
1424 case RTTESTUNIT_ROUND_TRIP: return "roundtrips";
1425 case RTTESTUNIT_CALLS: return "calls";
1426 case RTTESTUNIT_CALLS_PER_SEC: return "calls/s";
1427 case RTTESTUNIT_SECS: return "s";
1428 case RTTESTUNIT_MS: return "ms";
1429 case RTTESTUNIT_NS: return "ns";
1430 case RTTESTUNIT_NS_PER_CALL: return "ns/call";
1431 case RTTESTUNIT_NS_PER_FRAME: return "ns/frame";
1432 case RTTESTUNIT_NS_PER_OCCURRENCE: return "ns/occurrence";
1433 case RTTESTUNIT_NS_PER_PACKET: return "ns/packet";
1434 case RTTESTUNIT_NS_PER_ROUND_TRIP: return "ns/roundtrip";
1435 case RTTESTUNIT_INSTRS: return "ins";
1436 case RTTESTUNIT_INSTRS_PER_SEC: return "ins/sec";
1437 case RTTESTUNIT_NONE: return "";
1438 case RTTESTUNIT_PP1K: return "pp1k";
1439 case RTTESTUNIT_PP10K: return "pp10k";
1440 case RTTESTUNIT_PPM: return "ppm";
1441 case RTTESTUNIT_PPB: return "ppb";
1442 case RTTESTUNIT_TICKS: return "ticks";
1443 case RTTESTUNIT_TICKS_PER_CALL: return "ticks/call";
1444 case RTTESTUNIT_TICKS_PER_OCCURENCE: return "ticks/occ";
1445 case RTTESTUNIT_PAGES: return "pages";
1446 case RTTESTUNIT_PAGES_PER_SEC: return "pages/s";
1447 case RTTESTUNIT_TICKS_PER_PAGE: return "ticks/page";
1448 case RTTESTUNIT_NS_PER_PAGE: return "ns/page";
1449 case RTTESTUNIT_PS: return "ps";
1450 case RTTESTUNIT_PS_PER_CALL: return "ps/call";
1451 case RTTESTUNIT_PS_PER_FRAME: return "ps/frame";
1452 case RTTESTUNIT_PS_PER_OCCURRENCE: return "ps/occurrence";
1453 case RTTESTUNIT_PS_PER_PACKET: return "ps/packet";
1454 case RTTESTUNIT_PS_PER_ROUND_TRIP: return "ps/roundtrip";
1455 case RTTESTUNIT_PS_PER_PAGE: return "ps/page";
1456
1457 /* No default so gcc helps us keep this up to date. */
1458 case RTTESTUNIT_INVALID:
1459 case RTTESTUNIT_END:
1460 break;
1461 }
1462 AssertMsgFailed(("%d\n", enmUnit));
1463 return "unknown";
1464}
1465
1466
1467RTR3DECL(int) RTTestValue(RTTEST hTest, const char *pszName, uint64_t u64Value, RTTESTUNIT enmUnit)
1468{
1469 PRTTESTINT pTest = hTest;
1470 RTTEST_GET_VALID_RETURN(pTest);
1471
1472 Assert(strlen(pszName) < 56 /* See g_kcchMaxTestValueName in testmanager/config.py. */);
1473
1474 const char *pszUnit = rtTestUnitName(enmUnit);
1475
1476 RTCritSectEnter(&pTest->Lock);
1477 rtTestXmlElem(pTest, "Value", "name=%RMas unit=%RMas value=\"%llu\"", pszName, pszUnit, u64Value);
1478 RTCritSectLeave(&pTest->Lock);
1479
1480 RTCritSectEnter(&pTest->OutputLock);
1481 rtTestPrintf(pTest, " %-58s: %'16llu %s\n", pszName, u64Value, pszUnit);
1482 RTCritSectLeave(&pTest->OutputLock);
1483
1484 return VINF_SUCCESS;
1485}
1486
1487
1488RTR3DECL(int) RTTestValueF(RTTEST hTest, uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, ...)
1489{
1490 va_list va;
1491 va_start(va, pszNameFmt);
1492 int rc = RTTestValueV(hTest, u64Value, enmUnit, pszNameFmt, va);
1493 va_end(va);
1494 return rc;
1495}
1496
1497
1498RTR3DECL(int) RTTestValueV(RTTEST hTest, uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, va_list va)
1499{
1500 char *pszName;
1501 RTStrAPrintfV(&pszName, pszNameFmt, va);
1502 if (!pszName)
1503 return VERR_NO_MEMORY;
1504 int rc = RTTestValue(hTest, pszName, u64Value, enmUnit);
1505 RTStrFree(pszName);
1506 return rc;
1507}
1508
1509
1510RTR3DECL(int) RTTestErrorInc(RTTEST hTest)
1511{
1512 PRTTESTINT pTest = hTest;
1513 RTTEST_GET_VALID_RETURN(pTest);
1514
1515 ASMAtomicIncU32(&pTest->cErrors);
1516
1517 return VINF_SUCCESS;
1518}
1519
1520
1521RTR3DECL(uint32_t) RTTestErrorCount(RTTEST hTest)
1522{
1523 PRTTESTINT pTest = hTest;
1524 RTTEST_GET_VALID_RETURN_RC(pTest, UINT32_MAX);
1525
1526 return ASMAtomicReadU32(&pTest->cErrors);
1527}
1528
1529
1530RTR3DECL(uint32_t) RTTestSubErrorCount(RTTEST hTest)
1531{
1532 PRTTESTINT pTest = hTest;
1533 RTTEST_GET_VALID_RETURN_RC(pTest, UINT32_MAX);
1534
1535 return ASMAtomicReadU32(&pTest->cErrors) - pTest->cSubTestAtErrors;
1536}
1537
1538
1539RTR3DECL(int) RTTestFailedV(RTTEST hTest, const char *pszFormat, va_list va)
1540{
1541 PRTTESTINT pTest = hTest;
1542 RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
1543
1544 RTTestErrorInc(pTest);
1545
1546 int cch = 0;
1547 if (pTest->enmMaxLevel >= RTTESTLVL_FAILURE)
1548 {
1549 va_list va2;
1550 va_copy(va2, va);
1551
1552 const char *pszEnd = strchr(pszFormat, '\0');
1553 bool fHasNewLine = pszFormat != pszEnd
1554 && pszEnd[-1] == '\n';
1555
1556 RTCritSectEnter(&pTest->OutputLock);
1557 cch += rtTestPrintf(pTest, fHasNewLine ? "%N" : "%N\n", pszFormat, &va2);
1558 if (pTest->pszErrCtx)
1559 {
1560 cch += rtTestPrintf(pTest, "context: %s\n", pTest->pszErrCtx);
1561 RTStrFree(pTest->pszErrCtx);
1562 pTest->pszErrCtx = NULL;
1563 }
1564 RTCritSectLeave(&pTest->OutputLock);
1565
1566 va_end(va2);
1567 }
1568
1569 return cch;
1570}
1571
1572
1573RTR3DECL(int) RTTestFailed(RTTEST hTest, const char *pszFormat, ...)
1574{
1575 va_list va;
1576
1577 va_start(va, pszFormat);
1578 int cch = RTTestFailedV(hTest, pszFormat, va);
1579 va_end(va);
1580
1581 return cch;
1582}
1583
1584
1585RTR3DECL(int) RTTestFailureDetailsV(RTTEST hTest, const char *pszFormat, va_list va)
1586{
1587 return RTTestPrintfV(hTest, RTTESTLVL_FAILURE, pszFormat, va);
1588}
1589
1590
1591RTR3DECL(int) RTTestFailureDetails(RTTEST hTest, const char *pszFormat, ...)
1592{
1593 va_list va;
1594 va_start(va, pszFormat);
1595 int cch = RTTestFailureDetailsV(hTest, pszFormat, va);
1596 va_end(va);
1597 return cch;
1598}
1599
1600
1601RTR3DECL(int) RTTestErrContextV(RTTEST hTest, const char *pszFormat, va_list va)
1602{
1603 PRTTESTINT pTest = hTest;
1604 RTTEST_GET_VALID_RETURN(pTest);
1605
1606 RTStrFree(pTest->pszErrCtx);
1607 pTest->pszErrCtx = NULL;
1608
1609 if (pszFormat && *pszFormat)
1610 {
1611 pTest->pszErrCtx = RTStrAPrintf2V(pszFormat, va);
1612 AssertReturn(pTest->pszErrCtx, VERR_NO_STR_MEMORY);
1613 RTStrStripR(pTest->pszErrCtx);
1614 }
1615
1616 return VINF_SUCCESS;
1617}
1618
1619
1620RTR3DECL(int) RTTestErrContext(RTTEST hTest, const char *pszFormat, ...)
1621{
1622 va_list va;
1623 va_start(va, pszFormat);
1624 int rc = RTTestErrContextV(hTest, pszFormat, va);
1625 va_end(va);
1626 return rc;
1627}
1628
1629
1630RTR3DECL(int) RTTestDisableAssertions(RTTEST hTest)
1631{
1632 PRTTESTINT pTest = hTest;
1633 RTTEST_GET_VALID_RETURN(pTest);
1634
1635 uint32_t cTimes = ASMAtomicIncU32(&pTest->cAssertionsDisabledAndQuieted);
1636 if (cTimes >= 2 && cTimes <= 8)
1637 return VINF_SUCCESS;
1638 if (cTimes > 8)
1639 {
1640 RTAssertSetMayPanic(pTest->fAssertSavedMayPanic);
1641 RTAssertSetQuiet(pTest->fAssertSavedQuiet);
1642 Assert(cTimes <= 8);
1643 }
1644 pTest->fAssertSavedMayPanic = RTAssertSetMayPanic(false);
1645 pTest->fAssertSavedQuiet = RTAssertSetQuiet(true);
1646 return VINF_SUCCESS;
1647}
1648
1649
1650RTR3DECL(int) RTTestRestoreAssertions(RTTEST hTest)
1651{
1652 PRTTESTINT pTest = hTest;
1653 RTTEST_GET_VALID_RETURN(pTest);
1654
1655 uint32_t cTimes = ASMAtomicDecU32(&pTest->cAssertionsDisabledAndQuieted);
1656 if (cTimes == 0)
1657 {
1658 RTAssertSetMayPanic(pTest->fAssertSavedMayPanic);
1659 RTAssertSetQuiet(pTest->fAssertSavedQuiet);
1660 }
1661 else
1662 AssertStmt(cTimes < UINT32_MAX / 2, ASMAtomicIncU32(&pTest->cAssertionsDisabledAndQuieted));
1663 return VINF_SUCCESS;
1664}
1665
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette