VirtualBox

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

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

Audio/VKAT: More self-test fixes; docs. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.9 KB
Line 
1/* $Id: vkatCmdSelfTest.cpp 90769 2021-08-20 18:16:17Z 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. a) Creates an ATS instance to emulate the guest mode ("--mode guest")
11 * at port 6042 (ATS_TCP_DEF_BIND_PORT_GUEST).
12 * or
13 * b) Connect to an already existing guest ATS instance if "--guest-ats-address" is specified.
14 * This makes it more flexible in terms of testing / debugging.
15 * - 2. Uses the Validation Kit audio backend, which in turn creates an ATS instance
16 * listening at port 6062 (ATS_TCP_DEF_BIND_PORT_VALKIT).
17 * - 3. Uses the host test environment which creates an ATS instance
18 * listening at port 6052 (ATS_TCP_DEF_BIND_PORT_HOST).
19 * - 4. Executes a complete test run locally (e.g. without any guest (VM) involved).
20 */
21
22/*
23 * Copyright (C) 2021 Oracle Corporation
24 *
25 * This file is part of VirtualBox Open Source Edition (OSE), as
26 * available from http://www.virtualbox.org. This file is free software;
27 * you can redistribute it and/or modify it under the terms of the GNU
28 * General Public License (GPL) as published by the Free Software
29 * Foundation, in version 2 as it comes in the "COPYING" file of the
30 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
31 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
32 *
33 * The contents of this file may alternatively be used under the terms
34 * of the Common Development and Distribution License Version 1.0
35 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
36 * VirtualBox OSE distribution, in which case the provisions of the
37 * CDDL are applicable instead of those of the GPL.
38 *
39 * You may elect to license modified versions of this file under the
40 * terms and conditions of either the GPL or the CDDL or both.
41 */
42
43
44/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47
48#include <iprt/ctype.h>
49#include <iprt/errcore.h>
50#include <iprt/getopt.h>
51#include <iprt/message.h>
52#include <iprt/test.h>
53
54#include "Audio/AudioHlp.h"
55#include "Audio/AudioTest.h"
56#include "Audio/AudioTestService.h"
57#include "Audio/AudioTestServiceClient.h"
58
59#include "vkatInternal.h"
60
61
62/**
63 * Thread callback for mocking the guest (VM) side of things.
64 *
65 * @returns VBox status code.
66 * @param hThread Thread handle.
67 * @param pvUser Pointer to user-supplied data.
68 */
69static DECLCALLBACK(int) audioTestSelftestGuestAtsThread(RTTHREAD hThread, void *pvUser)
70{
71 RT_NOREF(hThread);
72 PSELFTESTCTX pCtx = (PSELFTESTCTX)pvUser;
73
74 PAUDIOTESTENV pTstEnv = &pCtx->Guest.TstEnv;
75
76 /* Flag the environment for self test mode. */
77 pTstEnv->fSelftest = true;
78
79 /* Tweak the address the guest ATS is trying to connect to the host if anything else is specified.
80 * Note: The host also runs on the same host (this self-test is completely self-contained and does not need a VM). */
81 if (!pTstEnv->u.Guest.TcpOpts.szTcpConnectAddr[0])
82 RTStrCopy(pTstEnv->u.Guest.TcpOpts.szTcpConnectAddr, sizeof(pTstEnv->u.Guest.TcpOpts.szTcpConnectAddr), "127.0.0.1");
83
84 int rc = AudioTestSvcCreate(&pTstEnv->u.Guest.Srv);
85 AssertRCReturn(rc, rc);
86
87 /* Generate tag for guest side. */
88 rc = RTStrCopy(pTstEnv->szTag, sizeof(pTstEnv->szTag), pCtx->szTag);
89 AssertRCReturn(rc, rc);
90
91 rc = AudioTestPathCreateTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), "selftest-guest");
92 AssertRCReturn(rc, rc);
93
94 rc = AudioTestPathCreateTemp(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), "selftest-out");
95 AssertRCReturn(rc, rc);
96
97 pTstEnv->enmMode = AUDIOTESTMODE_GUEST;
98
99 /** @todo Make this customizable. */
100 PDMAudioPropsInit(&pTstEnv->Props,
101 2 /* 16-bit */, true /* fSigned */, 2 /* cChannels */, 44100 /* uHz */);
102
103 rc = audioTestEnvInit(pTstEnv, &pCtx->DrvStack);
104 if (RT_SUCCESS(rc))
105 {
106 RTThreadUserSignal(hThread);
107
108 audioTestWorker(pTstEnv);
109 audioTestEnvDestroy(pTstEnv);
110 }
111
112 return rc;
113}
114
115/**
116 * Main function for performing the self test.
117 *
118 * @returns RTEXITCODE
119 * @param pCtx Self test context to use.
120 */
121RTEXITCODE audioTestDoSelftest(PSELFTESTCTX pCtx)
122{
123 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Running self test ...\n");
124
125 /* Generate a common tag for guest and host side. */
126 int rc = AudioTestGenTag(pCtx->szTag, sizeof(pCtx->szTag));
127 AssertRCReturn(rc, RTEXITCODE_FAILURE);
128
129 PAUDIOTESTENV pTstEnvHst = &pCtx->Host.TstEnv;
130
131 /* Flag the environment for self test mode. */
132 pTstEnvHst->fSelftest = true;
133
134 /* Generate tag for host side. */
135 rc = RTStrCopy(pTstEnvHst->szTag, sizeof(pTstEnvHst->szTag), pCtx->szTag);
136 AssertRCReturn(rc, RTEXITCODE_FAILURE);
137
138 rc = AudioTestPathCreateTemp(pTstEnvHst->szPathTemp, sizeof(pTstEnvHst->szPathTemp), "selftest-tmp");
139 AssertRCReturn(rc, RTEXITCODE_FAILURE);
140
141 rc = AudioTestPathCreateTemp(pTstEnvHst->szPathOut, sizeof(pTstEnvHst->szPathOut), "selftest-out");
142 AssertRCReturn(rc, RTEXITCODE_FAILURE);
143
144 /* Initialize the PCM properties to some sane values. */
145 PDMAudioPropsInit(&pTstEnvHst->Props,
146 2 /* 16-bit */, true /* fPcmSigned */, 2 /* cPcmChannels */, 44100 /* uPcmHz */);
147
148 /*
149 * Step 1.
150 */
151 RTTHREAD hThreadGstAts = NIL_RTTHREAD;
152
153 bool const fStartGuestAts = RTStrNLen(pCtx->Host.szGuestAtsAddr, sizeof(pCtx->Host.szGuestAtsAddr)) == 0;
154 if (fStartGuestAts)
155 {
156 /* Step 1b. */
157 rc = RTThreadCreate(&hThreadGstAts, audioTestSelftestGuestAtsThread, pCtx, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
158 "VKATGstAts");
159 if (RT_SUCCESS(rc))
160 rc = RTThreadUserWait(hThreadGstAts, RT_MS_30SEC);
161 }
162 /* else Step 1a later. */
163
164 RTThreadSleep(5000); /* Fudge: Wait until guest ATS is up. */
165
166 if (RT_SUCCESS(rc))
167 {
168 /*
169 * Steps 2 + 3.
170 */
171 pTstEnvHst->enmMode = AUDIOTESTMODE_HOST;
172
173 rc = audioTestEnvInit(pTstEnvHst, &pCtx->DrvStack);
174 if (RT_SUCCESS(rc))
175 {
176 /*
177 * Step 4.
178 */
179 rc = audioTestWorker(pTstEnvHst);
180 if (RT_SUCCESS(rc))
181 {
182
183 }
184
185 audioTestEnvDestroy(pTstEnvHst);
186 }
187 }
188
189 /*
190 * Shutting down.
191 */
192 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down self test\n");
193
194 ASMAtomicWriteBool(&g_fTerminate, true);
195
196 if (fStartGuestAts)
197 {
198 int rcThread;
199 int rc2 = RTThreadWait(hThreadGstAts, RT_MS_30SEC, &rcThread);
200 if (RT_SUCCESS(rc2))
201 rc2 = rcThread;
202 if (RT_FAILURE(rc2))
203 RTTestFailed(g_hTest, "Shutting down guest ATS failed with %Rrc\n", rc2);
204 if (RT_SUCCESS(rc))
205 rc = rc2;
206 }
207
208 if (RT_FAILURE(rc))
209 RTTestFailed(g_hTest, "Self test failed with %Rrc\n", rc);
210
211 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
212}
213
214
215/*********************************************************************************************************************************
216* Command: selftest *
217*********************************************************************************************************************************/
218
219/**
220 * Long option values for the 'selftest' command.
221 */
222enum
223{
224 VKAT_SELFTEST_OPT_GUEST_ATS_ADDR = 900,
225 VKAT_SELFTEST_OPT_GUEST_ATS_PORT,
226 VKAT_SELFTEST_OPT_HOST_ATS_ADDR,
227 VKAT_SELFTEST_OPT_HOST_ATS_PORT
228};
229
230/**
231 * Command line parameters for self-test mode.
232 */
233static const RTGETOPTDEF s_aCmdSelftestOptions[] =
234{
235 { "--guest-ats-addr", VKAT_SELFTEST_OPT_GUEST_ATS_ADDR, RTGETOPT_REQ_STRING },
236 { "--guest-ats-port", VKAT_SELFTEST_OPT_GUEST_ATS_PORT, RTGETOPT_REQ_UINT32 },
237 { "--host-ats-addr", VKAT_SELFTEST_OPT_HOST_ATS_ADDR, RTGETOPT_REQ_STRING },
238 { "--host-ats-port", VKAT_SELFTEST_OPT_HOST_ATS_PORT, RTGETOPT_REQ_UINT32 },
239 { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING },
240 { "--backend", 'b', RTGETOPT_REQ_STRING },
241 { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },
242 { "--exclude", 'e', RTGETOPT_REQ_UINT32 },
243 { "--include", 'i', RTGETOPT_REQ_UINT32 }
244};
245
246/** the 'selftest' command option help. */
247static DECLCALLBACK(const char *) audioTestCmdSelftestHelp(PCRTGETOPTDEF pOpt)
248{
249 switch (pOpt->iShort)
250 {
251 case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)";
252 case 'b': return "The audio backend to use";
253 case 'd': return "Go via DrvAudio instead of directly interfacing with the backend";
254 case 'e': return "Exclude the given test id from the list";
255 case 'i': return "Include the given test id in the list";
256 case VKAT_SELFTEST_OPT_GUEST_ATS_ADDR: return "Address of guest ATS to connect to\n"
257 " Default: 127.0.0.1; will start own guest ATS";
258 case VKAT_SELFTEST_OPT_GUEST_ATS_PORT: return "Port of guest ATS to connect to\n"
259 " Default: 6042"; /* ATS_TCP_DEF_CONNECT_PORT_GUEST */
260 case VKAT_SELFTEST_OPT_HOST_ATS_ADDR: return "Address of host ATS to connect to\n"
261 " Default: " ATS_TCP_DEF_CONNECT_HOST_ADDR_STR;
262 case VKAT_SELFTEST_OPT_HOST_ATS_PORT: return "Port of host ATS to connect to\n"
263 " Default: 6052"; /* ATS_TCP_DEF_BIND_PORT_VALKIT */
264 default: return NULL;
265 }
266}
267
268/**
269 * The 'selftest' command handler.
270 *
271 * @returns Program exit code.
272 * @param pGetState RTGetOpt state.
273 */
274DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)
275{
276 SELFTESTCTX Ctx;
277 RT_ZERO(Ctx);
278
279 /* Argument processing loop: */
280 int rc;
281 RTGETOPTUNION ValueUnion;
282 while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)
283 {
284 switch (rc)
285 {
286 case VKAT_SELFTEST_OPT_GUEST_ATS_ADDR:
287 rc = RTStrCopy(Ctx.Host.szGuestAtsAddr, sizeof(Ctx.Host.szGuestAtsAddr), ValueUnion.psz);
288 break;
289
290 case VKAT_SELFTEST_OPT_GUEST_ATS_PORT:
291 Ctx.Host.uGuestAtsPort = ValueUnion.u32;
292 break;
293
294 case VKAT_SELFTEST_OPT_HOST_ATS_ADDR:
295 rc = RTStrCopy(Ctx.Host.szValKitAtsAddr, sizeof(Ctx.Host.szValKitAtsAddr), ValueUnion.psz);
296 break;
297
298 case VKAT_SELFTEST_OPT_HOST_ATS_PORT:
299 Ctx.Host.uValKitAtsPort = ValueUnion.u32;
300 break;
301
302 case 'a':
303 for (unsigned i = 0; i < g_cTests; i++)
304 g_aTests[i].fExcluded = true;
305 break;
306
307 case 'b':
308 Ctx.pDrvReg = AudioTestFindBackendOpt(ValueUnion.psz);
309 if (Ctx.pDrvReg == NULL)
310 return RTEXITCODE_SYNTAX;
311 break;
312
313 case 'd':
314 Ctx.fWithDrvAudio = true;
315 break;
316
317 case 'e':
318 if (ValueUnion.u32 >= g_cTests)
319 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
320 g_aTests[ValueUnion.u32].fExcluded = true;
321 break;
322
323 case 'i':
324 if (ValueUnion.u32 >= g_cTests)
325 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
326 g_aTests[ValueUnion.u32].fExcluded = false;
327 break;
328
329 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
330
331 default:
332 return RTGetOptPrintError(rc, &ValueUnion);
333 }
334 }
335
336 /* Go with the Validation Kit audio backend if nothing else is specified. */
337 if (Ctx.pDrvReg == NULL)
338 Ctx.pDrvReg = AudioTestFindBackendOpt("valkit");
339
340 /*
341 * In self-test mode the guest and the host side have to share the same driver stack,
342 * as we don't have any device emulation between the two sides.
343 *
344 * This is necessary to actually get the played/recorded audio to from/to the guest
345 * and host respectively.
346 *
347 * Choosing any other backend than the Validation Kit above *will* break this self-test!
348 */
349 rc = audioTestDriverStackInitEx(&Ctx.DrvStack, Ctx.pDrvReg,
350 true /* fEnabledIn */, true /* fEnabledOut */, Ctx.fWithDrvAudio);
351 if (RT_FAILURE(rc))
352 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to init driver stack: %Rrc\n", rc);
353
354 /*
355 * Start testing.
356 */
357 RTTestBanner(g_hTest);
358
359 int rc2 = audioTestDoSelftest(&Ctx);
360 if (RT_FAILURE(rc2))
361 RTTestFailed(g_hTest, "Self test failed with rc=%Rrc", rc2);
362
363 audioTestDriverStackDelete(&Ctx.DrvStack);
364
365 /*
366 * Print summary and exit.
367 */
368 return RTTestSummaryAndDestroy(g_hTest);
369}
370
371/**
372 * Command table entry for 'selftest'.
373 */
374const VKATCMD g_CmdSelfTest =
375{
376 "selftest",
377 audioTestCmdSelftestHandler,
378 "Performs self-tests.",
379 s_aCmdSelftestOptions,
380 RT_ELEMENTS(s_aCmdSelftestOptions),
381 audioTestCmdSelftestHelp,
382 true /* fNeedsTransport */
383};
384
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