VirtualBox

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

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

Audio/Validation Kit: Log started process output to Validation Kit reporter. bugref:10008

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