VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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