VirtualBox

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

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

Audio/ValKit: Trying to fix the VM config action in tdAudioTest.py() for running on the test boxes. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: tdAudioTest.py 90221 2021-07-16 05:46:58Z 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: 90221 $"
34
35# Standard Python imports.
36import os
37import sys
38import subprocess
39import uuid
40
41# Only the main script needs to modify the path.
42try: __file__
43except: __file__ = sys.argv[0];
44g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
45sys.path.append(g_ksValidationKitDir);
46
47# Validation Kit imports.
48from testdriver import reporter
49from testdriver import base
50from testdriver import vbox
51from testdriver import vboxtestvms
52
53# pylint: disable=unnecessary-semicolon
54
55class tdAudioTest(vbox.TestDriver):
56 """
57 Runs various audio tests.
58 """
59 def __init__(self):
60 vbox.TestDriver.__init__(self);
61 self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
62 self.asGstVkatPaths = [
63 # Debugging stuff (SCP'd over to the guest).
64 '/tmp/vkat',
65 '/tmp/VBoxAudioTest',
66 # Validation Kit .ISO.
67 '${CDROM}/vboxvalidationkit/${OS/ARCH}/vkat${EXESUFF}',
68 '${CDROM}/${OS/ARCH}/vkat${EXESUFF}',
69 ## @odo VBoxAudioTest on Guest Additions?
70 ];
71 self.asTestsDef = [
72 'guest_tone_playback', 'guest_tone_recording'
73 ];
74 self.asTests = self.asTestsDef;
75
76 # Enable audio debug mode.
77 #
78 # This is needed in order to load and use the Validation Kit audio driver,
79 # which in turn is being used in conjunction with the guest side to record
80 # output (guest is playing back) and injecting input (guest is recording).
81 self.asOptExtraData = [
82 'VBoxInternal2/Audio/Debug/Enabled:true',
83 ];
84
85 # Name of the running VM to use for running the test driver. Optional, and None if not being used.
86 self.sRunningVmName = None;
87
88 def showUsage(self):
89 """
90 Shows the audio test driver-specific command line options.
91 """
92 fRc = vbox.TestDriver.showUsage(self);
93 reporter.log('');
94 reporter.log('tdAudioTest Options:');
95 reporter.log(' --runningvmname <vmname>');
96 reporter.log(' --audio-tests <s1[:s2[:]]>');
97 reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
98 return fRc;
99
100 def parseOption(self, asArgs, iArg):
101 """
102 Parses the audio test driver-specific command line options.
103 """
104 if asArgs[iArg] == '--runningvmname':
105 iArg += 1;
106 if iArg >= len(asArgs):
107 raise base.InvalidOption('The "--runningvmname" needs VM name');
108
109 self.sRunningVmName = asArgs[iArg];
110 elif asArgs[iArg] == '--audio-tests':
111 iArg += 1;
112 if asArgs[iArg] == 'all': # Nice for debugging scripts.
113 self.asTests = self.asTestsDef;
114 else:
115 self.asTests = asArgs[iArg].split(':');
116 for s in self.asTests:
117 if s not in self.asTestsDef:
118 raise base.InvalidOption('The "--audio-tests" value "%s" is not valid; valid values are: %s'
119 % (s, ' '.join(self.asTestsDef)));
120 else:
121 return vbox.TestDriver.parseOption(self, asArgs, iArg);
122 return iArg + 1;
123
124 def actionConfig(self):
125 """
126 Configures the test driver before running.
127 """
128 if not self.importVBoxApi(): # So we can use the constant below.
129 return False;
130 return self.oTestVmSet.actionConfig(self);
131
132 def actionExecute(self):
133 """
134 Executes the test driver.
135 """
136 if self.sRunningVmName is None:
137 return self.oTestVmSet.actionExecute(self, self.testOneVmConfig);
138 return self.actionExecuteOnRunnigVM();
139
140 def actionExecuteOnRunnigVM(self):
141 """
142 Executes the tests in an already configured + running VM.
143 """
144 if not self.importVBoxApi():
145 return False;
146
147 fRc = True;
148
149 oVirtualBox = self.oVBoxMgr.getVirtualBox();
150 try:
151 oVM = oVirtualBox.findMachine(self.sRunningVmName);
152 if oVM.state != self.oVBoxMgr.constants.MachineState_Running:
153 reporter.error("Machine '%s' is not in Running state" % (self.sRunningVmName));
154 fRc = False;
155 except:
156 reporter.errorXcpt("Machine '%s' not found" % (self.sRunningVmName));
157 fRc = False;
158
159 if fRc:
160 oSession = self.openSession(oVM);
161 if oSession:
162 # Tweak this to your likings.
163 oTestVm = vboxtestvms.TestVm('runningvm', sKind = 'Ubuntu_64');
164 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 30 * 1000);
165 if fRc:
166 self.doTest(oTestVm, oSession, oTxsSession);
167 else:
168 reporter.error("Unable to open session for machine '%s'" % (self.sRunningVmName));
169 fRc = False;
170
171 del oVM;
172 del oVirtualBox;
173 return fRc;
174
175 def locateGstVkat(self, oSession, oTxsSession):
176 """
177 Returns guest side path to VKAT.
178 """
179 for sVkatPath in self.asGstVkatPaths:
180 reporter.log2('Checking for VKAT at: %s ...' % (sVkatPath));
181 if self.txsIsFile(oSession, oTxsSession, sVkatPath):
182 return (True, sVkatPath);
183 reporter.error('Unable to find guest VKAT in any of these places:\n%s' % ('\n'.join(self.asGstVkatPaths),));
184 return (False, "");
185
186 def killProcessByName(self, sProcName):
187 """
188 Kills processes by their name.
189 """
190 if sys.platform == 'win32':
191 os.system('taskkill /IM "%s.exe" /F' % (sProcName));
192 else:
193 os.system('killall -9 %s' % (sProcName));
194
195 def killHstVkat(self):
196 """
197 Kills VKAT (VBoxAudioTest) on the host side.
198 """
199 self.killProcessByName("vkat");
200 self.killProcessByName("VBoxAudioTest");
201
202 def getLastRcFromTxs(self, oTxsSession):
203 """
204 Extracts the last exit code reported by TXS from a run before.
205 Assumes that nothing else has been run on the same TXS session in the meantime.
206 """
207 iRc = 0;
208 (_, sOpcode, abPayload) = oTxsSession.getLastReply();
209 if sOpcode.startswith('PROC NOK '): # Extract process rc
210 iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
211 return iRc;
212
213 def startVkatOnGuest(self, oTestVm, oSession, oTxsSession):
214 """
215 Starts VKAT on the guest (running in background).
216 """
217 sPathTemp = self.getGuestTempDir(oTestVm);
218 sPathAudioOut = oTestVm.pathJoin(sPathTemp, 'vkat-guest-out');
219 sPathAudioTemp = oTestVm.pathJoin(sPathTemp, 'vkat-guest-temp');
220
221 reporter.log('Guest audio test temp path is \"%s\"' % (sPathAudioOut));
222 reporter.log('Guest audio test output path is \"%s\"' % (sPathAudioTemp));
223
224 fRc, sVkatExe = self.locateGstVkat(oSession, oTxsSession);
225 if fRc:
226 reporter.log('Using VKAT on guest at \"%s\"' % (sVkatExe));
227
228 aArgs = [ sVkatExe, 'test', '-vvvv', '--mode', 'guest', \
229 '--tempdir', sPathAudioTemp, '--outdir', sPathAudioOut ];
230 #
231 # Start VKAT in the background (daemonized) on the guest side, so that we
232 # can continue starting VKAT on the host.
233 #
234 aArgs.extend(['--daemonize']);
235
236 fRc = self.txsRunTest(oTxsSession, 'Starting VKAT on guest', 15 * 60 * 1000,
237 sVkatExe, aArgs);
238 if not fRc:
239 reporter.error('VKAT on guest returned exit code error %d' % (self.getLastRcFromTxs(oTxsSession)));
240 else:
241 reporter.error('VKAT on guest not found');
242
243 return fRc;
244
245 def runTests(self, oTestVm, oSession, oTxsSession, sDesc, sTests):
246 """
247 Runs one or more tests using VKAT on the host, which in turn will
248 communicate with VKAT running on the guest and the Validation Kit
249 audio driver ATS (Audio Testing Service).
250 """
251 _ = oSession, oTxsSession;
252 sTag = uuid.uuid4();
253
254 sPathTemp = self.sScratchPath;
255 sPathAudioOut = oTestVm.pathJoin(sPathTemp, 'vkat-host-out-%s' % (sTag));
256 sPathAudioTemp = oTestVm.pathJoin(sPathTemp, 'vkat-host-temp-%s' % (sTag));
257
258 reporter.log('Host audio test temp path is \"%s\"' % (sPathAudioOut));
259 reporter.log('Host audio test output path is \"%s\"' % (sPathAudioTemp));
260 reporter.log('Host audio test tag is \"%s\"' % (sTag));
261
262 sVkatExe = self.getBinTool('vkat');
263
264 reporter.log('Using VKAT on host at: \"%s\"' % (sVkatExe));
265
266 # Build the base command line, exclude all tests by default.
267 sArgs = '%s test -vvvv --mode host --tempdir %s --outdir %s -a' \
268 % (sVkatExe, sPathAudioTemp, sPathAudioOut);
269
270 # ... and extend it with wanted tests.
271 sArgs += " " + sTests;
272
273 fRc = True;
274
275 reporter.testStart(sDesc);
276
277 #
278 # Let VKAT on the host run synchronously.
279 #
280 procVkat = subprocess.Popen(sArgs, \
281 stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True);
282 if procVkat:
283 reporter.log('VKAT on host started');
284
285 out, err = procVkat.communicate();
286 rc = procVkat.poll();
287
288 out = out.decode(sys.stdin.encoding);
289 for line in out.split('\n'):
290 reporter.log(line);
291
292 err = err.decode(sys.stdin.encoding);
293 for line in err.split('\n'):
294 reporter.log(line);
295
296 reporter.log('VKAT on host ended with exit code %d' % rc);
297 if rc != 0:
298 reporter.testFailure('VKAT on the host failed');
299 fRc = False;
300 else:
301 reporter.testFailure('VKAT on the host failed to start');
302 fRc = False;
303
304 reporter.testDone();
305
306 return fRc;
307
308 def doTest(self, oTestVm, oSession, oTxsSession):
309 """
310 Executes the specified audio tests.
311 """
312
313 # First try to kill any old VKAT / VBoxAudioTest processes lurking around on the host.
314 # Might happen because of former (aborted) runs.
315 self.killHstVkat();
316
317 reporter.log("Active tests: %s" % (self.asTests,));
318
319 fRc = self.startVkatOnGuest(oTestVm, oSession, oTxsSession);
320 if fRc:
321 #
322 # Execute the tests using VKAT on the guest side (in guest mode).
323 #
324 if "guest_tone_playback" in self.asTests:
325 fRc = self.runTests(oTestVm, oSession, oTxsSession, 'Guest audio playback', '-i0');
326 if "guest_tone_recording" in self.asTests:
327 fRc = fRc and self.runTests(oTestVm, oSession, oTxsSession, 'Guest audio recording', '-i1');
328
329 return fRc;
330
331 def testOneVmConfig(self, oVM, oTestVm):
332 """
333 Runs tests using one specific VM config.
334 """
335 fRc = False;
336
337 self.logVmInfo(oVM);
338
339 fSkip = False;
340 if oTestVm.isWindows() \
341 and oTestVm.sKind in ('WindowsNT4', 'Windows2000'): # Too old for DirectSound and WASAPI backends.
342 fSkip = True;
343
344 if not fSkip:
345 reporter.testStart('Waiting for TXS');
346 oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName,
347 fCdWait = True,
348 cMsTimeout = 3 * 60 * 1000,
349 sFileCdWait = '${CDROM}/${OS/ARCH}/vkat${EXESUFF}');
350 reporter.testDone();
351 if oSession is not None \
352 and oTxsSession is not None:
353 self.addTask(oTxsSession);
354
355 fRc = self.doTest(oTestVm, oSession, oTxsSession);
356
357 if oSession is not None:
358 self.removeTask(oTxsSession);
359 self.terminateVmBySession(oSession);
360
361 return fRc;
362
363 reporter.log('Audio testing skipped, not implemented/available for that OS yet.');
364 return True;
365
366 #
367 # Test execution helpers.
368 #
369 def testOneCfg(self, oVM, oTestVm):
370 """
371 Runs the specified VM thru the tests.
372
373 Returns a success indicator on the general test execution. This is not
374 the actual test result.
375 """
376
377 self.logVmInfo(oVM);
378
379 fRc = True;
380
381 # Reconfigure the VM.
382 oSession = self.openSession(oVM);
383 if oSession is not None:
384 # Set extra data.
385 for sExtraData in self.asOptExtraData:
386 sKey, sValue = sExtraData.split(':');
387 reporter.log('Set extradata: %s => %s' % (sKey, sValue))
388 fRc = oSession.setExtraData(sKey, sValue) and fRc;
389
390 # Save the settings.
391 fRc = fRc and oSession.saveSettings()
392 fRc = oSession.close() and fRc;
393
394 if fRc:
395 oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False);
396 reporter.log("TxsSession: %s" % (oTxsSession,));
397 if oSession is not None:
398 self.addTask(oTxsSession);
399
400 fRc, oTxsSession = self.aoSubTstDrvs[0].testIt(oTestVm, oSession, oTxsSession);
401
402 # Cleanup.
403 self.removeTask(oTxsSession);
404 if not self.aoSubTstDrvs[0].oDebug.fNoExit:
405 self.terminateVmBySession(oSession);
406 else:
407 fRc = False;
408
409 return fRc;
410
411 def onExit(self, iRc):
412 """
413 Exit handler for this test driver.
414 """
415 return vbox.TestDriver.onExit(self, iRc);
416
417if __name__ == '__main__':
418 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