VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp@ 99775

Last change on this file since 99775 was 99775, checked in by vboxsync, 18 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: vkatCmdSelfTest.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * Validation Kit Audio Test (VKAT) - Self test.
4 *
5 * Self-test which does a complete audio testing framework run without the need
6 * of a VM or other infrastructure, i.e. all required parts are running locally
7 * on the same machine.
8 *
9 * This self-test does the following:
10 * - 1. Creates a separate thread for the guest side VKAT and connects to the ATS instance on
11 * the host side at port 6052 (ATS_TCP_DEF_BIND_PORT_HOST).
12 * - 2. Uses the Validation Kit audio backend, which in turn creates an ATS instance
13 * listening at port 6062 (ATS_TCP_DEF_BIND_PORT_VALKIT).
14 * - 3. Uses the host test environment which creates an ATS instance
15 * listening at port 6052 (ATS_TCP_DEF_BIND_PORT_HOST).
16 * - 4. Executes a complete test run locally (e.g. without any guest (VM) involved).
17 */
18
19/*
20 * Copyright (C) 2021-2023 Oracle and/or its affiliates.
21 *
22 * This file is part of VirtualBox base platform packages, as
23 * available from https://www.virtualbox.org.
24 *
25 * This program is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU General Public License
27 * as published by the Free Software Foundation, in version 3 of the
28 * License.
29 *
30 * This program is distributed in the hope that it will be useful, but
31 * WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33 * General Public License for more details.
34 *
35 * You should have received a copy of the GNU General Public License
36 * along with this program; if not, see <https://www.gnu.org/licenses>.
37 *
38 * The contents of this file may alternatively be used under the terms
39 * of the Common Development and Distribution License Version 1.0
40 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
41 * in the VirtualBox distribution, in which case the provisions of the
42 * CDDL are applicable instead of those of the GPL.
43 *
44 * You may elect to license modified versions of this file under the
45 * terms and conditions of either the GPL or the CDDL or both.
46 *
47 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
48 */
49
50
51/*********************************************************************************************************************************
52* Header Files *
53*********************************************************************************************************************************/
54
55#include <iprt/ctype.h>
56#include <iprt/errcore.h>
57#include <iprt/getopt.h>
58#include <iprt/message.h>
59#include <iprt/rand.h>
60#include <iprt/test.h>
61
62#include "Audio/AudioHlp.h"
63#include "Audio/AudioTest.h"
64#include "Audio/AudioTestService.h"
65#include "Audio/AudioTestServiceClient.h"
66
67#include "vkatInternal.h"
68
69
70/*********************************************************************************************************************************
71* Internal structures *
72*********************************************************************************************************************************/
73
74/**
75 * Structure for keeping a VKAT self test context.
76 */
77typedef struct SELFTESTCTX
78{
79 /** Common tag for guest and host side. */
80 char szTag[AUDIOTEST_TAG_MAX];
81 /** The driver stack in use. */
82 AUDIOTESTDRVSTACK DrvStack;
83 /** Audio driver to use.
84 * Defaults to the platform's default driver. */
85 PCPDMDRVREG pDrvReg;
86 struct
87 {
88 AUDIOTESTENV TstEnv;
89 /** Where to bind the address of the guest ATS instance to.
90 * Defaults to localhost (127.0.0.1) if empty. */
91 char szAtsAddr[64];
92 /** Port of the guest ATS instance.
93 * Defaults to ATS_ALT_PORT if not set. */
94 uint32_t uAtsPort;
95 } Guest;
96 struct
97 {
98 AUDIOTESTENV TstEnv;
99 /** Address of the guest ATS instance.
100 * Defaults to localhost (127.0.0.1) if not set. */
101 char szGuestAtsAddr[64];
102 /** Port of the guest ATS instance.
103 * Defaults to ATS_DEFAULT_PORT if not set. */
104 uint32_t uGuestAtsPort;
105 /** Address of the Validation Kit audio driver ATS instance.
106 * Defaults to localhost (127.0.0.1) if not set. */
107 char szValKitAtsAddr[64];
108 /** Port of the Validation Kit audio driver ATS instance.
109 * Defaults to ATS_ALT_PORT if not set. */
110 uint32_t uValKitAtsPort;
111 } Host;
112} SELFTESTCTX;
113/** Pointer to a VKAT self test context. */
114typedef SELFTESTCTX *PSELFTESTCTX;
115
116
117/*********************************************************************************************************************************
118* Global Variables *
119*********************************************************************************************************************************/
120
121/** The global self-text context. */
122static SELFTESTCTX g_Ctx;
123
124
125/*********************************************************************************************************************************
126* Driver stack self-test implementation *
127*********************************************************************************************************************************/
128
129/**
130 * Performs a (quick) audio driver stack self test.
131 *
132 * Local only, no guest/host communication involved.
133 *
134 * @returns VBox status code.
135 */
136int AudioTestDriverStackPerformSelftest(void)
137{
138 PCPDMDRVREG pDrvReg = AudioTestGetDefaultBackend();
139
140 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Testing driver stack started\n");
141
142 AUDIOTESTDRVSTACK DrvStack;
143 int rc = audioTestDriverStackProbe(&DrvStack, pDrvReg,
144 true /* fEnabledIn */, true /* fEnabledOut */, false /* fWithDrvAudio */);
145 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
146
147 AUDIOTESTIOOPTS IoOpts;
148 audioTestIoOptsInitDefaults(&IoOpts);
149
150 PPDMAUDIOSTREAM pStream;
151 PDMAUDIOSTREAMCFG CfgAcq;
152 rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &IoOpts.Props,
153 IoOpts.cMsBufferSize, IoOpts.cMsPreBuffer, IoOpts.cMsSchedulingHint,
154 &pStream, &CfgAcq);
155 AssertRCReturn(rc, rc);
156
157 rc = audioTestDriverStackStreamEnable(&DrvStack, pStream);
158 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
159
160 RTTEST_CHECK_RET(g_hTest, audioTestDriverStackStreamIsOkay(&DrvStack, pStream), VERR_AUDIO_STREAM_NOT_READY);
161
162 uint8_t abBuf[_4K];
163 memset(abBuf, 0x42, sizeof(abBuf));
164
165 uint32_t cbWritten;
166 rc = audioTestDriverStackStreamPlay(&DrvStack, pStream, abBuf, sizeof(abBuf), &cbWritten);
167 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
168 RTTEST_CHECK_RET(g_hTest, cbWritten == sizeof(abBuf), VERR_AUDIO_STREAM_NOT_READY);
169
170 audioTestDriverStackStreamDrain(&DrvStack, pStream, true /* fSync */);
171 audioTestDriverStackStreamDestroy(&DrvStack, pStream);
172
173 audioTestDriverStackDelete(&DrvStack);
174
175 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Testing driver stack ended with %Rrc\n", rc);
176 return rc;
177}
178
179
180/*********************************************************************************************************************************
181* Self-test implementation *
182*********************************************************************************************************************************/
183
184/**
185 * Thread callback for mocking the guest (VM) side of things.
186 *
187 * @returns VBox status code.
188 * @param hThread Thread handle.
189 * @param pvUser Pointer to user-supplied data.
190 */
191static DECLCALLBACK(int) audioTestSelftestGuestAtsThread(RTTHREAD hThread, void *pvUser)
192{
193 RT_NOREF(hThread);
194 PSELFTESTCTX pCtx = (PSELFTESTCTX)pvUser;
195
196 PAUDIOTESTENV pTstEnvGst = &pCtx->Guest.TstEnv;
197
198 audioTestEnvInit(pTstEnvGst);
199
200 /* Flag the environment for self test mode. */
201 pTstEnvGst->fSelftest = true;
202
203 /* Tweak the address the guest ATS is trying to connect to the host if anything else is specified.
204 * Note: The host also runs on the same host (this self-test is completely self-contained and does not need a VM). */
205 if (!pTstEnvGst->TcpOpts.szConnectAddr[0])
206 RTStrCopy(pTstEnvGst->TcpOpts.szConnectAddr, sizeof(pTstEnvGst->TcpOpts.szConnectAddr), "127.0.0.1");
207
208 /* Generate tag for guest side. */
209 int rc = RTStrCopy(pTstEnvGst->szTag, sizeof(pTstEnvGst->szTag), pCtx->szTag);
210 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
211
212 rc = AudioTestPathCreateTemp(pTstEnvGst->szPathTemp, sizeof(pTstEnvGst->szPathTemp), "selftest-guest");
213 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
214
215 rc = AudioTestPathCreateTemp(pTstEnvGst->szPathOut, sizeof(pTstEnvGst->szPathOut), "selftest-out");
216 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
217
218 pTstEnvGst->enmMode = AUDIOTESTMODE_GUEST;
219
220 rc = audioTestEnvCreate(pTstEnvGst, &pCtx->DrvStack);
221 if (RT_SUCCESS(rc))
222 {
223 RTThreadUserSignal(hThread);
224
225 rc = audioTestWorker(pTstEnvGst);
226 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, rc);
227
228 audioTestEnvDestroy(pTstEnvGst);
229 }
230
231 return rc;
232}
233
234/**
235 * Main function for performing the self test.
236 *
237 * @returns RTEXITCODE
238 * @param pCtx Self test context to use.
239 */
240static RTEXITCODE audioTestDoSelftest(PSELFTESTCTX pCtx)
241{
242 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Running self test ...\n");
243
244 /* Generate a common tag for guest and host side. */
245 int rc = AudioTestGenTag(pCtx->szTag, sizeof(pCtx->szTag));
246 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
247
248 PAUDIOTESTENV pTstEnvHst = &pCtx->Host.TstEnv;
249
250 audioTestEnvInit(pTstEnvHst);
251
252 /* Flag the environment for self test mode. */
253 pTstEnvHst->fSelftest = true;
254
255 /* One test iteration with a 5s maximum test tone is enough for a (quick) self test. */
256 pTstEnvHst->cIterations = 1;
257 pTstEnvHst->ToneParms.msDuration = RTRandU32Ex(500, RT_MS_5SEC);
258
259 /* Generate tag for host side. */
260 rc = RTStrCopy(pTstEnvHst->szTag, sizeof(pTstEnvHst->szTag), pCtx->szTag);
261 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
262
263 rc = AudioTestPathCreateTemp(pTstEnvHst->szPathTemp, sizeof(pTstEnvHst->szPathTemp), "selftest-tmp");
264 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
265
266 rc = AudioTestPathCreateTemp(pTstEnvHst->szPathOut, sizeof(pTstEnvHst->szPathOut), "selftest-out");
267 RTTEST_CHECK_RC_OK_RET(g_hTest, rc, RTEXITCODE_FAILURE);
268
269 /*
270 * Step 1.
271 */
272 RTTHREAD hThreadGstAts = NIL_RTTHREAD;
273
274 bool const fStartGuestAts = RTStrNLen(pCtx->Host.szGuestAtsAddr, sizeof(pCtx->Host.szGuestAtsAddr)) == 0;
275 if (fStartGuestAts)
276 {
277 /* Step 1b. */
278 rc = RTThreadCreate(&hThreadGstAts, audioTestSelftestGuestAtsThread, pCtx, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
279 "VKATGstAts");
280 if (RT_SUCCESS(rc))
281 rc = RTThreadUserWait(hThreadGstAts, RT_MS_30SEC);
282 }
283
284 RTThreadSleep(2000); /* Fudge: Wait until guest ATS is up. 2 seconds should be enough (tm). */
285
286 if (RT_SUCCESS(rc))
287 {
288 /*
289 * Steps 2 + 3.
290 */
291 pTstEnvHst->enmMode = AUDIOTESTMODE_HOST;
292
293 rc = audioTestEnvCreate(pTstEnvHst, &pCtx->DrvStack);
294 if (RT_SUCCESS(rc))
295 {
296 /*
297 * Step 4.
298 */
299 rc = audioTestWorker(pTstEnvHst);
300 RTTEST_CHECK_RC_OK(g_hTest, rc);
301
302 audioTestEnvDestroy(pTstEnvHst);
303 }
304 }
305
306 /*
307 * Shutting down.
308 */
309 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down self test\n");
310
311 /* If we started the guest ATS ourselves, wait for it to terminate properly. */
312 if (fStartGuestAts)
313 {
314 int rcThread;
315 int rc2 = RTThreadWait(hThreadGstAts, RT_MS_30SEC, &rcThread);
316 if (RT_SUCCESS(rc2))
317 rc2 = rcThread;
318 if (RT_FAILURE(rc2))
319 RTTestFailed(g_hTest, "Shutting down guest ATS failed with %Rrc\n", rc2);
320 if (RT_SUCCESS(rc))
321 rc = rc2;
322 }
323
324 if (RT_FAILURE(rc))
325 RTTestFailed(g_hTest, "Self test failed with %Rrc\n", rc);
326
327 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
328}
329
330
331/*********************************************************************************************************************************
332* Command: selftest *
333*********************************************************************************************************************************/
334
335/**
336 * Command line parameters for self-test mode.
337 */
338static const RTGETOPTDEF s_aCmdSelftestOptions[] =
339{
340 { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
341 { "--backend", 'b', RTGETOPT_REQ_STRING },
342 { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },
343 { "--with-mixer", 'm', RTGETOPT_REQ_NOTHING },
344 { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
345 { "--include", 'i', RTGETOPT_REQ_UINT32 }
346};
347
348/** the 'selftest' command option help. */
349static DECLCALLBACK(const char *) audioTestCmdSelftestHelp(PCRTGETOPTDEF pOpt)
350{
351 switch (pOpt->iShort)
352 {
353 case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)";
354 case 'b': return "The audio backend to use";
355 case 'd': return "Go via DrvAudio instead of directly interfacing with the backend";
356 case 'e': return "Exclude the given test id from the list";
357 case 'i': return "Include the given test id in the list";
358 case 'm': return "Use the internal mixing engine explicitly";
359 default: return NULL;
360 }
361}
362
363/**
364 * The 'selftest' command handler.
365 *
366 * @returns Program exit code.
367 * @param pGetState RTGetOpt state.
368 */
369static DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)
370{
371 RT_ZERO(g_Ctx);
372
373 audioTestEnvInit(&g_Ctx.Guest.TstEnv);
374 audioTestEnvInit(&g_Ctx.Host.TstEnv);
375
376 AUDIOTESTIOOPTS IoOpts;
377 audioTestIoOptsInitDefaults(&IoOpts);
378
379 /* Argument processing loop: */
380 int rc;
381 RTGETOPTUNION ValueUnion;
382 while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)
383 {
384 switch (rc)
385 {
386 case 'a':
387 for (unsigned i = 0; i < g_cTests; i++)
388 g_aTests[i].fExcluded = true;
389 break;
390
391 case 'b':
392 g_Ctx.pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
393 if (g_Ctx.pDrvReg == NULL)
394 return RTEXITCODE_SYNTAX;
395 break;
396
397 case 'd':
398 IoOpts.fWithDrvAudio = true;
399 break;
400
401 case 'e':
402 if (ValueUnion.u32 >= g_cTests)
403 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
404 g_aTests[ValueUnion.u32].fExcluded = true;
405 break;
406
407 case 'i':
408 if (ValueUnion.u32 >= g_cTests)
409 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
410 g_aTests[ValueUnion.u32].fExcluded = false;
411 break;
412
413 case 'm':
414 IoOpts.fWithMixer = true;
415 break;
416
417 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion, &g_CmdSelfTest);
418
419 default:
420 return RTGetOptPrintError(rc, &ValueUnion);
421 }
422 }
423
424 /* For simplicity both test environments, guest and host, will have the same I/O options.
425 ** @todo Make this indepedent by a prefix, "--[guest|host]-<option>" -> e.g. "--guest-with-drv-audio". */
426 memcpy(&g_Ctx.Guest.TstEnv.IoOpts, &IoOpts, sizeof(AUDIOTESTIOOPTS));
427 memcpy(&g_Ctx.Host.TstEnv.IoOpts, &IoOpts, sizeof(AUDIOTESTIOOPTS));
428
429 rc = AudioTestDriverStackPerformSelftest();
430 if (RT_FAILURE(rc))
431 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Testing driver stack failed: %Rrc\n", rc);
432
433 /* Go with the Validation Kit audio backend if nothing else is specified. */
434 if (g_Ctx.pDrvReg == NULL)
435 g_Ctx.pDrvReg = AudioTestFindBackendOpt("valkit");
436
437 /*
438 * In self-test mode the guest and the host side have to share the same driver stack,
439 * as we don't have any device emulation between the two sides.
440 *
441 * This is necessary to actually get the played/recorded audio to from/to the guest
442 * and host respectively.
443 *
444 * Choosing any other backend than the Validation Kit above *will* break this self-test!
445 */
446 rc = audioTestDriverStackInitEx(&g_Ctx.DrvStack, g_Ctx.pDrvReg,
447 true /* fEnabledIn */, true /* fEnabledOut */, g_Ctx.Host.TstEnv.IoOpts.fWithDrvAudio);
448 if (RT_FAILURE(rc))
449 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to init driver stack: %Rrc\n", rc);
450
451 /*
452 * Start testing.
453 */
454 RTTestBanner(g_hTest);
455
456 int rc2 = audioTestDoSelftest(&g_Ctx);
457 if (RT_FAILURE(rc2))
458 RTTestFailed(g_hTest, "Self test failed with rc=%Rrc", rc2);
459
460 audioTestDriverStackDelete(&g_Ctx.DrvStack);
461
462 /*
463 * Print summary and exit.
464 */
465 return RTTestSummaryAndDestroy(g_hTest);
466}
467
468/**
469 * Command table entry for 'selftest'.
470 */
471const VKATCMD g_CmdSelfTest =
472{
473 "selftest",
474 audioTestCmdSelftestHandler,
475 "Performs self-tests.",
476 s_aCmdSelftestOptions,
477 RT_ELEMENTS(s_aCmdSelftestOptions),
478 audioTestCmdSelftestHelp,
479 true /* fNeedsTransport */
480};
481
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