VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp@ 89058

Last change on this file since 89058 was 89058, checked in by vboxsync, 4 years ago

ValKit/Audio: Put the backends in an array with command line names. Provide more sensible stream config values. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.6 KB
Line 
1/* $Id: vkat.cpp 89058 2021-05-15 17:03:22Z vboxsync $ */
2/** @file
3 * Validation Kit Audio Test (VKAT) utility for testing and validating the audio stack.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/buildconfig.h>
32#include <iprt/ctype.h>
33#include <iprt/errcore.h>
34#include <iprt/initterm.h>
35#include <iprt/getopt.h>
36#include <iprt/message.h>
37#include <iprt/path.h>
38#include <iprt/process.h>
39#include <iprt/rand.h>
40#include <iprt/stream.h>
41#include <iprt/string.h>
42#include <iprt/test.h>
43
44#include <package-generated.h>
45#include "product-generated.h"
46
47#include <VBox/version.h>
48#include <VBox/vmm/pdmaudioinline.h>
49#include <VBox/vmm/pdmaudiohostenuminline.h>
50
51#include "../../../Devices/Audio/AudioHlp.h"
52#include "../../../Devices/Audio/AudioTest.h"
53#include "VBoxDD.h"
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59struct AUDIOTESTENV;
60/** Pointer a audio test environment. */
61typedef AUDIOTESTENV *PAUDIOTESTENV;
62
63struct AUDIOTESTDESC;
64/** Pointer a audio test descriptor. */
65typedef AUDIOTESTDESC *PAUDIOTESTDESC;
66
67/**
68 * Callback to set up the test parameters for a specific test.
69 *
70 * @returns IPRT status code.
71 * @retval VINF_SUCCESS if setting the parameters up succeeded. Any other error code
72 * otherwise indicating the kind of error.
73 * @param pszTest Test name.
74 * @param pTstParmsAcq The audio test parameters to set up.
75 */
76typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx));
77/** Pointer to an audio test setup callback. */
78typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP;
79
80typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms));
81/** Pointer to an audio test exec callback. */
82typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC;
83
84typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(PAUDIOTESTENV pTstEnv, void *pvCtx));
85/** Pointer to an audio test destroy callback. */
86typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY;
87
88/** Maximum audio streams a test environment can handle. */
89#define AUDIOTESTENV_MAX_STREAMS 8
90
91/**
92 * Audio test environment parameters.
93 * Not necessarily bound to a specific test (can be reused).
94 */
95typedef struct AUDIOTESTENV
96{
97 /** Output path for storing the test environment's final test files. */
98 char szPathOut[RTPATH_MAX];
99 /** Temporary path for this test environment. */
100 char szPathTemp[RTPATH_MAX];
101 /** The host (backend) driver to use. */
102 PPDMIHOSTAUDIO pDrvAudio;
103 /** The current (last) audio device enumeration to use. */
104 PDMAUDIOHOSTENUM DevEnum;
105 /** Audio stream. */
106 AUDIOTESTSTREAM aStreams[AUDIOTESTENV_MAX_STREAMS];
107 /** The audio test set to use. */
108 AUDIOTESTSET Set;
109} AUDIOTESTENV;
110
111/**
112 * Audio test descriptor.
113 */
114typedef struct AUDIOTESTDESC
115{
116 /** (Sort of) Descriptive test name. */
117 const char *pszName;
118 /** Flag whether the test is excluded. */
119 bool fExcluded;
120 /** The setup callback. */
121 PFNAUDIOTESTSETUP pfnSetup;
122 /** The exec callback. */
123 PFNAUDIOTESTEXEC pfnExec;
124 /** The destruction callback. */
125 PFNAUDIOTESTDESTROY pfnDestroy;
126} AUDIOTESTDESC;
127
128
129/*********************************************************************************************************************************
130* Internal Functions *
131*********************************************************************************************************************************/
132static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms);
133static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms);
134static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
135static DECLCALLBACK(RTEXITCODE) audioTestMain(int argc, char **argv);
136static DECLCALLBACK(RTEXITCODE) audioVerifyMain(int argc, char **argv);
137
138
139/*********************************************************************************************************************************
140* Global Variables *
141*********************************************************************************************************************************/
142/**
143 * Enumeration of test ("test") command line parameters.
144 */
145enum
146{
147 VKAT_TEST_OPT_COUNT = 900,
148 VKAT_TEST_OPT_DEV,
149 VKAT_TEST_OPT_OUTDIR,
150 VKAT_TEST_OPT_PAUSE,
151 VKAT_TEST_OPT_PCM_HZ,
152 VKAT_TEST_OPT_PCM_BIT,
153 VKAT_TEST_OPT_PCM_CHAN,
154 VKAT_TEST_OPT_PCM_SIGNED,
155 VKAT_TEST_OPT_TAG,
156 VKAT_TEST_OPT_TEMPDIR,
157 VKAT_TEST_OPT_VOL
158};
159
160/**
161 * Enumeration of verification ("verify") command line parameters.
162 */
163enum
164{
165 VKAT_VERIFY_OPT_TAG = 900
166};
167
168/**
169 * Common command line parameters.
170 */
171static const RTGETOPTDEF g_aCmdCommonOptions[] =
172{
173 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
174 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
175};
176
177/**
178 * Command line parameters for test mode.
179 */
180static const RTGETOPTDEF g_aCmdTestOptions[] =
181{
182 { "--backend", 'b', RTGETOPT_REQ_STRING },
183 { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
184 { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
185 { "--include", 'i', RTGETOPT_REQ_UINT32 },
186 { "--outdir", VKAT_TEST_OPT_OUTDIR, RTGETOPT_REQ_STRING },
187 { "--count", VKAT_TEST_OPT_COUNT, RTGETOPT_REQ_UINT32 },
188 { "--device", VKAT_TEST_OPT_DEV, RTGETOPT_REQ_STRING },
189 { "--pause", VKAT_TEST_OPT_PAUSE, RTGETOPT_REQ_UINT32 },
190 { "--pcm-bit", VKAT_TEST_OPT_PCM_BIT, RTGETOPT_REQ_UINT8 },
191 { "--pcm-chan", VKAT_TEST_OPT_PCM_CHAN, RTGETOPT_REQ_UINT8 },
192 { "--pcm-hz", VKAT_TEST_OPT_PCM_HZ, RTGETOPT_REQ_UINT16 },
193 { "--pcm-signed", VKAT_TEST_OPT_PCM_SIGNED, RTGETOPT_REQ_BOOL },
194 { "--tag", VKAT_TEST_OPT_TAG, RTGETOPT_REQ_STRING },
195 { "--tempdir", VKAT_TEST_OPT_TEMPDIR, RTGETOPT_REQ_STRING },
196 { "--volume", VKAT_TEST_OPT_VOL, RTGETOPT_REQ_UINT8 }
197};
198
199/**
200 * Command line parameters for verification mode.
201 */
202static const RTGETOPTDEF g_aCmdVerifyOptions[] =
203{
204 { "--tag", VKAT_VERIFY_OPT_TAG, RTGETOPT_REQ_STRING }
205};
206
207/**
208 * Commands.
209 */
210static struct
211{
212 const char *pszCommand;
213 DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(int argc, char **argv));
214 PCRTGETOPTDEF paOptions;
215 size_t cOptions;
216 const char *pszDesc;
217} const g_aCommands[] =
218{
219 {
220 "test", audioTestMain, g_aCmdTestOptions, RT_ELEMENTS(g_aCmdTestOptions),
221 "Does some kind of testing, I guess..."
222 },
223 {
224 "verify", audioVerifyMain, g_aCmdVerifyOptions, RT_ELEMENTS(g_aCmdVerifyOptions),
225 "Verfies something, I guess..."
226 },
227};
228
229/**
230 * Backends.
231 *
232 * @note The first backend in the array is the default one for the platform.
233 */
234static struct
235{
236 /** The driver registration structure. */
237 PCPDMDRVREG pDrvReg;
238 /** The backend name.
239 * Aliases are implemented by having multiple entries for the same backend. */
240 const char *pszName;
241} const g_aBackends[] =
242{
243#if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX)
244 { &g_DrvHostALSAAudio, "alsa" },
245#endif
246#ifdef VBOX_WITH_AUDIO_PULSE
247 { &g_DrvHostPulseAudio, "pulseaudio" },
248 { &g_DrvHostPulseAudio, "pulse" },
249 { &g_DrvHostPulseAudio, "pa" },
250#endif
251#ifdef VBOX_WITH_AUDIO_OSS
252 { &g_DrvHostOSSAudio, "oss" },
253#endif
254#if defined(RT_OS_DARWIN)
255 { &g_DrvHostCoreAudio, "coreaudio" },
256 { &g_DrvHostCoreAudio, "core" },
257 { &g_DrvHostCoreAudio, "ca" },
258#endif
259#if defined(RT_OS_WINDOWS)
260 { &g_DrvHostAudioWas, "wasapi" },
261 { &g_DrvHostAudioWas, "was" },
262 { &g_DrvHostDSound, "directsound" },
263 { &g_DrvHostDSound, "dsound" },
264 { &g_DrvHostDSound, "ds" },
265#endif
266};
267AssertCompile(sizeof(g_aBackends) > 0 /* port me */);
268
269/** The test handle. */
270static RTTEST g_hTest;
271/** The current verbosity level. */
272static unsigned g_uVerbosity = 0;
273
274
275/*********************************************************************************************************************************
276* Implementation *
277*********************************************************************************************************************************/
278
279/**
280 * Initializes an audio test environment.
281 *
282 * @param pTstEnv Audio test environment to initialize.
283 * @param pDrvAudio Audio driver to use.
284 * @param pszPathOut Output path to use. If NULL, the system's temp directory will be used.
285 * @param pszPathTemp Temporary path to use. If NULL, the system's temp directory will be used.
286 * @param pszTag Tag name to use. If NULL, a generated UUID will be used.
287 */
288static int audioTestEnvInit(PAUDIOTESTENV pTstEnv, PPDMIHOSTAUDIO pDrvAudio, const char *pszTag)
289{
290 pTstEnv->pDrvAudio = pDrvAudio;
291 PDMAudioHostEnumInit(&pTstEnv->DevEnum);
292
293 int rc = VINF_SUCCESS;
294
295 char szPathTemp[RTPATH_MAX];
296 if ( !strlen(pTstEnv->szPathTemp)
297 || !strlen(pTstEnv->szPathOut))
298 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
299
300 if ( RT_SUCCESS(rc)
301 && !strlen(pTstEnv->szPathTemp))
302 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
303
304 if ( RT_SUCCESS(rc)
305 && !strlen(pTstEnv->szPathOut))
306 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
307
308 if (RT_SUCCESS(rc))
309 rc = AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
310
311 if (RT_SUCCESS(rc))
312 {
313 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pszTag);
314 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
315 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
316 }
317
318 return rc;
319}
320
321/**
322 * Destroys an audio test environment.
323 *
324 * @param pTstEnv Audio test environment to destroy.
325 */
326static void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
327{
328 if (!pTstEnv)
329 return;
330
331 PDMAudioHostEnumDelete(&pTstEnv->DevEnum);
332
333 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
334 {
335 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
336 if (RT_FAILURE(rc2))
337 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
338 }
339
340 AudioTestSetDestroy(&pTstEnv->Set);
341}
342
343/**
344 * Initializes an audio test parameters set.
345 *
346 * @param pTstParms Test parameters set to initialize.
347 */
348static void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
349{
350 RT_ZERO(*pTstParms);
351}
352
353/**
354 * Destroys an audio test parameters set.
355 *
356 * @param pTstParms Test parameters set to destroy.
357 */
358static void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
359{
360 if (!pTstParms)
361 return;
362
363 return;
364}
365
366/**
367 * Shows the logo.
368 *
369 * @param pStream Output stream to show logo on.
370 */
371static void audioTestShowLogo(PRTSTREAM pStream)
372{
373 RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) "
374 VBOX_VERSION_STRING " - r%s\n"
375 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
376 "All rights reserved.\n\n", RTBldCfgRevisionStr());
377}
378
379/**
380 * Shows tool usage text.
381 */
382static void audioTestUsage(PRTSTREAM pStrm)
383{
384 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n",
385 RTPathFilename(RTProcExecutablePath()));
386 RTStrmPrintf(pStrm,
387 "\n"
388 "Global Options:\n"
389 " -q, --quiet\n"
390 " Sets verbosity to zero.\n"
391 " -v, --verbose\n"
392 " Increase verbosity.\n"
393 " -V, --version\n"
394 " Displays version.\n"
395 " -h, -?, --help\n"
396 " Displays help.\n"
397 );
398
399 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
400 {
401 RTStrmPrintf(pStrm,
402 "\n"
403 "Command '%s':\n"
404 " %s\n"
405 "Options for '%s':\n",
406 g_aCommands[iCmd].pszCommand, g_aCommands[iCmd].pszDesc, g_aCommands[iCmd].pszCommand);
407 PCRTGETOPTDEF const paOptions = g_aCommands[iCmd].paOptions;
408 for (unsigned i = 0; i < g_aCommands[iCmd].cOptions; i++)
409 {
410 if (RT_C_IS_PRINT(paOptions[i].iShort))
411 RTStrmPrintf(pStrm, " -%c, %s\n", g_aCmdTestOptions[i].iShort, g_aCmdTestOptions[i].pszLong);
412 else
413 RTStrmPrintf(pStrm, " %s\n", g_aCmdTestOptions[i].pszLong);
414
415 const char *pszHelp = NULL;
416 if (paOptions == g_aCmdTestOptions)
417 {
418 switch (g_aCmdTestOptions[i].iShort)
419 {
420 case 'd':
421 pszHelp = "Use the specified audio device";
422 break;
423 case 'e':
424 pszHelp = "Exclude the given test id from the list";
425 break;
426 case 'a':
427 pszHelp = "Exclude all tests from the list (useful to enable single tests later with --include)";
428 break;
429 case 'i':
430 pszHelp = "Include the given test id in the list";
431 break;
432 }
433 }
434 /** @todo Add help text for all options. */
435 if (pszHelp)
436 RTStrmPrintf(pStrm, " %s\n", pszHelp);
437 }
438 }
439
440}
441
442/** @name Driver Helper Fakes / Stubs
443 * @{ */
444
445VMMR3DECL(int) CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString)
446{
447 if (pNode != NULL)
448 {
449 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
450 if (g_uVerbosity > 2)
451 RTPrintf("debug: CFGMR3QueryString([%s], %s, %p, %#x)\n", pDrvReg->szName, pszName, pszString, cchString);
452
453 if ( ( strcmp(pDrvReg->szName, "PulseAudio") == 0
454 || strcmp(pDrvReg->szName, "HostAudioWas") == 0)
455 && strcmp(pszName, "VmName") == 0)
456 return RTStrCopy(pszString, cchString, "vkat");
457
458 if ( strcmp(pDrvReg->szName, "HostAudioWas") == 0
459 && strcmp(pszName, "VmUuid") == 0)
460 return RTStrCopy(pszString, cchString, "794c9192-d045-4f28-91ed-46253ac9998e");
461 }
462 else if (g_uVerbosity > 2)
463 RTPrintf("debug: CFGMR3QueryString(%p, %s, %p, %#x)\n", pNode, pszName, pszString, cchString);
464
465 return VERR_CFGM_VALUE_NOT_FOUND;
466}
467
468/* Fake stub. Will be removed when this moves into the driver helpers. */
469VMMR3DECL(int) CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
470{
471 if (g_uVerbosity > 2)
472 RTPrintf("debug: CFGMR3QueryStringDef(%p, %s, %p, %#x, %s)\n", pNode, pszName, pszString, cchString, pszDef);
473 return RTStrCopy(pszString, cchString, pszDef);
474}
475
476/* Fake stub. Will be removed when this moves into the driver helpers. */
477VMMR3DECL(int) CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode,
478 const char *pszValidValues, const char *pszValidNodes,
479 const char *pszWho, uint32_t uInstance)
480{
481 RT_NOREF(pNode, pszNode, pszValidValues, pszValidNodes, pszWho, uInstance);
482 return VINF_SUCCESS;
483}
484
485/** @} */
486
487
488/**
489 * Constructs a PDM audio driver instance.
490 *
491 * @returns VBox status code.
492 * @param pDrvReg PDM driver registration record to use for construction.
493 * @param ppDrvIns Where to return the driver instance structure.
494 * @param ppDrvAudio Where to return the audio driver interface of type IHOSTAUDIO.
495 */
496static int audioTestDrvConstruct(PCPDMDRVREG pDrvReg, PPPDMDRVINS ppDrvIns, PPDMIHOSTAUDIO *ppDrvAudio)
497{
498 /* The destruct function must have valid data to work with. */
499 *ppDrvIns = NULL;
500 *ppDrvAudio = NULL;
501
502 /*
503 * Check registration structure validation (doesn't need to be too
504 * thorough, PDM check it in detail on every VM startup).
505 */
506 AssertPtrReturn(pDrvReg, VERR_INVALID_POINTER);
507 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Initializing backend '%s' ...\n", pDrvReg->szName);
508 AssertPtrReturn(pDrvReg->pfnConstruct, VERR_INVALID_PARAMETER);
509
510 /*
511 * Initialize the driver helper the first time thru.
512 */
513 static PDMDRVHLPR3 s_DrvHlp;
514 if (s_DrvHlp.u32Version == 0)
515 {
516 s_DrvHlp.u32Version = PDM_DRVHLPR3_VERSION;
517 s_DrvHlp.u32TheEnd = PDM_DRVHLPR3_VERSION;
518 }
519
520 /*
521 * Create the instance data structure.
522 */
523 PPDMDRVINS pDrvIns = (PPDMDRVINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrvReg->cbInstance]));
524 RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY);
525
526 pDrvIns->u32Version = PDM_DRVINS_VERSION;
527 pDrvIns->iInstance = 0;
528 pDrvIns->pHlpR3 = &s_DrvHlp;
529 pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0];
530 pDrvIns->pReg = pDrvReg;
531 pDrvIns->pCfg = (PCFGMNODE)pDrvReg;
532 //pDrvIns->pUpBase = NULL;
533 //pDrvIns->pDownBase = NULL;
534
535 /*
536 * Invoke the constructor.
537 */
538 int rc = pDrvReg->pfnConstruct(pDrvIns, pDrvIns->pCfg, 0 /*fFlags*/);
539 if (RT_SUCCESS(rc))
540 {
541 PPDMIHOSTAUDIO pDrvAudio = (PPDMIHOSTAUDIO)pDrvIns->IBase.pfnQueryInterface(&pDrvIns->IBase, PDMIHOSTAUDIO_IID);
542 if (pDrvAudio)
543 {
544 PDMAUDIOBACKENDSTS enmStatus = pDrvAudio->pfnGetStatus(pDrvAudio, PDMAUDIODIR_OUT);
545 if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING)
546 {
547 *ppDrvAudio = pDrvAudio;
548 *ppDrvIns = pDrvIns;
549 return VINF_SUCCESS;
550 }
551 RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus);
552 }
553 else
554 RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName);
555 }
556 else
557 RTTestFailed(g_hTest, "Failed to construct audio driver '%s': %Rrc", pDrvReg->szName, rc);
558 if (pDrvReg->pfnDestruct)
559 pDrvReg->pfnDestruct(pDrvIns);
560 RTMemFree(pDrvIns);
561 return rc;
562}
563
564/**
565 * Destructs a PDM audio driver instance.
566 *
567 * @returns VBox status code.
568 * @param pDrvReg PDM driver registration record to destruct.
569 * @param pDrvIns Driver instance to destruct.
570 */
571static int audioTestDrvDestruct(PCPDMDRVREG pDrvReg, PPDMDRVINS pDrvIns)
572{
573 if (!pDrvIns)
574 return VINF_SUCCESS;
575
576 if (pDrvReg->pfnDestruct)
577 pDrvReg->pfnDestruct(pDrvIns);
578
579 pDrvIns->u32Version = 0;
580 RTMemFree(pDrvIns);
581
582 return VINF_SUCCESS;
583}
584
585/**
586 * Enumerates audio devices and optionally searches for a specific device.
587 *
588 * @returns VBox status code.
589 * @param pTstEnv Test env to use for enumeration.
590 * @param pszDev Device name to search for. Can be NULL if the default device shall be used.
591 * @param ppDev Where to return the pointer of the device enumeration of \a pTstEnv when a
592 * specific device was found.
593 */
594static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
595{
596 RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");
597
598 if (!pTstEnv->pDrvAudio->pfnGetDevices)
599 {
600 RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
601 return VINF_NOT_SUPPORTED;
602 }
603
604 Assert(pszDev == NULL || ppDev);
605
606 if (ppDev)
607 *ppDev = NULL;
608
609 int rc = pTstEnv->pDrvAudio->pfnGetDevices(pTstEnv->pDrvAudio, &pTstEnv->DevEnum);
610 if (RT_SUCCESS(rc))
611 {
612 PPDMAUDIOHOSTDEV pDev;
613 RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
614 {
615 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
616 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->szName);
617 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage));
618 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
619 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Input channels = %RU8\n", pDev->cMaxInputChannels);
620 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Output channels = %RU8\n", pDev->cMaxOutputChannels);
621
622 if ( pszDev
623 && !RTStrCmp(pDev->szName, pszDev))
624 {
625 *ppDev = pDev;
626 }
627 }
628 }
629 else
630 RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
631
632 RTTestSubDone(g_hTest);
633
634 if ( pszDev
635 && *ppDev == NULL)
636 {
637 RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
638 return VERR_NOT_FOUND;
639 }
640
641 return VINF_SUCCESS;
642}
643
644/**
645 * Opens an audio device.
646 *
647 * @returns VBox status code.
648 * @param pDev Audio device to open.
649 */
650static int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)
651{
652 int rc = VINF_SUCCESS;
653
654 RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->szName);
655
656 /** @todo Detect + open device here. */
657
658 RTTestSubDone(g_hTest);
659
660 return rc;
661}
662
663/**
664 * Closes an audio device.
665 *
666 * @returns VBox status code.
667 * @param pDev Audio device to close.
668 */
669static int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)
670{
671 int rc = VINF_SUCCESS;
672
673 RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->szName);
674
675 /** @todo Close device here. */
676
677 RTTestSubDone(g_hTest);
678
679 return rc;
680}
681
682/**
683 * Creates an audio test stream.
684 *
685 * @returns VBox status code.
686 * @param pTstEnv Test environment to use for creating the stream.
687 * @param pStream Audio stream to create.
688 * @param pCfg Stream configuration to use for creation.
689 */
690static int audioTestStreamCreate(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg)
691{
692 PDMAUDIOSTREAMCFG CfgAcq;
693
694 int rc = PDMAudioStrmCfgCopy(&CfgAcq, pCfg);
695 AssertRC(rc); /* Cannot fail. */
696
697 rc = pTstEnv->pDrvAudio->pfnStreamCreate(pTstEnv->pDrvAudio, &pStream->Backend, pCfg, &CfgAcq);
698 if (RT_FAILURE(rc))
699 return rc;
700
701 /* Do the async init in a synchronous way for now here. */
702 if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED)
703 rc = pTstEnv->pDrvAudio->pfnStreamInitAsync(pTstEnv->pDrvAudio, &pStream->Backend, false /* fDestroyed */);
704
705 if (RT_SUCCESS(rc))
706 pStream->fCreated = true;
707
708 return rc;
709}
710
711/**
712 * Destroys an audio test stream.
713 *
714 * @returns VBox status code.
715 * @param pTstEnv Test environment the stream to destroy contains.
716 * @param pStream Audio stream to destroy.
717 */
718static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
719{
720 if (!pStream)
721 return VINF_SUCCESS;
722
723 if (!pStream->fCreated)
724 return VINF_SUCCESS;
725
726 /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
727
728 int rc = pTstEnv->pDrvAudio->pfnStreamDestroy(pTstEnv->pDrvAudio, &pStream->Backend);
729 if (RT_SUCCESS(rc))
730 RT_BZERO(pStream, sizeof(PDMAUDIOBACKENDSTREAM));
731
732 return rc;
733}
734
735/**
736 * Creates an audio default output test stream.
737 * Convenience function.
738 *
739 * @returns VBox status code.
740 * @param pTstEnv Test environment to use for creating the stream.
741 * @param pStream Audio stream to create.
742 * @param pProps PCM properties to use for creation.
743 */
744static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
745{
746 PDMAUDIOSTREAMCFG Cfg;
747 int rc = PDMAudioStrmCfgInitWithProps(&Cfg, pProps);
748 AssertRC(rc); /* Cannot fail. */
749
750 Cfg.enmDir = PDMAUDIODIR_OUT;
751 Cfg.u.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
752 Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
753
754 Cfg.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps, 300);
755 Cfg.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, 200);
756 Cfg.Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(pProps, 10);
757 Cfg.Device.cMsSchedulingHint = 10;
758
759 return audioTestStreamCreate(pTstEnv, pStream, &Cfg);
760}
761
762/**
763 * Plays a test tone on a specific audio test stream.
764 *
765 * @returns VBox status code.
766 * @param pTstEnv Test environment to use for running the test.
767 * @param pStream Stream to use for playing the tone.
768 * @param pParms Tone parameters to use.
769 *
770 * @note Blocking function.
771 */
772static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
773{
774 AUDIOTESTTONE TstTone;
775 AudioTestToneInitRandom(&TstTone, &pParms->Props);
776
777 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
778
779 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", TstTone.rdFreqHz, pParms->msDuration);
780 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
781
782 /** @todo Use .WAV here? */
783 PAUDIOTESTOBJ pObj;
784 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone.pcm", &pObj);
785 AssertRCReturn(rc, rc);
786
787 PDMHOSTAUDIOSTREAMSTATE enmState = pTstEnv->pDrvAudio->pfnStreamGetState(pTstEnv->pDrvAudio, &pStream->Backend);
788 if (enmState == PDMHOSTAUDIOSTREAMSTATE_OKAY)
789 {
790 uint32_t cbBuf;
791 uint8_t abBuf[_4K];
792
793 const uint64_t tsStartMs = RTTimeMilliTS();
794 const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Chose a random scheduling (in ms). */
795 const uint32_t cbPerMs = PDMAudioPropsMilliToBytes(&pParms->Props, cSchedulingMs);
796
797 do
798 {
799 rc = AudioTestToneGenerate(&TstTone, abBuf, RT_MIN(cbPerMs, sizeof(abBuf)), &cbBuf);
800 if (RT_SUCCESS(rc))
801 {
802 /* Write stuff to disk before trying to play it. Help analysis later. */
803 rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);
804 if (RT_SUCCESS(rc))
805 {
806 uint32_t cbWritten;
807 rc = pTstEnv->pDrvAudio->pfnStreamPlay(pTstEnv->pDrvAudio, &pStream->Backend, abBuf, cbBuf, &cbWritten);
808 }
809 }
810
811 if (RTTimeMilliTS() - tsStartMs >= pParms->msDuration)
812 break;
813
814 if (RT_FAILURE(rc))
815 break;
816
817 RTThreadSleep(cSchedulingMs);
818
819 } while (RT_SUCCESS(rc));
820 }
821 else
822 rc = VERR_AUDIO_STREAM_NOT_READY;
823
824 int rc2 = AudioTestSetObjClose(pObj);
825 if (RT_SUCCESS(rc))
826 rc = rc2;
827
828 return rc;
829}
830
831/**
832 * Overrides audio test base parameters with another set.
833 *
834 * @returns VBox status code.
835 * @param pBaseParms Base parameters to override.
836 * @param pOverrideParms Override parameters to use for overriding the base parameters.
837 *
838 * @note Overriding a parameter depends on its type / default values.
839 */
840static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms)
841{
842 RT_NOREF(pBaseParms, pOverrideParms);
843
844 /** @todo Implement parameter overriding. */
845 return VERR_NOT_IMPLEMENTED;
846}
847
848
849/*********************************************************************************************************************************
850* Test callbacks *
851*********************************************************************************************************************************/
852
853/**
854 * @copydoc FNAUDIOTESTSETUP
855 */
856static DECLCALLBACK(int) audioTestPlayToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
857{
858 RT_NOREF(pTstEnv, pTstDesc, ppvCtx);
859
860 PDMAudioPropsInit(&pTstParmsAcq->TestTone.Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);
861
862#ifdef DEBUG_andy
863 pTstParmsAcq->cIterations = RTRandU32Ex(1, 1);
864#endif
865 pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);
866 pTstParmsAcq->idxCurrent = 0;
867
868 return VINF_SUCCESS;
869}
870
871/**
872 * @copydoc FNAUDIOTESTEXEC
873 */
874static DECLCALLBACK(int) audioTestPlayToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
875{
876 RT_NOREF(pvCtx);
877
878 int rc = VINF_SUCCESS;
879
880 PAUDIOTESTSTREAM pStream = &pTstEnv->aStreams[0];
881
882 for (uint32_t i = 0; i < pTstParms->cIterations; i++)
883 {
884 AudioTestToneParamsInitRandom(&pTstParms->TestTone, &pTstParms->TestTone.Props);
885 rc = audioTestCreateStreamDefaultOut(pTstEnv, pStream, &pTstParms->TestTone.Props);
886 if (RT_SUCCESS(rc))
887 rc = audioTestPlayTone(pTstEnv, pStream, &pTstParms->TestTone);
888
889 int rc2 = audioTestStreamDestroy(pTstEnv, pStream);
890 if (RT_SUCCESS(rc))
891 rc = rc2;
892 }
893
894 return rc;
895}
896
897/**
898 * @copydoc FNAUDIOTESTDESTROY
899 */
900static DECLCALLBACK(int) audioTestPlayToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
901{
902 RT_NOREF(pTstEnv, pvCtx);
903
904 return VINF_SUCCESS;
905}
906
907
908/*********************************************************************************************************************************
909* Test execution *
910*********************************************************************************************************************************/
911
912static AUDIOTESTDESC g_aTests[] =
913{
914 /* pszTest fExcluded pfnSetup */
915 { "PlayTone", false, audioTestPlayToneSetup, audioTestPlayToneExec, audioTestPlayToneDestroy }
916};
917
918/**
919 * Runs one specific audio test.
920 *
921 * @returns VBox status code.
922 * @param pTstEnv Test environment to use for running the test.
923 * @param pTstDesc Test to run.
924 * @param uSeq Test sequence # in case there are more tests.
925 * @param pOverrideParms Test parameters for overriding the actual test parameters. Optional.
926 */
927static int audioTestOne(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc,
928 unsigned uSeq, PAUDIOTESTPARMS pOverrideParms)
929{
930 RT_NOREF(uSeq);
931
932 int rc;
933
934 AUDIOTESTPARMS TstParms;
935 audioTestParmsInit(&TstParms);
936
937 RTTestSub(g_hTest, pTstDesc->pszName);
938
939 if (pTstDesc->fExcluded)
940 {
941 RTTestSkipped(g_hTest, "Excluded from list");
942 return VINF_SUCCESS;
943 }
944
945 void *pvCtx = NULL; /* Test-specific opaque context. Optional and can be NULL. */
946
947 if (pTstDesc->pfnSetup)
948 {
949 rc = pTstDesc->pfnSetup(pTstEnv, pTstDesc, &TstParms, &pvCtx);
950 if (RT_FAILURE(rc))
951 {
952 RTTestFailed(g_hTest, "Test setup failed\n");
953 return rc;
954 }
955 }
956
957 audioTestCombineParms(&TstParms, pOverrideParms);
958
959 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%u: %RU32 iterations\n", uSeq, TstParms.cIterations);
960
961 if (strlen(TstParms.Dev.szName)) /** @todo Refine this check. */
962 rc = audioTestDeviceOpen(&TstParms.Dev);
963
964 AssertPtr(pTstDesc->pfnExec);
965 rc = pTstDesc->pfnExec(pTstEnv, pvCtx, &TstParms);
966
967 RTTestSubDone(g_hTest);
968
969 if (pTstDesc->pfnDestroy)
970 {
971 int rc2 = pTstDesc->pfnDestroy(pTstEnv, pvCtx);
972 if (RT_SUCCESS(rc))
973 rc = rc2;
974
975 if (RT_FAILURE(rc2))
976 RTTestFailed(g_hTest, "Test destruction failed\n");
977 }
978
979 rc = audioTestDeviceClose(&TstParms.Dev);
980
981 audioTestParmsDestroy(&TstParms);
982
983 return rc;
984}
985
986/**
987 * Runs all specified tests in a row.
988 *
989 * @returns VBox status code.
990 * @param pTstEnv Test environment to use for running all tests.
991 * @param pOverrideParms Test parameters for (some / all) specific test parameters. Optional.
992 */
993static int audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms)
994{
995 int rc = VINF_SUCCESS;
996
997 unsigned uSeq = 0;
998 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
999 {
1000 int rc2 = audioTestOne(pTstEnv, &g_aTests[i], uSeq, pOverrideParms);
1001 if (RT_SUCCESS(rc))
1002 rc = rc2;
1003
1004 if (!g_aTests[i].fExcluded)
1005 uSeq++;
1006 }
1007
1008 return rc;
1009}
1010
1011/**
1012 * Main (entry) function for the testing functionality of VKAT.
1013 *
1014 * @returns RTEXITCODE
1015 * @param argc Number of argv arguments.
1016 * @param argv argv arguments.
1017 */
1018static DECLCALLBACK(RTEXITCODE) audioTestMain(int argc, char **argv)
1019{
1020 AUDIOTESTENV TstEnv;
1021 RT_ZERO(TstEnv);
1022
1023 AUDIOTESTPARMS TstCust;
1024 audioTestParmsInit(&TstCust);
1025
1026 const char *pszDevice = NULL; /* Custom device to use. Can be NULL if not being used. */
1027 const char *pszTag = NULL; /* Custom tag to use. Can be NULL if not being used. */
1028 PCPDMDRVREG pDrvReg = g_aBackends[0].pDrvReg;
1029
1030 RTGETOPTSTATE GetState;
1031 int rc = RTGetOptInit(&GetState, argc, argv, g_aCmdTestOptions, RT_ELEMENTS(g_aCmdTestOptions), 1, 0 /*fFlags*/);
1032 AssertRCReturn(rc, RTEXITCODE_INIT);
1033
1034 RTGETOPTUNION ValueUnion;
1035 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
1036 {
1037 switch (rc)
1038 {
1039 case 'h':
1040 audioTestUsage(g_pStdOut);
1041 return RTEXITCODE_SUCCESS;
1042
1043 case 'a':
1044 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
1045 g_aTests[i].fExcluded = true;
1046 break;
1047
1048 case 'b':
1049 pDrvReg = NULL;
1050 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
1051 if ( strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 0
1052 || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)
1053 {
1054 pDrvReg = g_aBackends[i].pDrvReg;
1055 break;
1056 }
1057 if (pDrvReg == NULL)
1058 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);
1059 break;
1060
1061 case 'e':
1062 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
1063 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
1064 g_aTests[ValueUnion.u32].fExcluded = true;
1065 break;
1066
1067 case 'i':
1068 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
1069 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
1070 g_aTests[ValueUnion.u32].fExcluded = false;
1071 break;
1072
1073 case VKAT_TEST_OPT_COUNT:
1074 break;
1075
1076 case VKAT_TEST_OPT_DEV:
1077 pszDevice = ValueUnion.psz;
1078 break;
1079
1080 case VKAT_TEST_OPT_PAUSE:
1081 break;
1082
1083 case VKAT_TEST_OPT_OUTDIR:
1084 rc = RTStrCopy(TstEnv.szPathOut, sizeof(TstEnv.szPathOut), ValueUnion.psz);
1085 if (RT_FAILURE(rc))
1086 return RTMsgErrorExitFailure("Failed to copy out directory: %Rrc", rc);
1087 break;
1088
1089 case VKAT_TEST_OPT_PCM_BIT:
1090 /** @todo r=bird: the X suffix means: "fingers off!" */
1091 TstCust.TestTone.Props.cbSampleX = ValueUnion.u8 / 8 /* bit */;
1092 break;
1093
1094 case VKAT_TEST_OPT_PCM_CHAN:
1095 /** @todo r=bird: the X suffix means: "fingers off!" */
1096 TstCust.TestTone.Props.cChannelsX = ValueUnion.u8;
1097 break;
1098
1099 case VKAT_TEST_OPT_PCM_HZ:
1100 TstCust.TestTone.Props.uHz = ValueUnion.u32;
1101 break;
1102
1103 case VKAT_TEST_OPT_PCM_SIGNED:
1104 TstCust.TestTone.Props.fSigned = ValueUnion.f;
1105 break;
1106
1107 case VKAT_TEST_OPT_TAG:
1108 pszTag = ValueUnion.psz;
1109 break;
1110
1111 case VKAT_TEST_OPT_TEMPDIR:
1112 rc = RTStrCopy(TstEnv.szPathTemp, sizeof(TstEnv.szPathTemp), ValueUnion.psz);
1113 if (RT_FAILURE(rc))
1114 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Temp dir invalid, rc=%Rrc", rc);
1115 break;
1116
1117 case VKAT_TEST_OPT_VOL:
1118 TstCust.TestTone.uVolumePercent = ValueUnion.u8;
1119 break;
1120
1121 default:
1122 return RTGetOptPrintError(rc, &ValueUnion);
1123 }
1124 }
1125
1126 /*
1127 * Start testing.
1128 */
1129 RTTestBanner(g_hTest);
1130
1131 PPDMIHOSTAUDIO pDrvAudio = NULL;
1132 PPDMDRVINS pDrvIns = NULL;
1133 rc = audioTestDrvConstruct(pDrvReg, &pDrvIns, &pDrvAudio);
1134 if (RT_SUCCESS(rc))
1135 {
1136 /* For now all tests have the same test environment. */
1137 rc = audioTestEnvInit(&TstEnv, pDrvAudio, pszTag);
1138 if (RT_SUCCESS(rc))
1139 {
1140 PPDMAUDIOHOSTDEV pDev;
1141 rc = audioTestDevicesEnumerateAndCheck(&TstEnv, pszDevice, &pDev);
1142 if (RT_SUCCESS(rc))
1143 audioTestWorker(&TstEnv, &TstCust);
1144
1145 /* Before destroying the test environment, pack up the test set so
1146 * that it's ready for transmission. */
1147 char szFileOut[RTPATH_MAX];
1148 rc = AudioTestSetPack(&TstEnv.Set, TstEnv.szPathOut, szFileOut, sizeof(szFileOut));
1149 if (RT_SUCCESS(rc))
1150 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", szFileOut);
1151
1152 /* Clean up. */
1153 AudioTestSetWipe(&TstEnv.Set);
1154
1155 audioTestEnvDestroy(&TstEnv);
1156 }
1157 }
1158 audioTestDrvDestruct(pDrvReg, pDrvIns);
1159
1160 audioTestParmsDestroy(&TstCust);
1161
1162 if (RT_FAILURE(rc)) /* Let us know that something went wrong in case we forgot to mention it. */
1163 RTTestFailed(g_hTest, "Tested failed with %Rrc\n", rc);
1164
1165 /*
1166 * Print summary and exit.
1167 */
1168 return RTTestSummaryAndDestroy(g_hTest);
1169}
1170
1171/**
1172 * Verifies one single test set.
1173 *
1174 * @returns VBox status code.
1175 * @param pszPath Absolute path to test set.
1176 * @param pszTag Tag of test set to verify. Optional and can be NULL.
1177 */
1178static int audioVerifyOne(const char *pszPath, const char *pszTag)
1179{
1180 RTTestSubF(g_hTest, "Verifying test set (tag '%s') ...", pszTag ? pszTag : "default");
1181
1182 AUDIOTESTSET tstSet;
1183 int rc = AudioTestSetOpen(&tstSet, pszPath);
1184 if (RT_SUCCESS(rc))
1185 {
1186 AUDIOTESTERRORDESC errDesc;
1187 rc = AudioTestSetVerify(&tstSet, pszTag ? pszTag : "default", &errDesc);
1188 if (RT_SUCCESS(rc))
1189 {
1190 if (AudioTestErrorDescFailed(&errDesc))
1191 {
1192 /** @todo Use some AudioTestErrorXXX API for enumeration here later. */
1193 PAUDIOTESTERRORENTRY pErrEntry;
1194 RTListForEach(&errDesc.List, pErrEntry, AUDIOTESTERRORENTRY, Node)
1195 {
1196 RTTestFailed(g_hTest, pErrEntry->szDesc);
1197 }
1198 }
1199 else
1200 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification successful");
1201
1202 AudioTestErrorDescDestroy(&errDesc);
1203 }
1204 else
1205 RTTestFailed(g_hTest, "Verification failed with %Rrc", rc);
1206
1207 AudioTestSetClose(&tstSet);
1208 }
1209 else
1210 RTTestFailed(g_hTest, "Opening test set '%s' (tag '%s') failed, rc=%Rrc\n", pszPath, pszTag, rc);
1211
1212 RTTestSubDone(g_hTest);
1213
1214 return rc;
1215}
1216
1217/**
1218 * Main (entry) function for the verification functionality of VKAT.
1219 *
1220 * @returns RTEXITCODE
1221 * @param argc Number of argv arguments.
1222 * @param argv argv arguments.
1223 */
1224static DECLCALLBACK(RTEXITCODE) audioVerifyMain(int argc, char **argv)
1225{
1226 /*
1227 * Parse options and process arguments.
1228 */
1229 RTGETOPTSTATE GetState;
1230 int rc = RTGetOptInit(&GetState, argc, argv, g_aCmdVerifyOptions, RT_ELEMENTS(g_aCmdVerifyOptions),
1231 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1232 AssertRCReturn(rc, RTEXITCODE_INIT);
1233
1234 const char *pszTag = NULL; /* Custom tag to use. Can be NULL if not being used. */
1235 unsigned iTestSet = 0;
1236 RTGETOPTUNION ValueUnion;
1237 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
1238 {
1239 switch (rc)
1240 {
1241 case VKAT_VERIFY_OPT_TAG:
1242 pszTag = ValueUnion.psz;
1243 if (g_uVerbosity > 0)
1244 RTMsgInfo("Using tag '%s'\n", pszTag);
1245 break;
1246
1247 case VINF_GETOPT_NOT_OPTION:
1248 if (iTestSet == 0)
1249 RTTestBanner(g_hTest);
1250 audioVerifyOne(ValueUnion.psz, pszTag);
1251 iTestSet++;
1252 break;
1253
1254 default:
1255 return RTGetOptPrintError(rc, &ValueUnion);
1256 }
1257 }
1258
1259 /*
1260 * If no paths given, default to the current directory.
1261 */
1262 if (iTestSet == 0)
1263 {
1264 if (iTestSet == 0)
1265 RTTestBanner(g_hTest);
1266 char szDirCur[RTPATH_MAX];
1267 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
1268 audioVerifyOne(RT_SUCCESS(rc) ? szDirCur : ".", pszTag);
1269 }
1270
1271 /*
1272 * Print summary and exit.
1273 */
1274 return RTTestSummaryAndDestroy(g_hTest);
1275}
1276
1277int main(int argc, char **argv)
1278{
1279 /*
1280 * Init IPRT and globals.
1281 */
1282 RTEXITCODE rcExit = RTTestInitAndCreate("AudioTest", &g_hTest);
1283 if (rcExit != RTEXITCODE_SUCCESS)
1284 return rcExit;
1285
1286 /*
1287 * Process common options.
1288 */
1289 RTGETOPTUNION ValueUnion;
1290 RTGETOPTSTATE GetState;
1291 int rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions,
1292 RT_ELEMENTS(g_aCmdCommonOptions), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
1293 AssertRCReturn(rc, RTEXITCODE_INIT);
1294
1295 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
1296 {
1297 switch (rc)
1298 {
1299 case 'q':
1300 g_uVerbosity = 0;
1301 break;
1302
1303 case 'v':
1304 g_uVerbosity++;
1305 break;
1306
1307 case 'V':
1308 RTPrintf("v0.0.1\n");
1309 return RTEXITCODE_SUCCESS;
1310
1311 case 'h':
1312 audioTestShowLogo(g_pStdOut);
1313 audioTestUsage(g_pStdOut);
1314 return RTEXITCODE_SUCCESS;
1315
1316 case VINF_GETOPT_NOT_OPTION:
1317 {
1318 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aCommands); i++)
1319 if (strcmp(ValueUnion.psz, g_aCommands[i].pszCommand) == 0)
1320 {
1321 audioTestShowLogo(g_pStdOut);
1322 int32_t iCurArg = GetState.iNext - 1;
1323 return g_aCommands[i].pfnHandler(argc - iCurArg, argv + iCurArg);
1324 }
1325 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
1326 audioTestUsage(g_pStdErr);
1327 return RTEXITCODE_SYNTAX;
1328 }
1329
1330 default:
1331 return RTGetOptPrintError(rc, &ValueUnion);
1332 }
1333 }
1334
1335 RTMsgError("No command specified!\n");
1336 audioTestUsage(g_pStdErr);
1337 return RTEXITCODE_SYNTAX;
1338}
1339
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