VirtualBox

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

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

scm --update-copyright-year

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