VirtualBox

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

Last change on this file since 90918 was 90918, checked in by vboxsync, 3 years ago

Audio/Validation Kit: Simplified ATS init / destruction handling. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.4 KB
Line 
1/* $Id: vkat.cpp 90918 2021-08-26 15:29:25Z 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/dir.h>
34#include <iprt/errcore.h>
35#include <iprt/file.h>
36#include <iprt/initterm.h>
37#include <iprt/getopt.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/process.h>
41#include <iprt/rand.h>
42#include <iprt/stream.h>
43#include <iprt/string.h>
44#include <iprt/test.h>
45
46#include <package-generated.h>
47#include "product-generated.h"
48
49#include <VBox/version.h>
50#include <VBox/log.h>
51
52#ifdef RT_OS_WINDOWS
53# include <iprt/win/windows.h> /* for CoInitializeEx */
54#endif
55#include <signal.h>
56
57#include "vkatInternal.h"
58
59
60/*********************************************************************************************************************************
61* Internal Functions *
62*********************************************************************************************************************************/
63static int audioVerifyOne(const char *pszPathSetA, const char *pszPathSetB);
64
65
66/*********************************************************************************************************************************
67* Global Variables *
68*********************************************************************************************************************************/
69/**
70 * Backends.
71 *
72 * @note The first backend in the array is the default one for the platform.
73 */
74struct
75{
76 /** The driver registration structure. */
77 PCPDMDRVREG pDrvReg;
78 /** The backend name.
79 * Aliases are implemented by having multiple entries for the same backend. */
80 const char *pszName;
81} const g_aBackends[] =
82{
83#if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX)
84 { &g_DrvHostALSAAudio, "alsa" },
85#endif
86#ifdef VBOX_WITH_AUDIO_PULSE
87 { &g_DrvHostPulseAudio, "pulseaudio" },
88 { &g_DrvHostPulseAudio, "pulse" },
89 { &g_DrvHostPulseAudio, "pa" },
90#endif
91#ifdef VBOX_WITH_AUDIO_OSS
92 { &g_DrvHostOSSAudio, "oss" },
93#endif
94#if defined(RT_OS_DARWIN)
95 { &g_DrvHostCoreAudio, "coreaudio" },
96 { &g_DrvHostCoreAudio, "core" },
97 { &g_DrvHostCoreAudio, "ca" },
98#endif
99#if defined(RT_OS_WINDOWS)
100 { &g_DrvHostAudioWas, "wasapi" },
101 { &g_DrvHostAudioWas, "was" },
102 { &g_DrvHostDSound, "directsound" },
103 { &g_DrvHostDSound, "dsound" },
104 { &g_DrvHostDSound, "ds" },
105#endif
106 { &g_DrvHostValidationKitAudio, "valkit" }
107};
108AssertCompile(sizeof(g_aBackends) > 0 /* port me */);
109
110/**
111 * Long option values for the 'test' command.
112 */
113enum
114{
115 VKAT_TEST_OPT_COUNT = 900,
116 VKAT_TEST_OPT_DEV,
117 VKAT_TEST_OPT_GUEST_ATS_ADDR,
118 VKAT_TEST_OPT_GUEST_ATS_PORT,
119 VKAT_TEST_OPT_HOST_ATS_ADDR,
120 VKAT_TEST_OPT_HOST_ATS_PORT,
121 VKAT_TEST_OPT_MODE,
122 VKAT_TEST_OPT_NO_VERIFY,
123 VKAT_TEST_OPT_OUTDIR,
124 VKAT_TEST_OPT_PAUSE,
125 VKAT_TEST_OPT_PCM_HZ,
126 VKAT_TEST_OPT_PCM_BIT,
127 VKAT_TEST_OPT_PCM_CHAN,
128 VKAT_TEST_OPT_PCM_SIGNED,
129 VKAT_TEST_OPT_PROBE_BACKENDS,
130 VKAT_TEST_OPT_TAG,
131 VKAT_TEST_OPT_TEMPDIR,
132 VKAT_TEST_OPT_VOL,
133 VKAT_TEST_OPT_TCP_BIND_ADDRESS,
134 VKAT_TEST_OPT_TCP_BIND_PORT,
135 VKAT_TEST_OPT_TCP_CONNECT_ADDRESS,
136 VKAT_TEST_OPT_TCP_CONNECT_PORT
137};
138
139/**
140 * Long option values for the 'verify' command.
141 */
142enum
143{
144 VKAT_VERIFY_OPT_TAG = 900
145};
146
147/**
148 * Common command line parameters.
149 */
150static const RTGETOPTDEF g_aCmdCommonOptions[] =
151{
152 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
153 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
154 { "--daemonize", AUDIO_TEST_OPT_CMN_DAEMONIZE, RTGETOPT_REQ_NOTHING },
155 { "--daemonized", AUDIO_TEST_OPT_CMN_DAEMONIZED, RTGETOPT_REQ_NOTHING },
156 { "--debug-audio", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE, RTGETOPT_REQ_NOTHING },
157 { "--debug-audio-path", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH, RTGETOPT_REQ_STRING },
158};
159
160/**
161 * Command line parameters for test mode.
162 */
163static const RTGETOPTDEF g_aCmdTestOptions[] =
164{
165 { "--backend", 'b', RTGETOPT_REQ_STRING },
166 { "--drvaudio", 'd', RTGETOPT_REQ_NOTHING },
167 { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
168 { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
169 { "--guest-ats-addr", VKAT_TEST_OPT_GUEST_ATS_ADDR, RTGETOPT_REQ_STRING },
170 { "--guest-ats-port", VKAT_TEST_OPT_GUEST_ATS_PORT, RTGETOPT_REQ_UINT32 },
171 { "--host-ats-address", VKAT_TEST_OPT_HOST_ATS_ADDR, RTGETOPT_REQ_STRING },
172 { "--host-ats-port", VKAT_TEST_OPT_HOST_ATS_PORT, RTGETOPT_REQ_UINT32 },
173 { "--include", 'i', RTGETOPT_REQ_UINT32 },
174 { "--outdir", VKAT_TEST_OPT_OUTDIR, RTGETOPT_REQ_STRING },
175 { "--count", VKAT_TEST_OPT_COUNT, RTGETOPT_REQ_UINT32 },
176 { "--device", VKAT_TEST_OPT_DEV, RTGETOPT_REQ_STRING },
177 { "--pause", VKAT_TEST_OPT_PAUSE, RTGETOPT_REQ_UINT32 },
178 { "--pcm-bit", VKAT_TEST_OPT_PCM_BIT, RTGETOPT_REQ_UINT8 },
179 { "--pcm-chan", VKAT_TEST_OPT_PCM_CHAN, RTGETOPT_REQ_UINT8 },
180 { "--pcm-hz", VKAT_TEST_OPT_PCM_HZ, RTGETOPT_REQ_UINT16 },
181 { "--pcm-signed", VKAT_TEST_OPT_PCM_SIGNED, RTGETOPT_REQ_BOOL },
182 { "--probe-backends", VKAT_TEST_OPT_PROBE_BACKENDS, RTGETOPT_REQ_NOTHING },
183 { "--mode", VKAT_TEST_OPT_MODE, RTGETOPT_REQ_STRING },
184 { "--no-verify", VKAT_TEST_OPT_NO_VERIFY, RTGETOPT_REQ_NOTHING },
185 { "--tag", VKAT_TEST_OPT_TAG, RTGETOPT_REQ_STRING },
186 { "--tempdir", VKAT_TEST_OPT_TEMPDIR, RTGETOPT_REQ_STRING },
187 { "--volume", VKAT_TEST_OPT_VOL, RTGETOPT_REQ_UINT8 },
188 { "--tcp-bind-addr", VKAT_TEST_OPT_TCP_BIND_ADDRESS, RTGETOPT_REQ_STRING },
189 { "--tcp-bind-port", VKAT_TEST_OPT_TCP_BIND_PORT, RTGETOPT_REQ_UINT16 },
190 { "--tcp-connect-addr", VKAT_TEST_OPT_TCP_CONNECT_ADDRESS, RTGETOPT_REQ_STRING },
191 { "--tcp-connect-port", VKAT_TEST_OPT_TCP_CONNECT_PORT, RTGETOPT_REQ_UINT16 }
192};
193
194/**
195 * Command line parameters for verification mode.
196 */
197static const RTGETOPTDEF g_aCmdVerifyOptions[] =
198{
199 { "--tag", VKAT_VERIFY_OPT_TAG, RTGETOPT_REQ_STRING }
200};
201
202/** Terminate ASAP if set. Set on Ctrl-C. */
203bool volatile g_fTerminate = false;
204/** The release logger. */
205PRTLOGGER g_pRelLogger = NULL;
206/** The test handle. */
207RTTEST g_hTest;
208/** The current verbosity level. */
209unsigned g_uVerbosity = 0;
210/** DrvAudio: Enable debug (or not). */
211bool g_fDrvAudioDebug = false;
212/** DrvAudio: The debug output path. */
213const char *g_pszDrvAudioDebug = NULL;
214
215
216/**
217 * Get default backend.
218 */
219PCPDMDRVREG AudioTestGetDefaultBackend(void)
220{
221 return g_aBackends[0].pDrvReg;
222}
223
224
225/**
226 * Helper for handling --backend options.
227 *
228 * @returns Pointer to the specified backend, NULL if not found (error
229 * displayed).
230 * @param pszBackend The backend option value.
231 */
232PCPDMDRVREG AudioTestFindBackendOpt(const char *pszBackend)
233{
234 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
235 if ( strcmp(pszBackend, g_aBackends[i].pszName) == 0
236 || strcmp(pszBackend, g_aBackends[i].pDrvReg->szName) == 0)
237 return g_aBackends[i].pDrvReg;
238 RTMsgError("Unknown backend: '%s'", pszBackend);
239 return NULL;
240}
241
242
243/*********************************************************************************************************************************
244* Test callbacks *
245*********************************************************************************************************************************/
246
247/**
248 * @copydoc FNAUDIOTESTSETUP
249 */
250static DECLCALLBACK(int) audioTestPlayToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
251{
252 RT_NOREF(pTstDesc, ppvCtx);
253
254 int rc = VINF_SUCCESS;
255
256 if (strlen(pTstEnv->szDev))
257 {
258 rc = audioTestDriverStackSetDevice(pTstEnv->pDrvStack, PDMAUDIODIR_OUT, pTstEnv->szDev);
259 if (RT_FAILURE(rc))
260 return rc;
261 }
262
263 pTstParmsAcq->enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
264 pTstParmsAcq->Props = pTstEnv->Props;
265 pTstParmsAcq->enmDir = PDMAUDIODIR_OUT;
266#ifdef DEBUG
267 pTstParmsAcq->cIterations = 1;
268#else
269 pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);
270#endif
271 pTstParmsAcq->idxCurrent = 0;
272
273 PAUDIOTESTTONEPARMS pToneParms = &pTstParmsAcq->TestTone;
274
275 pToneParms->Props = pTstParmsAcq->Props;
276 pToneParms->dbFreqHz = AudioTestToneGetRandomFreq();
277 pToneParms->msPrequel = 0; /** @todo Implement analyzing this first! */
278#ifdef DEBUG
279 pToneParms->msDuration = RTRandU32Ex(50, 2500);
280#else
281 pToneParms->msDuration = RTRandU32Ex(0, RT_MS_10SEC); /** @todo Probably a bit too long, but let's see. */
282#endif
283 pToneParms->msSequel = 0; /** @todo Implement analyzing this first! */
284 pToneParms->uVolumePercent = 100; /** @todo Implement analyzing this first! */
285
286 return rc;
287}
288
289/**
290 * @copydoc FNAUDIOTESTEXEC
291 */
292static DECLCALLBACK(int) audioTestPlayToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
293{
294 RT_NOREF(pvCtx);
295
296 int rc = VINF_SUCCESS;
297
298 for (uint32_t i = 0; i < pTstParms->cIterations; i++)
299 {
300 PAUDIOTESTTONEPARMS const pToneParms = &pTstParms->TestTone;
301
302 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32/%RU16: Playing test tone (%RU16Hz, %RU32ms)\n",
303 pTstParms->idxCurrent, i, (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
304
305 /*
306 * 1. Arm the (host) ValKit ATS with the recording parameters.
307 */
308 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Telling ValKit audio driver on host to record new tone ...\n");
309 rc = AudioTestSvcClientToneRecord(&pTstEnv->u.Host.AtsClValKit, pToneParms);
310 if (RT_SUCCESS(rc))
311 {
312 /*
313 * 2. Tell VKAT on guest to start playback.
314 */
315 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Telling VKAT on guest to play tone ...\n");
316 rc = AudioTestSvcClientTonePlay(&pTstEnv->u.Host.AtsClGuest, pToneParms);
317 if (RT_FAILURE(rc))
318 RTTestFailed(g_hTest, "Test #%RU32/%RU16: AudioTestSvcClientTonePlay() failed with %Rrc\n",
319 pTstParms->idxCurrent, i, rc);
320 }
321 else
322 RTTestFailed(g_hTest, "Test #%RU32/%RU16: AudioTestSvcClientToneRecord() failed with %Rrc\n",
323 pTstParms->idxCurrent, i, rc);
324
325 if (RT_SUCCESS(rc))
326 {
327 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Playing tone done\n");
328
329 /* Give the audio stack a random amount of time for draining data before the next iteration. */
330 if (pTstParms->cIterations > 1)
331 RTThreadSleep(RTRandU32Ex(2000, 5000)); /** @todo Implement some dedicated ATS command for this? */
332 }
333
334 if (RT_FAILURE(rc))
335 RTTestFailed(g_hTest, "Test #%RU32/%RU16: Playing test tone failed with %Rrc\n", pTstParms->idxCurrent, i, rc);
336 }
337
338 return rc;
339}
340
341/**
342 * @copydoc FNAUDIOTESTDESTROY
343 */
344static DECLCALLBACK(int) audioTestPlayToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
345{
346 RT_NOREF(pTstEnv, pvCtx);
347
348 return VINF_SUCCESS;
349}
350
351/**
352 * @copydoc FNAUDIOTESTSETUP
353 */
354static DECLCALLBACK(int) audioTestRecordToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
355{
356 RT_NOREF(pTstDesc, ppvCtx);
357
358 int rc = VINF_SUCCESS;
359
360 if (strlen(pTstEnv->szDev))
361 {
362 rc = audioTestDriverStackSetDevice(pTstEnv->pDrvStack, PDMAUDIODIR_IN, pTstEnv->szDev);
363 if (RT_FAILURE(rc))
364 return rc;
365 }
366
367 pTstParmsAcq->enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
368 pTstParmsAcq->Props = pTstEnv->Props;
369 pTstParmsAcq->enmDir = PDMAUDIODIR_IN;
370#ifdef DEBUG
371 pTstParmsAcq->cIterations = 1;
372#else
373 pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);
374#endif
375 pTstParmsAcq->idxCurrent = 0;
376
377 PAUDIOTESTTONEPARMS pToneParms = &pTstParmsAcq->TestTone;
378
379 pToneParms->Props = pTstParmsAcq->Props;
380 pToneParms->dbFreqHz = AudioTestToneGetRandomFreq();
381 pToneParms->msPrequel = 0; /** @todo Implement analyzing this first! */
382 pToneParms->Props = pTstParmsAcq->Props;
383#ifdef DEBUG
384 pToneParms->msDuration = RTRandU32Ex(50 /* ms */, 2500);
385#else
386 pToneParms->msDuration = RTRandU32Ex(50 /* ms */, RT_MS_30SEC); /** @todo Record even longer? */
387#endif
388 pToneParms->msSequel = 0; /** @todo Implement analyzing this first! */
389 pToneParms->uVolumePercent = 100; /** @todo Implement analyzing this first! */
390
391 return rc;
392}
393
394/**
395 * @copydoc FNAUDIOTESTEXEC
396 */
397static DECLCALLBACK(int) audioTestRecordToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
398{
399 RT_NOREF(pvCtx);
400
401 int rc = VINF_SUCCESS;
402
403 for (uint32_t i = 0; i < pTstParms->cIterations; i++)
404 {
405 PAUDIOTESTTONEPARMS const pToneParms = &pTstParms->TestTone;
406
407 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%RU32/%RU16: Recording test tone (%RU16Hz, %RU32ms)\n",
408 pTstParms->idxCurrent, i, (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
409
410 /*
411 * 1. Arm the (host) ValKit ATS with the playback parameters.
412 */
413 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Telling ValKit audio driver on host to inject recording data ...\n");
414 rc = AudioTestSvcClientTonePlay(&pTstEnv->u.Host.AtsClValKit, &pTstParms->TestTone);
415 if (RT_SUCCESS(rc))
416 {
417 /*
418 * 2. Tell the guest ATS to start recording.
419 */
420 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Telling VKAT on guest to record audio ...\n");
421 rc = AudioTestSvcClientToneRecord(&pTstEnv->u.Host.AtsClGuest, &pTstParms->TestTone);
422 if (RT_FAILURE(rc))
423 RTTestFailed(g_hTest, "Test #%RU32/%RU16: AudioTestSvcClientToneRecord() failed with %Rrc\n",
424 pTstParms->idxCurrent, i, rc);
425 }
426 else
427 RTTestFailed(g_hTest, "Test #%RU32/%RU16: AudioTestSvcClientTonePlay() failed with %Rrc\n",
428 pTstParms->idxCurrent, i, rc);
429
430 if (RT_SUCCESS(rc))
431 {
432 /* Wait a bit to let the left over audio bits being processed. */
433 if (pTstParms->cIterations > 1)
434 RTThreadSleep(RTRandU32Ex(2000, 5000)); /** @todo Implement some dedicated ATS command for this? */
435 }
436
437 if (RT_FAILURE(rc))
438 RTTestFailed(g_hTest, "Test #%RU32/%RU16: Recording test tone failed with %Rrc\n", pTstParms->idxCurrent, i, rc);
439 }
440
441 return rc;
442}
443
444/**
445 * @copydoc FNAUDIOTESTDESTROY
446 */
447static DECLCALLBACK(int) audioTestRecordToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
448{
449 RT_NOREF(pTstEnv, pvCtx);
450
451 return VINF_SUCCESS;
452}
453
454
455/*********************************************************************************************************************************
456* Test execution *
457*********************************************************************************************************************************/
458
459/** Test definition table. */
460AUDIOTESTDESC g_aTests[] =
461{
462 /* pszTest fExcluded pfnSetup */
463 { "PlayTone", false, audioTestPlayToneSetup, audioTestPlayToneExec, audioTestPlayToneDestroy },
464 { "RecordTone", false, audioTestRecordToneSetup, audioTestRecordToneExec, audioTestRecordToneDestroy }
465};
466/** Number of tests defined. */
467unsigned g_cTests = RT_ELEMENTS(g_aTests);
468
469/**
470 * Runs one specific audio test.
471 *
472 * @returns VBox status code.
473 * @param pTstEnv Test environment to use for running the test.
474 * @param pTstDesc Test to run.
475 * @param uSeq Test sequence # in case there are more tests.
476 */
477static int audioTestOne(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, unsigned uSeq)
478{
479 RT_NOREF(uSeq);
480
481 int rc;
482
483 AUDIOTESTPARMS TstParms;
484 audioTestParmsInit(&TstParms);
485
486 RTTestSub(g_hTest, pTstDesc->pszName);
487
488 if (pTstDesc->fExcluded)
489 {
490 RTTestSkipped(g_hTest, "Test #%u is excluded from list, skipping", uSeq);
491 return VINF_SUCCESS;
492 }
493
494 void *pvCtx = NULL; /* Test-specific opaque context. Optional and can be NULL. */
495
496 if (pTstDesc->pfnSetup)
497 {
498 rc = pTstDesc->pfnSetup(pTstEnv, pTstDesc, &TstParms, &pvCtx);
499 if (RT_FAILURE(rc))
500 {
501 RTTestFailed(g_hTest, "Test #%u setup failed with %Rrc\n", uSeq, rc);
502 return rc;
503 }
504 }
505
506 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%u (%RU32 iterations total)\n", uSeq, TstParms.cIterations);
507
508 AssertPtr(pTstDesc->pfnExec);
509 rc = pTstDesc->pfnExec(pTstEnv, pvCtx, &TstParms);
510 if (RT_FAILURE(rc))
511 RTTestFailed(g_hTest, "Test #%u failed with %Rrc\n", uSeq, rc);
512
513 RTTestSubDone(g_hTest);
514
515 if (pTstDesc->pfnDestroy)
516 {
517 int rc2 = pTstDesc->pfnDestroy(pTstEnv, pvCtx);
518 if (RT_SUCCESS(rc))
519 rc = rc2;
520
521 if (RT_FAILURE(rc2))
522 RTTestFailed(g_hTest, "Test #%u destruction failed with %Rrc\n", uSeq, rc2);
523 }
524
525 audioTestParmsDestroy(&TstParms);
526
527 return rc;
528}
529
530/**
531 * Runs all specified tests in a row.
532 *
533 * @returns VBox status code.
534 * @param pTstEnv Test environment to use for running all tests.
535 */
536int audioTestWorker(PAUDIOTESTENV pTstEnv)
537{
538 int rc = VINF_SUCCESS;
539
540 if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
541 {
542 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Guest ATS running\n");
543
544 while (!g_fTerminate)
545 RTThreadSleep(100);
546
547 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down guest ATS ...\n");
548
549 int rc2 = AudioTestSvcStop(pTstEnv->pSrv);
550 if (RT_SUCCESS(rc))
551 rc = rc2;
552
553 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Guest ATS shutdown complete\n");
554 }
555 else if (pTstEnv->enmMode == AUDIOTESTMODE_HOST)
556 {
557 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Using tag '%s'\n", pTstEnv->szTag);
558
559 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Telling ValKit audio driver on host to begin a new test set ...\n");
560 rc = AudioTestSvcClientTestSetBegin(&pTstEnv->u.Host.AtsClValKit, pTstEnv->szTag);
561 if (RT_SUCCESS(rc))
562 {
563 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Telling VKAT on guest to begin a new test set ...\n");
564 rc = AudioTestSvcClientTestSetBegin(&pTstEnv->u.Host.AtsClGuest, pTstEnv->szTag);
565 if (RT_FAILURE(rc))
566 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
567 "Beginning test set on guest failed with %Rrc\n", rc);
568 }
569 else
570 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
571 "Beginning test set on host (Validation Kit audio driver) failed with %Rrc\n", rc);
572
573 if (RT_SUCCESS(rc))
574 {
575 unsigned uSeq = 0;
576 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
577 {
578 int rc2 = audioTestOne(pTstEnv, &g_aTests[i], uSeq);
579 if (RT_SUCCESS(rc))
580 rc = rc2;
581
582 if (!g_aTests[i].fExcluded)
583 uSeq++;
584
585 if (g_fTerminate)
586 break;
587 }
588
589 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Ending test set on guest ...\n");
590 int rc2 = AudioTestSvcClientTestSetEnd(&pTstEnv->u.Host.AtsClGuest, pTstEnv->szTag);
591 if (RT_FAILURE(rc2))
592 {
593 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Ending test set on guest failed with %Rrc\n", rc2);
594 if (RT_SUCCESS(rc))
595 rc = rc2;
596 }
597
598 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Ending test set on host (Validation Kit audio driver) ...\n");
599 rc2 = AudioTestSvcClientTestSetEnd(&pTstEnv->u.Host.AtsClValKit, pTstEnv->szTag);
600 if (RT_FAILURE(rc2))
601 {
602 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
603 "Ending test set on host (Validation Kit audio driver) failed with %Rrc\n", rc2);
604 if (RT_SUCCESS(rc))
605 rc = rc2;
606 }
607
608 if ( !g_fTerminate
609 && RT_SUCCESS(rc))
610 {
611 /*
612 * Download guest + Validation Kit audio driver test sets to our output directory.
613 */
614 char szFileName[RTPATH_MAX];
615 if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s-guest.tar.gz", pTstEnv->szTag))
616 {
617 rc = RTPathJoin(pTstEnv->u.Host.szPathTestSetGuest, sizeof(pTstEnv->u.Host.szPathTestSetGuest),
618 pTstEnv->szPathOut, szFileName);
619 if (RT_SUCCESS(rc))
620 {
621 if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s-host.tar.gz", pTstEnv->szTag))
622 {
623 rc = RTPathJoin(pTstEnv->u.Host.szPathTestSetValKit, sizeof(pTstEnv->u.Host.szPathTestSetValKit),
624 pTstEnv->szPathOut, szFileName);
625 }
626 else
627 rc = VERR_BUFFER_OVERFLOW;
628 }
629 else
630 rc = VERR_BUFFER_OVERFLOW;
631
632 if (RT_SUCCESS(rc))
633 {
634 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Downloading guest test set to '%s'\n",
635 pTstEnv->u.Host.szPathTestSetGuest);
636 rc = AudioTestSvcClientTestSetDownload(&pTstEnv->u.Host.AtsClGuest,
637 pTstEnv->szTag, pTstEnv->u.Host.szPathTestSetGuest);
638 }
639
640 if (RT_SUCCESS(rc))
641 {
642 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Downloading host test set to '%s'\n",
643 pTstEnv->u.Host.szPathTestSetValKit);
644 rc = AudioTestSvcClientTestSetDownload(&pTstEnv->u.Host.AtsClValKit,
645 pTstEnv->szTag, pTstEnv->u.Host.szPathTestSetValKit);
646 }
647 }
648 else
649 rc = VERR_BUFFER_OVERFLOW;
650
651 if ( RT_SUCCESS(rc)
652 && !pTstEnv->fSkipVerify)
653 {
654 rc = audioVerifyOne(pTstEnv->u.Host.szPathTestSetGuest, pTstEnv->u.Host.szPathTestSetValKit);
655 }
656 else
657 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification skipped\n");
658
659 RTFileDelete(pTstEnv->u.Host.szPathTestSetGuest);
660 RTFileDelete(pTstEnv->u.Host.szPathTestSetValKit);
661 }
662 }
663 }
664 else
665 AssertFailed();
666
667 /* Clean up. */
668 RTDirRemove(pTstEnv->szPathTemp);
669 RTDirRemove(pTstEnv->szPathOut);
670
671 if (RT_FAILURE(rc))
672 RTTestFailed(g_hTest, "Test worker failed with %Rrc", rc);
673
674 return rc;
675}
676
677/** Option help for the 'test' command. */
678static DECLCALLBACK(const char *) audioTestCmdTestHelp(PCRTGETOPTDEF pOpt)
679{
680 switch (pOpt->iShort)
681 {
682 case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)";
683 case 'b': return "The audio backend to use";
684 case 'd': return "Go via DrvAudio instead of directly interfacing with the backend";
685 case 'e': return "Exclude the given test id from the list";
686 case 'i': return "Include the given test id in the list";
687 case VKAT_TEST_OPT_COUNT: return "Number of test iterations to perform for selected tests\n"
688 " Default: random number";
689 case VKAT_TEST_OPT_DEV: return "Name of the input/output device to use\n"
690 " Default: default device";
691 case VKAT_TEST_OPT_GUEST_ATS_ADDR: return "Address of guest ATS to connect to\n"
692 " Default: " ATS_TCP_DEF_CONNECT_GUEST_STR;
693 case VKAT_TEST_OPT_GUEST_ATS_PORT: return "Port of guest ATS to connect to (needs NAT port forwarding)\n"
694 " Default: 6042"; /* ATS_TCP_DEF_CONNECT_PORT_GUEST */
695 case VKAT_TEST_OPT_HOST_ATS_ADDR: return "Address of host ATS to connect to\n"
696 " Default: " ATS_TCP_DEF_CONNECT_HOST_ADDR_STR;
697 case VKAT_TEST_OPT_HOST_ATS_PORT: return "Port of host ATS to connect to\n"
698 " Default: 6052"; /* ATS_TCP_DEF_BIND_PORT_VALKIT */
699 case VKAT_TEST_OPT_MODE: return "Specifies the test mode to use when running the tests";
700 case VKAT_TEST_OPT_NO_VERIFY: return "Skips the verification step";
701 case VKAT_TEST_OPT_OUTDIR: return "Specifies the output directory to use";
702 case VKAT_TEST_OPT_PAUSE: return "Not yet implemented";
703 case VKAT_TEST_OPT_PCM_HZ: return "Specifies the PCM Hetz (Hz) rate to use\n"
704 " Default: 44100";
705 case VKAT_TEST_OPT_PCM_BIT: return "Specifies the PCM sample bits (i.e. 16) to use\n"
706 " Default: 16";
707 case VKAT_TEST_OPT_PCM_CHAN: return "Specifies the number of PCM channels to use\n"
708 " Default: 2";
709 case VKAT_TEST_OPT_PCM_SIGNED: return "Specifies whether to use signed (true) or unsigned (false) samples\n"
710 " Default: true";
711 case VKAT_TEST_OPT_PROBE_BACKENDS: return "Specifies whether to probe all (available) backends until a working one is found\n"
712 " Default: false";
713 case VKAT_TEST_OPT_TAG: return "Specifies the test set tag to use";
714 case VKAT_TEST_OPT_TEMPDIR: return "Specifies the temporary directory to use";
715 case VKAT_TEST_OPT_VOL: return "Specifies the audio volume (in percent, 0-100) to use";
716 case VKAT_TEST_OPT_TCP_BIND_ADDRESS: return "Specifies the TCP address listening to (server mode)";
717 case VKAT_TEST_OPT_TCP_BIND_PORT: return "Specifies the TCP port listening to (server mode)";
718 case VKAT_TEST_OPT_TCP_CONNECT_ADDRESS: return "Specifies the TCP address to connect to (client mode)";
719 case VKAT_TEST_OPT_TCP_CONNECT_PORT: return "Specifies the TCP port to connect to (client mode)";
720 default:
721 break;
722 }
723 return NULL;
724}
725
726/**
727 * Main (entry) function for the testing functionality of VKAT.
728 *
729 * @returns Program exit code.
730 * @param pGetState RTGetOpt state.
731 */
732static DECLCALLBACK(RTEXITCODE) audioTestMain(PRTGETOPTSTATE pGetState)
733{
734 AUDIOTESTENV TstEnv;
735 RT_ZERO(TstEnv);
736
737 int rc;
738
739 const char *pszTag = NULL; /* Custom tag to use. Can be NULL if not being used. */
740 PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
741 bool fWithDrvAudio = false;
742 uint8_t cPcmSampleBit = 0;
743 uint8_t cPcmChannels = 0;
744 uint32_t uPcmHz = 0;
745 bool fPcmSigned = true;
746 bool fProbeBackends = false;
747
748 const char *pszGuestTcpAddr = NULL;
749 uint16_t uGuestTcpPort = ATS_TCP_DEF_BIND_PORT_GUEST;
750 const char *pszValKitTcpAddr = NULL;
751 uint16_t uValKitTcpPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
752
753 int ch;
754 RTGETOPTUNION ValueUnion;
755 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
756 {
757 switch (ch)
758 {
759 case 'a':
760 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
761 g_aTests[i].fExcluded = true;
762 break;
763
764 case 'b':
765 pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
766 if (pDrvReg == NULL)
767 return RTEXITCODE_SYNTAX;
768 break;
769
770 case 'd':
771 fWithDrvAudio = true;
772 break;
773
774 case 'e':
775 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
776 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
777 g_aTests[ValueUnion.u32].fExcluded = true;
778 break;
779
780 case VKAT_TEST_OPT_GUEST_ATS_ADDR:
781 pszGuestTcpAddr = ValueUnion.psz;
782 break;
783
784 case VKAT_TEST_OPT_GUEST_ATS_PORT:
785 uGuestTcpPort = ValueUnion.u32;
786 break;
787
788 case VKAT_TEST_OPT_HOST_ATS_ADDR:
789 pszValKitTcpAddr = ValueUnion.psz;
790 break;
791
792 case VKAT_TEST_OPT_HOST_ATS_PORT:
793 uValKitTcpPort = ValueUnion.u32;
794 break;
795
796 case VKAT_TEST_OPT_MODE:
797 if (TstEnv.enmMode != AUDIOTESTMODE_UNKNOWN)
798 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Test mode (guest / host) already specified");
799 TstEnv.enmMode = RTStrICmp(ValueUnion.psz, "guest") == 0 ? AUDIOTESTMODE_GUEST : AUDIOTESTMODE_HOST;
800 break;
801
802 case VKAT_TEST_OPT_NO_VERIFY:
803 TstEnv.fSkipVerify = true;
804 break;
805
806 case 'i':
807 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
808 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
809 g_aTests[ValueUnion.u32].fExcluded = false;
810 break;
811
812 case VKAT_TEST_OPT_COUNT:
813 return RTMsgErrorExitFailure("Not yet implemented!");
814
815 case VKAT_TEST_OPT_DEV:
816 rc = RTStrCopy(TstEnv.szDev, sizeof(TstEnv.szDev), ValueUnion.psz);
817 if (RT_FAILURE(rc))
818 return RTMsgErrorExitFailure("Failed to copy out device: %Rrc", rc);
819 break;
820
821 case VKAT_TEST_OPT_PAUSE:
822 return RTMsgErrorExitFailure("Not yet implemented!");
823
824 case VKAT_TEST_OPT_OUTDIR:
825 rc = RTStrCopy(TstEnv.szPathOut, sizeof(TstEnv.szPathOut), ValueUnion.psz);
826 if (RT_FAILURE(rc))
827 return RTMsgErrorExitFailure("Failed to copy out directory: %Rrc", rc);
828 break;
829
830 case VKAT_TEST_OPT_PCM_BIT:
831 cPcmSampleBit = ValueUnion.u8;
832 break;
833
834 case VKAT_TEST_OPT_PCM_CHAN:
835 cPcmChannels = ValueUnion.u8;
836 break;
837
838 case VKAT_TEST_OPT_PCM_HZ:
839 uPcmHz = ValueUnion.u32;
840 break;
841
842 case VKAT_TEST_OPT_PCM_SIGNED:
843 fPcmSigned = ValueUnion.f;
844 break;
845
846 case VKAT_TEST_OPT_PROBE_BACKENDS:
847 fProbeBackends = ValueUnion.f;
848 break;
849
850 case VKAT_TEST_OPT_TAG:
851 pszTag = ValueUnion.psz;
852 break;
853
854 case VKAT_TEST_OPT_TEMPDIR:
855 rc = RTStrCopy(TstEnv.szPathTemp, sizeof(TstEnv.szPathTemp), ValueUnion.psz);
856 if (RT_FAILURE(rc))
857 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Temp dir invalid, rc=%Rrc", rc);
858 break;
859
860 case VKAT_TEST_OPT_VOL:
861 TstEnv.uVolumePercent = ValueUnion.u8;
862 break;
863
864 case VKAT_TEST_OPT_TCP_BIND_ADDRESS:
865 rc = RTStrCopy(TstEnv.TcpOpts.szBindAddr, sizeof(TstEnv.TcpOpts.szBindAddr), ValueUnion.psz);
866 if (RT_FAILURE(rc))
867 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Bind address invalid, rc=%Rrc", rc);
868 break;
869
870 case VKAT_TEST_OPT_TCP_BIND_PORT:
871 TstEnv.TcpOpts.uBindPort = ValueUnion.u16;
872 break;
873
874 case VKAT_TEST_OPT_TCP_CONNECT_ADDRESS:
875 rc = RTStrCopy(TstEnv.TcpOpts.szConnectAddr, sizeof(TstEnv.TcpOpts.szConnectAddr), ValueUnion.psz);
876 if (RT_FAILURE(rc))
877 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Connect address invalid, rc=%Rrc", rc);
878 break;
879
880 case VKAT_TEST_OPT_TCP_CONNECT_PORT:
881 TstEnv.TcpOpts.uConnectPort = ValueUnion.u16;
882 break;
883
884 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
885
886 default:
887 return RTGetOptPrintError(ch, &ValueUnion);
888 }
889 }
890
891 /*
892 * Start testing.
893 */
894 RTTestBanner(g_hTest);
895
896 /* Initialize the custom test parameters with sensible defaults if nothing else is given. */
897 PDMAudioPropsInit(&TstEnv.Props,
898 cPcmSampleBit ? cPcmSampleBit / 8 : 2 /* 16-bit */, fPcmSigned, cPcmChannels ? cPcmChannels : 2,
899 uPcmHz ? uPcmHz : 44100);
900
901 if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)
902 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No test mode (--mode) specified!\n");
903
904 /* Validate TCP options. */
905 if ( TstEnv.TcpOpts.szBindAddr[0]
906 && TstEnv.TcpOpts.szConnectAddr[0])
907 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one TCP connection mode (connect as client *or* bind as server) can be specified) at a time!\n");
908
909 AUDIOTESTDRVSTACK DrvStack;
910 for (size_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
911 {
912 if (fProbeBackends)
913 {
914 pDrvReg = g_aBackends[i].pDrvReg;
915 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing for backend '%s' ...\n", g_aBackends[i].pszName);
916 }
917 rc = audioTestDriverStackInitEx(&DrvStack, pDrvReg,
918 true /* fEnabledIn */, true /* fEnabledOut */, fWithDrvAudio); /** @todo Make in/out configurable, too. */
919 if (RT_SUCCESS(rc))
920 {
921 if (fProbeBackends)
922 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing backend '%s' successful\n", g_aBackends[i].pszName);
923 break;
924 }
925 if (fProbeBackends)
926 {
927 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing backend '%s' failed with %Rrc, trying next one\n",
928 g_aBackends[i].pszName, rc);
929 continue;
930 }
931 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to init driver stack: %Rrc\n", rc);
932 }
933
934 if (RT_FAILURE(rc))
935 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Probing all backends failed, unable to continue\n");
936
937 PPDMAUDIOHOSTDEV pDev;
938 rc = audioTestDevicesEnumerateAndCheck(&DrvStack, TstEnv.szDev, &pDev);
939 if (RT_FAILURE(rc))
940 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Enumerating device(s) failed: %Rrc\n", rc);
941
942 /* For now all tests have the same test environment and driver stack. */
943 rc = audioTestEnvInit(&TstEnv, &DrvStack);
944 if (RT_SUCCESS(rc))
945 rc = audioTestWorker(&TstEnv);
946
947 audioTestEnvDestroy(&TstEnv);
948 audioTestDriverStackDelete(&DrvStack);
949
950 if (RT_FAILURE(rc)) /* Let us know that something went wrong in case we forgot to mention it. */
951 RTTestFailed(g_hTest, "Testing failed with %Rrc\n", rc);
952
953 /*
954 * Print summary and exit.
955 */
956 return RTTestSummaryAndDestroy(g_hTest);
957}
958
959
960const VKATCMD g_CmdTest =
961{
962 "test",
963 audioTestMain,
964 "Runs audio tests and creates an audio test set.",
965 g_aCmdTestOptions,
966 RT_ELEMENTS(g_aCmdTestOptions),
967 audioTestCmdTestHelp,
968 true /* fNeedsTransport */
969};
970
971
972/*********************************************************************************************************************************
973* Command: verify *
974*********************************************************************************************************************************/
975
976static int audioVerifyOpenTestSet(const char *pszPathSet, PAUDIOTESTSET pSet)
977{
978 int rc;
979
980 char szPathExtracted[RTPATH_MAX];
981
982 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Opening test set '%s'\n", pszPathSet);
983
984 const bool fPacked = AudioTestSetIsPacked(pszPathSet);
985 if (fPacked)
986 {
987 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set is an archive and needs to be unpacked\n");
988
989 char szPathTemp[RTPATH_MAX];
990 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
991 if (RT_SUCCESS(rc))
992 {
993 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using temporary directory '%s'\n", szPathTemp);
994
995 rc = RTPathJoin(szPathExtracted, sizeof(szPathExtracted), szPathTemp, "vkat-testset-XXXX");
996 if (RT_SUCCESS(rc))
997 {
998 rc = RTDirCreateTemp(szPathExtracted, 0755);
999 if (RT_SUCCESS(rc))
1000 {
1001 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unpacking archive to '%s'\n", szPathExtracted);
1002 rc = AudioTestSetUnpack(pszPathSet, szPathExtracted);
1003 if (RT_SUCCESS(rc))
1004 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Archive successfully unpacked\n");
1005 }
1006 }
1007 }
1008 }
1009 else
1010 rc = VINF_SUCCESS;
1011
1012 if (RT_SUCCESS(rc))
1013 rc = AudioTestSetOpen(pSet, fPacked ? szPathExtracted : pszPathSet);
1014
1015 if (RT_FAILURE(rc))
1016 RTTestFailed(g_hTest, "Unable to open / unpack test set archive: %Rrc", rc);
1017
1018 return rc;
1019}
1020
1021/**
1022 * Verifies one test set pair.
1023 *
1024 * @returns VBox status code.
1025 * @param pszPathSetA Absolute path to test set A.
1026 * @param pszPathSetB Absolute path to test set B.
1027 */
1028static int audioVerifyOne(const char *pszPathSetA, const char *pszPathSetB)
1029{
1030 RTTestSubF(g_hTest, "Verifying");
1031 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verifying test set '%s' with test set '%s'\n", pszPathSetA, pszPathSetB);
1032
1033 AUDIOTESTSET SetA, SetB;
1034 int rc = audioVerifyOpenTestSet(pszPathSetA, &SetA);
1035 if (RT_SUCCESS(rc))
1036 {
1037 rc = audioVerifyOpenTestSet(pszPathSetB, &SetB);
1038 if (RT_SUCCESS(rc))
1039 {
1040 AUDIOTESTERRORDESC errDesc;
1041 rc = AudioTestSetVerify(&SetA, &SetB, &errDesc);
1042 if (RT_SUCCESS(rc))
1043 {
1044 uint32_t const cErr = AudioTestErrorDescCount(&errDesc);
1045 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "%RU32 errors occurred while verifying\n", cErr);
1046
1047 /** @todo Use some AudioTestErrorXXX API for enumeration here later. */
1048 PAUDIOTESTERRORENTRY pErrEntry;
1049 RTListForEach(&errDesc.List, pErrEntry, AUDIOTESTERRORENTRY, Node)
1050 {
1051 if (RT_FAILURE(pErrEntry->rc))
1052 RTTestFailed(g_hTest, "%s\n", pErrEntry->szDesc);
1053 else
1054 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "%s\n", pErrEntry->szDesc);
1055 }
1056
1057 if (cErr == 0)
1058 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification successful\n");
1059
1060 AudioTestErrorDescDestroy(&errDesc);
1061 }
1062 else
1063 RTTestFailed(g_hTest, "Verification failed with %Rrc", rc);
1064
1065#ifdef DEBUG
1066 if (g_fDrvAudioDebug)
1067 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
1068 "\n"
1069 "Use the following command line to re-run verification in the debugger:\n"
1070 "gdb --args ./VBoxAudioTest -vvvv --debug-audio verify \"%s\" \"%s\"\n",
1071 SetA.szPathAbs, SetB.szPathAbs);
1072#endif
1073 if (!g_fDrvAudioDebug) /* Don't wipe stuff when debugging. Can be useful for introspecting data. */
1074 AudioTestSetWipe(&SetB);
1075 AudioTestSetClose(&SetB);
1076 }
1077
1078 if (!g_fDrvAudioDebug) /* Ditto. */
1079 AudioTestSetWipe(&SetA);
1080 AudioTestSetClose(&SetA);
1081 }
1082
1083 RTTestSubDone(g_hTest);
1084
1085 return rc;
1086}
1087
1088/**
1089 * Main (entry) function for the verification functionality of VKAT.
1090 *
1091 * @returns Program exit code.
1092 * @param pGetState RTGetOpt state.
1093 */
1094static DECLCALLBACK(RTEXITCODE) audioVerifyMain(PRTGETOPTSTATE pGetState)
1095{
1096 /*
1097 * Parse options and process arguments.
1098 */
1099 const char *apszSets[2] = { NULL, NULL };
1100 unsigned iTestSet = 0;
1101
1102 int ch;
1103 RTGETOPTUNION ValueUnion;
1104 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
1105 {
1106 switch (ch)
1107 {
1108 case VKAT_VERIFY_OPT_TAG:
1109 break;
1110
1111 case VINF_GETOPT_NOT_OPTION:
1112 if (iTestSet == 0)
1113 RTTestBanner(g_hTest);
1114 if (iTestSet >= RT_ELEMENTS(apszSets))
1115 return RTMsgErrorExitFailure("Only two test sets can be verified at one time");
1116 apszSets[iTestSet++] = ValueUnion.psz;
1117 break;
1118
1119 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
1120
1121 default:
1122 return RTGetOptPrintError(ch, &ValueUnion);
1123 }
1124 }
1125
1126 if (!iTestSet)
1127 return RTMsgErrorExitFailure("At least one test set must be specified");
1128
1129 int rc = VINF_SUCCESS;
1130
1131 /*
1132 * If only test set A is given, default to the current directory
1133 * for test set B.
1134 */
1135 char szDirCur[RTPATH_MAX];
1136 if (iTestSet == 1)
1137 {
1138 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
1139 if (RT_SUCCESS(rc))
1140 apszSets[1] = szDirCur;
1141 else
1142 RTTestFailed(g_hTest, "Failed to retrieve current directory: %Rrc", rc);
1143 }
1144
1145 if (RT_SUCCESS(rc))
1146 audioVerifyOne(apszSets[0], apszSets[1]);
1147
1148 /*
1149 * Print summary and exit.
1150 */
1151 return RTTestSummaryAndDestroy(g_hTest);
1152}
1153
1154
1155const VKATCMD g_CmdVerify =
1156{
1157 "verify",
1158 audioVerifyMain,
1159 "Verifies a formerly created audio test set.",
1160 g_aCmdVerifyOptions,
1161 RT_ELEMENTS(g_aCmdVerifyOptions),
1162 NULL,
1163 false /* fNeedsTransport */
1164};
1165
1166
1167/*********************************************************************************************************************************
1168* Main *
1169*********************************************************************************************************************************/
1170
1171/**
1172 * Ctrl-C signal handler.
1173 *
1174 * This just sets g_fTerminate and hope it will be noticed soon. It restores
1175 * the SIGINT action to default, so that a second Ctrl-C will have the normal
1176 * effect (just in case the code doesn't respond to g_fTerminate).
1177 */
1178static void audioTestSignalHandler(int iSig) RT_NOEXCEPT
1179{
1180 Assert(iSig == SIGINT); RT_NOREF(iSig);
1181 RTPrintf("Ctrl-C!\n");
1182 ASMAtomicWriteBool(&g_fTerminate, true);
1183 signal(SIGINT, SIG_DFL);
1184}
1185
1186/**
1187 * Commands.
1188 */
1189const VKATCMD *g_apCommands[] =
1190{
1191 &g_CmdTest,
1192 &g_CmdVerify,
1193 &g_CmdEnum,
1194 &g_CmdPlay,
1195 &g_CmdRec,
1196 &g_CmdSelfTest
1197};
1198
1199/**
1200 * Shows tool usage text.
1201 */
1202RTEXITCODE audioTestUsage(PRTSTREAM pStrm)
1203{
1204 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n",
1205 RTPathFilename(RTProcExecutablePath()));
1206 RTStrmPrintf(pStrm,
1207 "\n"
1208 "Global Options:\n"
1209 " --debug-audio\n"
1210 " Enables (DrvAudio) debugging.\n"
1211 " --debug-audio-path=<path>\n"
1212 " Tells DrvAudio where to put its debug output (wav-files).\n"
1213 " -q, --quiet\n"
1214 " Sets verbosity to zero.\n"
1215 " -v, --verbose\n"
1216 " Increase verbosity.\n"
1217 " -V, --version\n"
1218 " Displays version.\n"
1219 " -h, -?, --help\n"
1220 " Displays help.\n"
1221 );
1222
1223 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
1224 {
1225 PCVKATCMD const pCmd = g_apCommands[iCmd];
1226 RTStrmPrintf(pStrm,
1227 "\n"
1228 "Command '%s':\n"
1229 " %s\n"
1230 "Options for '%s':\n",
1231 pCmd->pszCommand, pCmd->pszDesc, pCmd->pszCommand);
1232 PCRTGETOPTDEF const paOptions = pCmd->paOptions;
1233 for (unsigned i = 0; i < pCmd->cOptions; i++)
1234 {
1235 if (RT_C_IS_PRINT(paOptions[i].iShort))
1236 RTStrmPrintf(pStrm, " -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);
1237 else
1238 RTStrmPrintf(pStrm, " %s\n", paOptions[i].pszLong);
1239
1240 const char *pszHelp = NULL;
1241 if (pCmd->pfnOptionHelp)
1242 pszHelp = pCmd->pfnOptionHelp(&paOptions[i]);
1243 if (pszHelp)
1244 RTStrmPrintf(pStrm, " %s\n", pszHelp);
1245 }
1246
1247 if (pCmd->fNeedsTransport)
1248 {
1249 for (uintptr_t iTx = 0; iTx < g_cTransports; iTx++)
1250 g_apTransports[iTx]->pfnUsage(pStrm);
1251 }
1252 }
1253
1254 return RTEXITCODE_SUCCESS;
1255}
1256
1257/**
1258 * Shows tool version.
1259 */
1260RTEXITCODE audioTestVersion(void)
1261{
1262 RTPrintf("%s\n", RTBldCfgRevisionStr());
1263 return RTEXITCODE_SUCCESS;
1264}
1265
1266/**
1267 * Shows the logo.
1268 *
1269 * @param pStream Output stream to show logo on.
1270 */
1271void audioTestShowLogo(PRTSTREAM pStream)
1272{
1273 RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) Version " VBOX_VERSION_STRING " - r%s\n"
1274 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
1275 "All rights reserved.\n\n", RTBldCfgRevisionStr());
1276}
1277
1278int main(int argc, char **argv)
1279{
1280 /*
1281 * Init IPRT.
1282 */
1283 int rc = RTR3InitExe(argc, &argv, 0);
1284 if (RT_FAILURE(rc))
1285 {
1286 RTPrintf("RTR3InitExe() failed with %Rrc\n", rc);
1287 return RTMsgInitFailure(rc);
1288 }
1289
1290 /*
1291 * Daemonize ourselves if asked to.
1292 */
1293 bool fDaemonize = false;
1294 bool fDaemonized = false;
1295
1296 for (int i = 1; i < argc; i++)
1297 {
1298 const char *psz = argv[i];
1299 if (!RTStrICmp(psz, "--daemonize"))
1300 {
1301 fDaemonize = true;
1302 continue;
1303 }
1304 else if (!RTStrICmp(psz, "--daemonized"))
1305 {
1306 fDaemonized = true;
1307 continue;
1308 }
1309 }
1310
1311 if (fDaemonize)
1312 {
1313 if (!fDaemonized)
1314 {
1315 audioTestShowLogo(g_pStdOut);
1316
1317 rc = RTProcDaemonize(argv, "--daemonized");
1318 if (RT_FAILURE(rc))
1319 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize() failed with %Rrc\n", rc);
1320
1321 RTMsgInfo("Starting in background (daemonizing) ...");
1322 return RTEXITCODE_SUCCESS;
1323 }
1324 /* else continue running in background. */
1325 }
1326
1327 /*
1328 * Init test and globals.
1329 * Note: Needs to be done *after* daemonizing, otherwise the child will fail!
1330 */
1331 rc = RTTestCreate("AudioTest", &g_hTest);
1332 if (RT_FAILURE(rc))
1333 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTTestCreate() failed with %Rrc\n", rc);
1334
1335#ifdef RT_OS_WINDOWS
1336 HRESULT hrc = CoInitializeEx(NULL /*pReserved*/, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE);
1337 if (FAILED(hrc))
1338 RTMsgWarning("CoInitializeEx failed: %#x", hrc);
1339#endif
1340
1341 /*
1342 * Configure release logging to go to stderr.
1343 */
1344 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
1345#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1346 fFlags |= RTLOGFLAGS_USECRLF;
1347#endif
1348 static const char * const g_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
1349 rc = RTLogCreate(&g_pRelLogger, fFlags, "all.e.l", "VKAT_RELEASE_LOG",
1350 RT_ELEMENTS(g_apszLogGroups), g_apszLogGroups, RTLOGDEST_STDERR, NULL /*"vkat-release.log"*/);
1351 if (RT_SUCCESS(rc))
1352 RTLogRelSetDefaultInstance(g_pRelLogger);
1353 else
1354 RTMsgWarning("Failed to create release logger: %Rrc", rc);
1355
1356 /*
1357 * Install a Ctrl-C signal handler.
1358 */
1359#ifdef RT_OS_WINDOWS
1360 signal(SIGINT, audioTestSignalHandler);
1361#else
1362 struct sigaction sa;
1363 RT_ZERO(sa);
1364 sa.sa_handler = audioTestSignalHandler;
1365 sigaction(SIGINT, &sa, NULL);
1366#endif
1367
1368 /*
1369 * Process common options.
1370 */
1371 RTGETOPTSTATE GetState;
1372 rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions,
1373 RT_ELEMENTS(g_aCmdCommonOptions), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
1374 AssertRCReturn(rc, RTEXITCODE_INIT);
1375
1376 int ch;
1377 RTGETOPTUNION ValueUnion;
1378 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1379 {
1380 switch (ch)
1381 {
1382 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
1383
1384 case VINF_GETOPT_NOT_OPTION:
1385 {
1386 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
1387 {
1388 PCVKATCMD const pCmd = g_apCommands[iCmd];
1389 if (strcmp(ValueUnion.psz, pCmd->pszCommand) == 0)
1390 {
1391 size_t cCombinedOptions = pCmd->cOptions + RT_ELEMENTS(g_aCmdCommonOptions);
1392 if (pCmd->fNeedsTransport)
1393 {
1394 for (uintptr_t iTx = 0; iTx < g_cTransports; iTx++)
1395 cCombinedOptions += g_apTransports[iTx]->cOpts;
1396 }
1397 PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));
1398 if (paCombinedOptions)
1399 {
1400 uint32_t idxOpts = 0;
1401 memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));
1402 idxOpts += RT_ELEMENTS(g_aCmdCommonOptions);
1403 memcpy(&paCombinedOptions[idxOpts], pCmd->paOptions, pCmd->cOptions * sizeof(RTGETOPTDEF));
1404 idxOpts += (uint32_t)pCmd->cOptions;
1405 if (pCmd->fNeedsTransport)
1406 {
1407 for (uintptr_t iTx = 0; iTx < g_cTransports; iTx++)
1408 {
1409 memcpy(&paCombinedOptions[idxOpts],
1410 g_apTransports[iTx]->paOpts, g_apTransports[iTx]->cOpts * sizeof(RTGETOPTDEF));
1411 idxOpts += (uint32_t)g_apTransports[iTx]->cOpts;
1412 }
1413 }
1414
1415 rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,
1416 GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1417 if (RT_SUCCESS(rc))
1418 {
1419 RTEXITCODE rcExit = pCmd->pfnHandler(&GetState);
1420 RTMemFree(paCombinedOptions);
1421 return rcExit;
1422 }
1423 return RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);
1424 }
1425 return RTMsgErrorExitFailure("Out of memory!");
1426 }
1427 }
1428 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
1429 audioTestUsage(g_pStdErr);
1430 return RTEXITCODE_SYNTAX;
1431 }
1432
1433 default:
1434 return RTGetOptPrintError(ch, &ValueUnion);
1435 }
1436 }
1437
1438 RTMsgError("No command specified!\n");
1439 audioTestUsage(g_pStdErr);
1440 return RTEXITCODE_SYNTAX;
1441}
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