VirtualBox

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

最後變更 在這個檔案從94706是 94250,由 vboxsync 提交於 3 年 前

Audio/Validation Kit: More code to handle host process execution in a separate thread to not block required event processing [build fix]. bugref:10008

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette