VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/audio/tdAudioTest.py@ 97865

Last change on this file since 97865 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.0 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: tdAudioTest.py 96407 2022-08-22 17:43:14Z vboxsync $
3
4"""
5AudioTest test driver which invokes the VKAT (Validation Kit Audio Test)
6binary to perform the actual audio tests.
7
8The generated test set archive on the guest will be downloaded by TXS
9to the host for later audio comparison / verification.
10"""
11
12__copyright__ = \
13"""
14Copyright (C) 2021-2022 Oracle and/or its affiliates.
15
16This file is part of VirtualBox base platform packages, as
17available from https://www.virtualbox.org.
18
19This program is free software; you can redistribute it and/or
20modify it under the terms of the GNU General Public License
21as published by the Free Software Foundation, in version 3 of the
22License.
23
24This program is distributed in the hope that it will be useful, but
25WITHOUT ANY WARRANTY; without even the implied warranty of
26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with this program; if not, see <https://www.gnu.org/licenses>.
31
32The contents of this file may alternatively be used under the terms
33of the Common Development and Distribution License Version 1.0
34(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
35in the VirtualBox distribution, in which case the provisions of the
36CDDL are applicable instead of those of the GPL.
37
38You may elect to license modified versions of this file under the
39terms and conditions of either the GPL or the CDDL or both.
40
41SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
42"""
43__version__ = "$Revision: 96407 $"
44
45# Standard Python imports.
46from datetime import datetime
47import os
48import sys
49import subprocess
50import time
51import threading
52
53# Only the main script needs to modify the path.
54try: __file__
55except: __file__ = sys.argv[0];
56g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
57sys.path.append(g_ksValidationKitDir);
58
59# Validation Kit imports.
60from testdriver import reporter
61from testdriver import base
62from testdriver import vbox
63from testdriver import vboxcon;
64from testdriver import vboxtestvms
65from common import utils;
66
67# pylint: disable=unnecessary-semicolon
68
69class tdAudioTest(vbox.TestDriver):
70 """
71 Runs various audio tests.
72 """
73 def __init__(self):
74 vbox.TestDriver.__init__(self);
75 self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
76 self.asGstVkatPaths = [
77 # Debugging stuff (SCP'd over to the guest).
78 '/tmp/vkat',
79 '/tmp/VBoxAudioTest',
80 'C:\\Temp\\vkat',
81 'C:\\Temp\\VBoxAudioTest',
82 # Validation Kit .ISO.
83 '${CDROM}/vboxvalidationkit/${OS/ARCH}/vkat${EXESUFF}',
84 '${CDROM}/${OS/ARCH}/vkat${EXESUFF}',
85 # Test VMs.
86 '/opt/apps/vkat',
87 '/opt/apps/VBoxAudioTest',
88 '/apps/vkat',
89 '/apps/VBoxAudioTest',
90 'C:\\Apps\\vkat${EXESUFF}',
91 'C:\\Apps\\VBoxAudioTest${EXESUFF}',
92 ## @todo VBoxAudioTest on Guest Additions?
93 ];
94 self.asTestsDef = [
95 'guest_tone_playback', 'guest_tone_recording'
96 ];
97 self.asTests = self.asTestsDef;
98
99 # Optional arguments passing to VKAT when doing the actual audio tests.
100 self.asVkatTestArgs = [];
101 # Optional arguments passing to VKAT when verifying audio test sets.
102 self.asVkatVerifyArgs = [];
103
104 # Exit code of last host process execution, shared between exeuction thread and main thread.
105 # This ASSUMES that we only have one thread running at a time. Rather hacky, but does the job for now.
106 self.iThreadHstProcRc = 0;
107
108 # Enable audio debug mode.
109 #
110 # This is needed in order to load and use the Validation Kit audio driver,
111 # which in turn is being used in conjunction with the guest side to record
112 # output (guest is playing back) and injecting input (guest is recording).
113 self.asOptExtraData = [
114 'VBoxInternal2/Audio/Debug/Enabled:true',
115 ];
116
117 # Name of the running VM to use for running the test driver. Optional, and None if not being used.
118 self.sRunningVmName = None;
119
120 # Audio controller type to use.
121 # If set to None, the OS' recommended controller type will be used (defined by Main).
122 self.sAudioControllerType = None;
123
124 def showUsage(self):
125 """
126 Shows the audio test driver-specific command line options.
127 """
128 fRc = vbox.TestDriver.showUsage(self);
129 reporter.log('');
130 reporter.log('tdAudioTest Options:');
131 reporter.log(' --runningvmname <vmname>');
132 reporter.log(' --audio-tests <s1[:s2[:]]>');
133 reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
134 reporter.log(' --audio-controller-type <HDA|AC97|SB16>');
135 reporter.log(' Default: recommended controller');
136 reporter.log(' --audio-test-count <number>');
137 reporter.log(' Default: 0 (means random)');
138 reporter.log(' --audio-test-tone-duration <ms>');
139 reporter.log(' Default: 0 (means random)');
140 reporter.log(' --audio-verify-max-diff-count <number>');
141 reporter.log(' Default: 0 (strict)');
142 reporter.log(' --audio-verify-max-diff-percent <0-100>');
143 reporter.log(' Default: 0 (strict)');
144 reporter.log(' --audio-verify-max-size-percent <0-100>');
145 reporter.log(' Default: 0 (strict)');
146 return fRc;
147
148 def parseOption(self, asArgs, iArg):
149 """
150 Parses the audio test driver-specific command line options.
151 """
152 if asArgs[iArg] == '--runningvmname':
153 iArg += 1;
154 if iArg >= len(asArgs):
155 raise base.InvalidOption('The "--runningvmname" needs VM name');
156
157 self.sRunningVmName = asArgs[iArg];
158 elif asArgs[iArg] == '--audio-tests':
159 iArg += 1;
160 if asArgs[iArg] == 'all': # Nice for debugging scripts.
161 self.asTests = self.asTestsDef;
162 else:
163 self.asTests = asArgs[iArg].split(':');
164 for s in self.asTests:
165 if s not in self.asTestsDef:
166 raise base.InvalidOption('The "--audio-tests" value "%s" is not valid; valid values are: %s'
167 % (s, ' '.join(self.asTestsDef)));
168 elif asArgs[iArg] == '--audio-controller-type':
169 iArg += 1;
170 if iArg >= len(asArgs):
171 raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
172 if asArgs[iArg] == 'HDA' \
173 or asArgs[iArg] == 'AC97' \
174 or asArgs[iArg] == 'SB16':
175 self.sAudioControllerType = asArgs[iArg];
176 else:
177 raise base.InvalidOption('The "--audio-controller-type" value "%s" is not valid' % (asArgs[iArg]));
178 elif asArgs[iArg] == '--audio-test-count' \
179 or asArgs[iArg] == '--audio-test-tone-duration':
180 # Strip the "--audio-test-" prefix and keep the options as defined in VKAT,
181 # e.g. "--audio-test-count" -> "--count". That way we don't
182 # need to do any special argument translation and whatnot.
183 self.asVkatTestArgs.extend(['--' + asArgs[iArg][len('--audio-test-'):]]);
184 iArg += 1;
185 if iArg >= len(asArgs):
186 raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
187 self.asVkatTestArgs.extend([asArgs[iArg]]);
188 elif asArgs[iArg] == '--audio-verify-max-diff-count' \
189 or asArgs[iArg] == '--audio-verify-max-diff-percent' \
190 or asArgs[iArg] == '--audio-verify-max-size-percent':
191 # Strip the "--audio-verify-" prefix and keep the options as defined in VKAT,
192 # e.g. "--audio-verify-max-diff-count" -> "--max-diff-count". That way we don't
193 # need to do any special argument translation and whatnot.
194 self.asVkatVerifyArgs.extend(['--' + asArgs[iArg][len('--audio-verify-'):]]);
195 iArg += 1;
196 if iArg >= len(asArgs):
197 raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
198 self.asVkatVerifyArgs.extend([asArgs[iArg]]);
199 else:
200 return vbox.TestDriver.parseOption(self, asArgs, iArg);
201 return iArg + 1;
202
203 def actionVerify(self):
204 """
205 Verifies the test driver before running.
206 """
207 if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
208 reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
209 'Please unzip a Validation Kit build in the current directory or in some parent one.'
210 % (self.sVBoxValidationKitIso,) );
211 return False;
212 return vbox.TestDriver.actionVerify(self);
213
214 def actionConfig(self):
215 """
216 Configures the test driver before running.
217 """
218 if not self.importVBoxApi(): # So we can use the constant below.
219 return False;
220
221 # Make sure that the Validation Kit .ISO is mounted
222 # to find the VKAT (Validation Kit Audio Test) binary on it.
223 assert self.sVBoxValidationKitIso is not None;
224 return self.oTestVmSet.actionConfig(self, sDvdImage = self.sVBoxValidationKitIso);
225
226 def actionExecute(self):
227 """
228 Executes the test driver.
229 """
230
231 # Disable maximum logging line restrictions per group.
232 # This comes in handy when running this test driver in a (very) verbose mode, e.g. for debugging.
233 os.environ['VBOX_LOG_MAX_PER_GROUP'] = '0';
234 os.environ['VBOX_RELEASE_LOG_MAX_PER_GROUP'] = '0';
235 os.environ['VKAT_RELEASE_LOG_MAX_PER_GROUP'] = '0';
236
237 if self.sRunningVmName is None:
238 return self.oTestVmSet.actionExecute(self, self.testOneVmConfig);
239 return self.actionExecuteOnRunnigVM();
240
241 def actionExecuteOnRunnigVM(self):
242 """
243 Executes the tests in an already configured + running VM.
244 """
245 if not self.importVBoxApi():
246 return False;
247
248 fRc = True;
249
250 oVM = None;
251 oVirtualBox = None;
252
253 oVirtualBox = self.oVBoxMgr.getVirtualBox();
254 try:
255 oVM = oVirtualBox.findMachine(self.sRunningVmName);
256 if oVM.state != self.oVBoxMgr.constants.MachineState_Running:
257 reporter.error("Machine '%s' is not in Running state (state is %d)" % (self.sRunningVmName, oVM.state));
258 fRc = False;
259 except:
260 reporter.errorXcpt("Machine '%s' not found" % (self.sRunningVmName));
261 fRc = False;
262
263 if fRc:
264 oSession = self.openSession(oVM);
265 if oSession:
266 # Tweak this to your likings.
267 oTestVm = vboxtestvms.TestVm('runningvm', sKind = 'WindowsXP'); #sKind = 'WindowsXP' # sKind = 'Ubuntu_64'
268 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 30 * 1000);
269 if fRc:
270 self.doTest(oTestVm, oSession, oTxsSession);
271 else:
272 reporter.error("Unable to open session for machine '%s'" % (self.sRunningVmName));
273 fRc = False;
274
275 if oVM:
276 del oVM;
277 if oVirtualBox:
278 del oVirtualBox;
279 return fRc;
280
281 def getGstVkatLogFilePath(self, oTestVm):
282 """
283 Returns the log file path of VKAT running on the guest (daemonized).
284 """
285 return oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'vkat-guest.log');
286
287 def locateGstBinary(self, oSession, oTxsSession, asPaths):
288 """
289 Locates a guest binary on the guest by checking the paths in \a asPaths.
290 """
291 for sCurPath in asPaths:
292 reporter.log2('Checking for \"%s\" ...' % (sCurPath));
293 if self.txsIsFile(oSession, oTxsSession, sCurPath, fIgnoreErrors = True):
294 return (True, sCurPath);
295 reporter.error('Unable to find guest binary in any of these places:\n%s' % ('\n'.join(asPaths),));
296 return (False, "");
297
298 def executeHstLoop(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
299 """
300 Inner loop which handles the execution of a host binary.
301
302 Might be called synchronously in main thread or via the thread exeuction helper (asynchronous).
303 """
304 fRc = False;
305
306 asEnvTmp = os.environ.copy();
307 if asEnv:
308 for sEnv in asEnv:
309 sKey, sValue = sEnv.split('=');
310 reporter.log2('Setting env var \"%s\" -> \"%s\"' % (sKey, sValue));
311 os.environ[sKey] = sValue; # Also apply it to the current environment.
312 asEnvTmp[sKey] = sValue;
313
314 try:
315 # Spawn process.
316 if fAsAdmin \
317 and utils.getHostOs() != 'win':
318 oProcess = utils.sudoProcessStart(asArgs, env = asEnvTmp, stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
319 else:
320 oProcess = utils.processStart(asArgs, env = asEnvTmp, stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
321
322 if not oProcess:
323 reporter.error('Starting process for "%s" failed!' % (sWhat));
324 return False;
325
326 iPid = oProcess.pid;
327 self.pidFileAdd(iPid, sWhat);
328
329 iRc = 0;
330
331 # For Python 3.x we provide "real-time" output.
332 if sys.version_info[0] >= 3:
333 while oProcess.stdout.readable(): # pylint: disable=no-member
334 sStdOut = oProcess.stdout.readline();
335 if sStdOut:
336 sStdOut = sStdOut.strip();
337 reporter.log('%s: %s' % (sWhat, sStdOut));
338 iRc = oProcess.poll();
339 if iRc is not None:
340 break;
341 else:
342 # For Python 2.x it's too much hassle to set the file descriptor options (O_NONBLOCK) and stuff,
343 # so just use communicate() here and dump everythiong all at once when finished.
344 sStdOut = oProcess.communicate();
345 if sStdOut:
346 reporter.log('%s: %s' % (sWhat, sStdOut));
347 iRc = oProcess.poll();
348
349 if iRc == 0:
350 reporter.log('*** %s: exit code %d' % (sWhat, iRc));
351 fRc = True;
352 else:
353 reporter.log('!*! %s: exit code %d' % (sWhat, iRc));
354
355 self.pidFileRemove(iPid);
356
357 # Save thread result code.
358 self.iThreadHstProcRc = iRc;
359
360 except:
361 reporter.logXcpt('Executing "%s" failed!' % (sWhat));
362
363 return fRc;
364
365 def executeHstThread(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
366 """
367 Thread execution helper to run a process on the host.
368 """
369 fRc = self.executeHstLoop(sWhat, asArgs, asEnv, fAsAdmin);
370 if fRc:
371 reporter.log('Executing \"%s\" on host done' % (sWhat,));
372 else:
373 reporter.log('Executing \"%s\" on host failed' % (sWhat,));
374
375 def executeHst(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
376 """
377 Runs a binary (image) with optional admin (root) rights on the host and
378 waits until it terminates.
379
380 Windows currently is not supported yet running stuff as Administrator.
381
382 Returns success status (exit code is 0).
383 """
384 reporter.log('Executing \"%s\" on host (as admin = %s)' % (sWhat, fAsAdmin));
385
386 try: sys.stdout.flush();
387 except: pass;
388 try: sys.stderr.flush();
389 except: pass;
390
391 # Initialize thread rc.
392 self.iThreadHstProcRc = -42;
393
394 try:
395 oThread = threading.Thread(target = self.executeHstThread, args = [ sWhat, asArgs, asEnv, fAsAdmin ]);
396 oThread.start();
397 while oThread.join(0.1):
398 if not oThread.is_alive():
399 break;
400 self.processEvents(0);
401 reporter.log2('Thread returned exit code for "%s": %d' % (sWhat, self.iThreadHstProcRc));
402 except:
403 reporter.logXcpt('Starting thread for "%s" failed' % (sWhat,));
404
405 return self.iThreadHstProcRc == 0;
406
407 def getWinFirewallArgsDisable(self, sOsType):
408 """
409 Returns the command line arguments for Windows OSes
410 to disable the built-in firewall (if any).
411
412 If not supported, returns an empty array.
413 """
414 if sOsType == 'vista': # pylint: disable=no-else-return
415 # Vista and up.
416 return (['netsh.exe', 'advfirewall', 'set', 'allprofiles', 'state', 'off']);
417 elif sOsType == 'xp': # Older stuff (XP / 2003).
418 return(['netsh.exe', 'firewall', 'set', 'opmode', 'mode=DISABLE']);
419 # Not supported / available.
420 return [];
421
422 def disableGstFirewall(self, oTestVm, oTxsSession):
423 """
424 Disables the firewall on a guest (if any).
425
426 Needs elevated / admin / root privileges.
427
428 Returns success status, not logged.
429 """
430 fRc = False;
431
432 asArgs = [];
433 sOsType = '';
434 if oTestVm.isWindows():
435 if oTestVm.sKind in ['WindowsNT4', 'WindowsNT3x']:
436 sOsType = 'nt3x'; # Not supported, but define it anyway.
437 elif oTestVm.sKind in ('Windows2000', 'WindowsXP', 'Windows2003'):
438 sOsType = 'xp';
439 else:
440 sOsType = 'vista';
441 asArgs = self.getWinFirewallArgsDisable(sOsType);
442 else:
443 sOsType = 'unsupported';
444
445 reporter.log('Disabling firewall on guest (type: %s) ...' % (sOsType,));
446
447 if asArgs:
448 fRc = self.txsRunTest(oTxsSession, 'Disabling guest firewall', 3 * 60 * 1000, \
449 oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), asArgs[0]), asArgs);
450 if not fRc:
451 reporter.error('Disabling firewall on guest returned exit code error %d' % (self.getLastRcFromTxs(oTxsSession)));
452 else:
453 reporter.log('Firewall not available on guest, skipping');
454 fRc = True; # Not available, just skip.
455
456 return fRc;
457
458 def disableHstFirewall(self):
459 """
460 Disables the firewall on the host (if any).
461
462 Needs elevated / admin / root privileges.
463
464 Returns success status, not logged.
465 """
466 fRc = False;
467
468 asArgs = [];
469 sOsType = sys.platform;
470
471 if sOsType == 'win32':
472 reporter.log('Disabling firewall on host (type: %s) ...' % (sOsType));
473
474 ## @todo For now we ASSUME that we don't run (and don't support even) on old(er)
475 # Windows hosts than Vista.
476 asArgs = self.getWinFirewallArgsDisable('vista');
477 if asArgs:
478 fRc = self.executeHst('Disabling host firewall', asArgs, fAsAdmin = True);
479 else:
480 reporter.log('Firewall not available on host, skipping');
481 fRc = True; # Not available, just skip.
482
483 return fRc;
484
485 def getLastRcFromTxs(self, oTxsSession):
486 """
487 Extracts the last exit code reported by TXS from a run before.
488 Assumes that nothing else has been run on the same TXS session in the meantime.
489 """
490 iRc = 0;
491 (_, sOpcode, abPayload) = oTxsSession.getLastReply();
492 if sOpcode.startswith('PROC NOK '): # Extract process rc
493 iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
494 return iRc;
495
496 def startVkatOnGuest(self, oTestVm, oSession, oTxsSession, sTag):
497 """
498 Starts VKAT on the guest (running in background).
499 """
500 sPathTemp = self.getGuestTempDir(oTestVm);
501 sPathAudioOut = oTestVm.pathJoin(sPathTemp, 'vkat-guest-out');
502 sPathAudioTemp = oTestVm.pathJoin(sPathTemp, 'vkat-guest-temp');
503
504 reporter.log('Guest audio test temp path is \"%s\"' % (sPathAudioOut));
505 reporter.log('Guest audio test output path is \"%s\"' % (sPathAudioTemp));
506 reporter.log('Guest audio test tag is \"%s\"' % (sTag));
507
508 fRc, sVkatExe = self.locateGstBinary(oSession, oTxsSession, self.asGstVkatPaths);
509 if fRc:
510 reporter.log('Using VKAT on guest at \"%s\"' % (sVkatExe));
511
512 sCmd = '';
513 asArgs = [];
514
515 asArgsVkat = [ sVkatExe, 'test', '--mode', 'guest', '--probe-backends', \
516 '--tempdir', sPathAudioTemp, '--outdir', sPathAudioOut, \
517 '--tag', sTag ];
518
519 asArgs.extend(asArgsVkat);
520
521 for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
522 asArgs.extend([ '-v' ]);
523
524 # Needed for NATed VMs.
525 asArgs.extend(['--tcp-connect-addr', '10.0.2.2' ]);
526
527 if oTestVm.sKind in 'Oracle_64':
528 #
529 # Some Linux distros have a bug / are configured (?) so that processes started by init system
530 # cannot access the PulseAudio server ("Connection refused"), for example OL 8.1.
531 #
532 # To work around this, we use the (hopefully) configured user "vbox" and run it under its behalf,
533 # as the Test Execution Service (TxS) currently does not implement impersonation yet.
534 #
535 asSU = [ '/bin/su',
536 '/usr/bin/su',
537 '/usr/local/bin/su' ];
538 fRc, sCmd = self.locateGstBinary(oSession, oTxsSession, asSU);
539 if fRc:
540 sCmdArgs = '';
541 for sArg in asArgs:
542 sCmdArgs += sArg + " ";
543 asArgs = [ sCmd, oTestVm.getTestUser(), '-c', sCmdArgs ];
544 else:
545 reporter.log('Unable to find SU on guest, falling back to regular starting ...')
546
547 if not sCmd: # Just start it with the same privileges as TxS.
548 sCmd = sVkatExe;
549
550 reporter.log2('startVkatOnGuest: sCmd=%s' % (sCmd,));
551 reporter.log2('startVkatOnGuest: asArgs=%s' % (asArgs,));
552
553 #
554 # Add own environment stuff.
555 #
556 asEnv = [];
557
558 # Write the log file to some deterministic place so TxS can retrieve it later.
559 sVkatLogFile = 'VKAT_RELEASE_LOG_DEST=file=' + self.getGstVkatLogFilePath(oTestVm);
560 asEnv.extend([ sVkatLogFile ]);
561
562 #
563 # Execute asynchronously on the guest.
564 #
565 fRc = oTxsSession.asyncExec(sCmd, asArgs, asEnv, cMsTimeout = 15 * 60 * 1000, sPrefix = '[VKAT Guest] ');
566 if fRc:
567 self.addTask(oTxsSession);
568
569 if not fRc:
570 reporter.error('VKAT on guest returned exit code error %d' % (self.getLastRcFromTxs(oTxsSession)));
571 else:
572 reporter.error('VKAT on guest not found');
573
574 return fRc;
575
576 def runTests(self, oTestVm, oSession, oTxsSession, sDesc, sTag, asTests):
577 """
578 Runs one or more tests using VKAT on the host, which in turn will
579 communicate with VKAT running on the guest and the Validation Kit
580 audio driver ATS (Audio Testing Service).
581 """
582 _ = oTestVm, oSession, oTxsSession;
583
584 sPathTemp = self.sScratchPath;
585 sPathAudioOut = os.path.join(sPathTemp, 'vkat-host-out-%s' % (sTag));
586 sPathAudioTemp = os.path.join(sPathTemp, 'vkat-host-temp-%s' % (sTag));
587
588 reporter.log('Host audio test temp path is \"%s\"' % (sPathAudioOut));
589 reporter.log('Host audio test output path is \"%s\"' % (sPathAudioTemp));
590 reporter.log('Host audio test tag is \"%s\"' % (sTag));
591
592 reporter.testStart(sDesc);
593
594 sVkatExe = self.getBinTool('vkat');
595
596 reporter.log('Using VKAT on host at: \"%s\"' % (sVkatExe));
597
598 # Build the base command line, exclude all tests by default.
599 asArgs = [ sVkatExe, 'test', '--mode', 'host', '--probe-backends',
600 '--tempdir', sPathAudioTemp, '--outdir', sPathAudioOut, '-a',
601 '--tag', sTag,
602 '--no-audio-ok', # Enables running on hosts which do not have any audio hardware.
603 '--no-verify' ]; # We do the verification separately in the step below.
604
605 for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
606 asArgs.extend([ '-v' ]);
607
608 if self.asVkatTestArgs:
609 asArgs += self.asVkatTestArgs;
610
611 # ... and extend it with wanted tests.
612 asArgs.extend(asTests);
613
614 #
615 # Let VKAT on the host run synchronously.
616 #
617 fRc = self.executeHst("VKAT Host", asArgs);
618
619 reporter.testDone();
620
621 if fRc:
622 #
623 # When running the test(s) above were successful, do the verification step next.
624 # This gives us a bit more fine-grained test results in the test manager.
625 #
626 reporter.testStart('Verifying audio data');
627
628 sNameSetHst = '%s-host.tar.gz' % (sTag);
629 sPathSetHst = os.path.join(sPathAudioOut, sNameSetHst);
630 sNameSetGst = '%s-guest.tar.gz' % (sTag);
631 sPathSetGst = os.path.join(sPathAudioOut, sNameSetGst);
632
633 asArgs = [ sVkatExe, 'verify', sPathSetHst, sPathSetGst ];
634
635 for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
636 asArgs.extend([ '-v' ]);
637
638 if self.asVkatVerifyArgs:
639 asArgs += self.asVkatVerifyArgs;
640
641 fRc = self.executeHst("VKAT Host Verify", asArgs);
642 if fRc:
643 reporter.log("Verification audio data successful");
644 else:
645 #
646 # Add the test sets to the test manager for later (manual) diagnosis.
647 #
648 reporter.addLogFile(sPathSetGst, 'misc/other', 'Guest audio test set');
649 reporter.addLogFile(sPathSetHst, 'misc/other', 'Host audio test set');
650
651 reporter.error("Verification of audio data failed");
652
653 reporter.testDone();
654
655 return fRc;
656
657 def doTest(self, oTestVm, oSession, oTxsSession):
658 """
659 Executes the specified audio tests.
660 """
661
662 # Disable any OS-specific firewalls preventing VKAT / ATS to run.
663 fRc = self.disableHstFirewall();
664 fRc = self.disableGstFirewall(oTestVm, oTxsSession) and fRc;
665
666 if not fRc:
667 return False;
668
669 reporter.log("Active tests: %s" % (self.asTests,));
670
671 # Define a tag for the whole run.
672 sTag = oTestVm.sVmName + "_" + datetime.now().strftime("%Y%m%d_%H%M%S");
673
674 fRc = self.startVkatOnGuest(oTestVm, oSession, oTxsSession, sTag);
675 if fRc:
676 #
677 # Execute the tests using VKAT on the guest side (in guest mode).
678 #
679 if "guest_tone_playback" in self.asTests:
680 fRc = self.runTests(oTestVm, oSession, oTxsSession, \
681 'Guest audio playback', sTag + "_test_playback", \
682 asTests = [ '-i0' ]);
683 if "guest_tone_recording" in self.asTests:
684 fRc = fRc and self.runTests(oTestVm, oSession, oTxsSession, \
685 'Guest audio recording', sTag + "_test_recording", \
686 asTests = [ '-i1' ]);
687
688 # Cancel guest VKAT execution task summoned by startVkatOnGuest().
689 oTxsSession.cancelTask();
690
691 #
692 # Retrieve log files for diagnosis.
693 #
694 self.txsDownloadFiles(oSession, oTxsSession,
695 [ ( self.getGstVkatLogFilePath(oTestVm),
696 'vkat-guest-%s.log' % (oTestVm.sVmName,),),
697 ],
698 fIgnoreErrors = True);
699
700 # A bit of diagnosis on error.
701 ## @todo Remove this later when stuff runs stable.
702 if not fRc:
703 reporter.log('Kernel messages:');
704 sCmdDmesg = oTestVm.pathJoin(self.getGuestSystemDir(oTestVm), 'dmesg');
705 oTxsSession.syncExec(sCmdDmesg, (sCmdDmesg), fIgnoreErrors = True);
706 reporter.log('Loaded kernel modules:');
707 sCmdLsMod = oTestVm.pathJoin(self.getGuestSystemAdminDir(oTestVm), 'lsmod');
708 oTxsSession.syncExec(sCmdLsMod, (sCmdLsMod), fIgnoreErrors = True);
709
710 return fRc;
711
712 def testOneVmConfig(self, oVM, oTestVm):
713 """
714 Runs tests using one specific VM config.
715 """
716
717 self.logVmInfo(oVM);
718
719 reporter.testStart("Audio Testing");
720
721 fSkip = False;
722
723 if oTestVm.isWindows() \
724 and oTestVm.sKind in ('WindowsNT4', 'Windows2000'): # Too old for DirectSound and WASAPI backends.
725 reporter.log('Audio testing skipped, not implemented/available for that OS yet.');
726 fSkip = True;
727
728 if not fSkip \
729 and self.fpApiVer < 7.0:
730 reporter.log('Audio testing for non-trunk builds skipped.');
731 fSkip = True;
732
733 if not fSkip:
734 sVkatExe = self.getBinTool('vkat');
735 asArgs = [ sVkatExe, 'enum', '--probe-backends' ];
736 for _ in range(1, reporter.getVerbosity()): # Verbosity always is initialized at 1.
737 asArgs.extend([ '-v' ]);
738 fRc = self.executeHst("VKAT Host Audio Probing", asArgs);
739 if not fRc:
740 # Not fatal, as VBox then should fall back to the NULL audio backend (also worth having as a test case).
741 reporter.log('Warning: Backend probing on host failed, no audio available (pure server installation?)');
742
743 if fSkip:
744 reporter.testDone(fSkipped = True);
745 return True;
746
747 # Reconfigure the VM.
748 oSession = self.openSession(oVM);
749 if oSession is not None:
750
751 cVerbosity = reporter.getVerbosity();
752 if cVerbosity >= 2: # Explicitly set verbosity via extra-data when >= level 2.
753 self.asOptExtraData.extend([ 'VBoxInternal2/Audio/Debug/Level:' + str(cVerbosity) ]);
754
755 # Set extra data.
756 for sExtraData in self.asOptExtraData:
757 sKey, sValue = sExtraData.split(':');
758 reporter.log('Set extradata: %s => %s' % (sKey, sValue));
759 fRc = oSession.setExtraData(sKey, sValue) and fRc;
760
761 # Make sure that the VM's audio adapter is configured the way we need it to.
762 if self.fpApiVer >= 4.0:
763 enmAudioControllerType = None;
764 reporter.log('Configuring audio controller type ...');
765 if self.sAudioControllerType is None:
766 oOsType = oSession.getOsType();
767 enmAudioControllerType = oOsType.recommendedAudioController;
768 else:
769 if self.sAudioControllerType == 'HDA':
770 enmAudioControllerType = vboxcon.AudioControllerType_HDA;
771 elif self.sAudioControllerType == 'AC97':
772 enmAudioControllerType = vboxcon.AudioControllerType_AC97;
773 elif self.sAudioControllerType == 'SB16':
774 enmAudioControllerType = vboxcon.AudioControllerType_SB16;
775 assert enmAudioControllerType is not None;
776
777 # For now we're encforcing to test the HDA emulation only, regardless of
778 # what the recommended audio controller type from above was.
779 ## @todo Make other emulations work as well.
780 fEncforceHDA = True;
781
782 if fEncforceHDA:
783 enmAudioControllerType = vboxcon.AudioControllerType_HDA;
784 reporter.log('Enforcing audio controller type to HDA');
785
786 reporter.log('Setting user-defined audio controller type to %d' % (enmAudioControllerType));
787 oSession.setupAudio(enmAudioControllerType,
788 fEnable = True, fEnableIn = True, fEnableOut = True);
789
790 # Save the settings.
791 fRc = fRc and oSession.saveSettings();
792 fRc = oSession.close() and fRc;
793
794 reporter.testStart('Waiting for TXS');
795 oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName,
796 fCdWait = True,
797 cMsTimeout = 3 * 60 * 1000,
798 sFileCdWait = '${OS/ARCH}/vkat${EXESUFF}');
799 reporter.testDone();
800
801 reporter.log('Waiting for any OS startup sounds getting played (to skip those) ...');
802 time.sleep(5);
803
804 if oSession is not None:
805 self.addTask(oTxsSession);
806
807 fRc = self.doTest(oTestVm, oSession, oTxsSession);
808
809 # Cleanup.
810 self.removeTask(oTxsSession);
811 self.terminateVmBySession(oSession);
812
813 reporter.testDone();
814 return fRc;
815
816 def onExit(self, iRc):
817 """
818 Exit handler for this test driver.
819 """
820 return vbox.TestDriver.onExit(self, iRc);
821
822if __name__ == '__main__':
823 sys.exit(tdAudioTest().main(sys.argv))
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