VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py@ 93904

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

Validation Kit: Extended unit test test driver so that it also allows running directly off a remote location (i.e. Validation Kit .ISO, if included). Useful for testing cross arch unit tests (i.e. host 64-bit, guest 32-bit) See command line help / comments for more [pylint]. bugref:10195

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 48.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdUnitTest1.py 93904 2022-02-24 08:18:36Z vboxsync $
4
5"""
6VirtualBox Validation Kit - Unit Tests.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2022 Oracle Corporation
12
13This file is part of VirtualBox Open Source Edition (OSE), as
14available from http://www.virtualbox.org. This file is free software;
15you can redistribute it and/or modify it under the terms of the GNU
16General Public License (GPL) as published by the Free Software
17Foundation, in version 2 as it comes in the "COPYING" file of the
18VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20
21The contents of this file may alternatively be used under the terms
22of the Common Development and Distribution License Version 1.0
23(CDDL) only, as it comes in the "COPYING.CDDL" file of the
24VirtualBox OSE distribution, in which case the provisions of the
25CDDL are applicable instead of those of the GPL.
26
27You may elect to license modified versions of this file under the
28terms and conditions of either the GPL or the CDDL or both.
29"""
30__version__ = "$Revision: 93904 $"
31
32
33# Standard Python imports.
34import os
35import sys
36import re
37
38
39# Only the main script needs to modify the path.
40try: __file__
41except: __file__ = sys.argv[0];
42g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
43sys.path.append(g_ksValidationKitDir)
44
45# Validation Kit imports.
46from common import utils;
47from testdriver import base;
48from testdriver import reporter;
49from testdriver import vbox;
50from testdriver import vboxcon;
51
52
53class tdUnitTest1(vbox.TestDriver):
54 """
55 Unit Tests.
56 """
57
58 ## The temporary exclude list.
59 ## @note This shall be empty before we release 4.3!
60 kdTestCasesBuggyPerOs = {
61 'darwin': {
62 'testcase/tstX86-1': '', # 'FSTP M32R, ST0' fails; no idea why.
63 },
64 'linux': {
65 'testcase/tstRTFileAio': '', # See xTracker #8035.
66 },
67 'linux.amd64': {
68 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
69 # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
70 },
71 'solaris': {
72 'testcase/tstIntNet-1': '', # Fails opening rge0, probably a generic issue figuring which nic to use.
73 'testcase/tstIprtList': '', # Crashes in the multithreaded test, I think.
74 'testcase/tstRTCritSect': '', # Fairness/whatever issue here.
75 'testcase/tstRTR0MemUserKernelDriver': '', # Failes when kernel to kernel buffers.
76 'testcase/tstRTSemRW': '', # line 338: RTSemRWReleaseRead(hSemRW): got VERR_ACCESS_DENIED
77 'testcase/tstRTStrAlloc': '', # VERR_NO_STR_MEMORY!
78 'testcase/tstRTFileQuerySize-1': '', # VERR_DEV_IO_ERROR on /dev/null!
79 },
80 'solaris.amd64': {
81 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
82 # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
83 },
84 'win': {
85 'testcase/tstFile': '', # ??
86 'testcase/tstIntNet-1': '', # possibly same issue as solaris.
87 'testcase/tstMouseImpl': '', # STATUS_ACCESS_VIOLATION
88 'testcase/tstRTR0ThreadPreemptionDriver': '', # ??
89 'testcase/tstRTPath': '<4.3.51r89894',
90 'testcase/tstRTPipe': '', # ??
91 'testcase/tstRTR0MemUserKernelDriver': '', # ??
92 'testcase/tstRTR0SemMutexDriver': '', # ??
93 'testcase/tstRTStrAlloc': '', # ??
94 'testcase/tstRTStrFormat': '', # ??
95 'testcase/tstRTSystemQueryOsInfo': '', # ??
96 'testcase/tstRTTemp': '', # ??
97 'testcase/tstRTTime': '', # ??
98 'testcase/tstTime-2': '', # Total time differs too much! ... delta=-10859859
99 'testcase/tstTime-4': '', # Needs to be converted to DLL; ditto for tstTime-2.
100 'testcase/tstUtf8': '', # ??
101 'testcase/tstVMMR0CallHost-2': '', # STATUS_STACK_OVERFLOW
102 'testcase/tstX86-1': '', # Fails on win.x86.
103 'tscpasswd': '', # ??
104 'tstVMREQ': '', # ?? Same as darwin.x86?
105 },
106 'win.x86': {
107 'testcase/tstRTR0TimerDriver': '', # See xTracker #8041.
108 }
109 };
110
111 kdTestCasesBuggy = {
112 'testcase/tstGuestPropSvc': '', # GET_NOTIFICATION fails on testboxlin5.de.oracle.com and others.
113 'testcase/tstRTProcCreateEx': '', # Seen failing on wei01-b6ka-9.de.oracle.com.
114 'testcase/tstTimer': '', # Sometimes fails on linux, not important atm.
115 'testcase/tstGIP-2': '', # 2015-09-10: Fails regularly. E.g. TestSetID 2744205 (testboxsh2),
116 # 2743961 (wei01-b6kc-6). The responsible engineer should reenable
117 # it once it has been fixed.
118 };
119
120 ## The permanent exclude list.
121 # @note Stripped of extensions!
122 kdTestCasesBlackList = {
123 'testcase/tstClipboardX11Smoke': '', # (Old naming, deprecated) Needs X, not available on all test boxes.
124 'testcase/tstClipboardGH-X11Smoke': '', # (New name) Ditto.
125 'tstClipboardQt': '', # Is interactive and needs Qt, needed for Qt clipboard bugfixing.
126 'testcase/tstClipboardQt': '', # In case it moves here.
127 'testcase/tstFileLock': '',
128 'testcase/tstDisasm-2': '', # without parameters it will disassembler 1GB starting from 0
129 'testcase/tstFileAppendWin-1': '',
130 'testcase/tstDir': '', # useless without parameters
131 'testcase/tstDir-2': '', # useless without parameters
132 'testcase/tstGlobalConfig': '',
133 'testcase/tstHostHardwareLinux': '', # must be killed with CTRL-C
134 'testcase/tstHttp': '', # Talks to outside servers.
135 'testcase/tstRTHttp': '', # parameters required
136 'testcase/tstLdr-2': '', # parameters required
137 'testcase/tstLdr-3': '', # parameters required
138 'testcase/tstLdr': '', # parameters required
139 'testcase/tstLdrLoad': '', # parameters required
140 'testcase/tstMove': '', # parameters required
141 'testcase/tstRTR0Timer': '', # loads 'tstRTR0Timer.r0'
142 'testcase/tstRTR0ThreadDriver': '', # loads 'tstRTR0Thread.r0'
143 'testcase/tstRunTestcases': '', # that's a script like this one
144 'testcase/tstRTReqPool': '', # fails sometimes, testcase buggy
145 'testcase/tstRTS3': '', # parameters required
146 'testcase/tstSDL': '', # graphics test
147 'testcase/tstSupLoadModule': '', # Needs parameters and vboxdrv access. Covered elsewhere.
148 'testcase/tstSeamlessX11': '', # graphics test
149 'testcase/tstTime-3': '', # parameters required
150 'testcase/tstVBoxControl': '', # works only inside a guest
151 'testcase/tstVDCopy': '', # parameters required
152 'testcase/tstVDFill': '', # parameters required
153 'tstAnimate': '', # parameters required
154 'testcase/tstAPI': '', # user interaction required
155 'tstCollector': '', # takes forever
156 'testcase/tstHeadless': '', # parameters required
157 'tstHeadless': '', # parameters required
158 'tstMicroRC': '', # required by tstMicro
159 'tstVBoxDbg': '', # interactive test
160 'testcase/tstTestServMgr': '', # some strange xpcom18a4 test, does not work
161 'tstTestServMgr': '', # some strange xpcom18a4 test, does not work
162 'tstPDMAsyncCompletion': '', # parameters required
163 'testcase/tstXptDump': '', # parameters required
164 'tstXptDump': '', # parameters required
165 'testcase/tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
166 'tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
167 'testcase/tstSimpleTypeLib': '', # parameters required
168 'tstSimpleTypeLib': '', # parameters required
169 'testcase/tstTestAtoms': '', # additional test file (words.txt) required
170 'tstTestAtoms': '', # additional test file (words.txt) required
171 'testcase/tstXptLink': '', # parameters required
172 'tstXptLink': '', # parameters required
173 'tstXPCOMCGlue': '', # user interaction required
174 'testcase/tstXPCOMCGlue': '', # user interaction required
175 'testcase/tstCAPIGlue': '', # user interaction required
176 'testcase/tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
177 'tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
178 'testcase/tstRTFilesystem': '', # parameters required
179 'testcase/tstRTDvm': '', # parameters required
180 'tstSSLCertDownloads': '', # Obsolete.
181 # later
182 'testcase/tstIntNetR0': '', # RTSPINLOCK_FLAGS_INTERRUPT_SAFE == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE
183 # slow stuff
184 'testcase/tstAvl': '', # SLOW!
185 'testcase/tstRTAvl': '', # SLOW! (new name)
186 'testcase/tstVD': '', # 8GB fixed-sized vmdk
187 # failed or hang
188 'testcase/tstCryptoPkcs7Verify': '', # hang
189 'tstOVF': '', # hang (only ancient version, now in new place)
190 'testcase/tstOVF': '', # Creates mess when fails, needs to be run in a separate test.
191 'testcase/tstRTLockValidator': '', # Lock validation is not enabled for critical sections
192 'testcase/tstGuestControlSvc': '', # failed: line 288: testHost(&svcTable):
193 # expected VINF_SUCCESS, got VERR_NOT_FOUND
194 'testcase/tstRTMemEf': '', # failed w/o error message
195 'testcase/tstSupSem': '', # failed: SRE Timeout Accuracy (ms) : FAILED (1 errors)
196 'testcase/tstCryptoPkcs7Sign': '', # failed: 29330:
197 # error:02001002:lib(2):func(1):reason(2):NA:0:fopen('server.pem': '','r')
198 'testcase/tstCompressionBenchmark': '', # failed: error: RTZipBlockCompress failed
199 # for 'RTZipBlock/LZJB' (#4): VERR_NOT_SUPPORTED
200 'tstPDMAsyncCompletionStress': '', # VERR_INVALID_PARAMETER (cbSize = 0)
201 'tstMicro': '', # doesn't work on solaris, fix later if we care.
202 'tstVMM-HwAccm': '', # failed: Only checked AMD-V on linux
203 'tstVMM-HM': '', # failed: Only checked AMD-V on linux
204 'tstVMMFork': '', # failed: xtracker 6171
205 'tstTestFactory': '', # some strange xpcom18a4 test, does not work
206 'testcase/tstRTSemXRoads': '', # sporadically failed: Traffic - 8 threads per direction, 10 sec :
207 # FAILED (8 errors)
208 'tstVBoxAPILinux': '', # creates VirtualBox directories for root user because of sudo
209 # (should be in vbox)
210 'testcase/tstVMStructDTrace': '', # This is a D-script generator.
211 'tstVMStructRC': '', # This is a C-code generator.
212 'tstDeviceStructSizeRC': '', # This is a C-code generator.
213 'testcase/tstTSC': '', # Doesn't test anything and might fail with HT or/and too many cores.
214 'testcase/tstOpenUSBDev': '', # Not a useful testcase.
215 'testcase/tstX86-1': '', # Really more guest side.
216 'testcase/tstX86-FpuSaveRestore': '', # Experiments, could be useful for the guest not the host.
217 'tstAsmStructsRC': '', # Testcase run during build time (fails to find libstdc++.so.6 on some
218 # Solaris testboxes).
219 };
220
221 # Suffix exclude list.
222 kasSuffixBlackList = [
223 '.r0',
224 '.gc',
225 '.debug',
226 '.rel',
227 '.sys',
228 '.ko',
229 '.o',
230 '.obj',
231 '.lib',
232 '.a',
233 '.so',
234 '.dll',
235 '.dylib',
236 '.tmp',
237 '.log',
238 '.py',
239 '.pyc',
240 '.pyo',
241 '.pdb',
242 '.dSYM',
243 '.sym',
244 '.template',
245 '.expected',
246 '.expect',
247 ];
248
249 # White list, which contains tests considered to be safe to execute,
250 # even on remote targets (guests).
251 kdTestCasesWhiteList = {
252 'testcase/tstFile': '',
253 'testcase/tstFileLock': '',
254 'testcase/tstRTLocalIpc': '',
255 'testcase/tstRTPathQueryInfo': '',
256 'testcase/tstRTPipe': '',
257 'testcase/tstRTProcCreateEx': '',
258 'testcase/tstRTProcCreatePrf': '',
259 'testcase/tstRTProcIsRunningByName': '',
260 'testcase/tstRTProcQueryUsername': '',
261 'testcase/tstRTProcWait': '',
262 'testcase/tstTime-2': '',
263 'testcase/tstTime-3': '',
264 'testcase/tstTime-4': '',
265 'testcase/tstTimer': '',
266 'testcase/tstThread-1': '',
267 'testcase/tstUtf8': ''
268 };
269
270 # Test dependency list -- libraries.
271 # Needed in order to execute testcases on remote targets which don't have a VBox installation present.
272 kdTestCaseDepsLibs = [
273 "VBoxRT"
274 ];
275
276 ## The exclude list.
277 # @note Stripped extensions!
278 kasHardened = [
279 "testcase/tstIntNet-1",
280 "testcase/tstR0ThreadPreemptionDriver", # VBox 4.3
281 "testcase/tstRTR0ThreadPreemptionDriver",
282 "testcase/tstRTR0MemUserKernelDriver",
283 "testcase/tstRTR0SemMutexDriver",
284 "testcase/tstRTR0TimerDriver",
285 "testcase/tstRTR0ThreadDriver",
286 'testcase/tstRTR0DbgKrnlInfoDriver',
287 "tstInt",
288 "tstPDMQueue", # Comment in testcase says its driverless, but it needs driver access.
289 "tstVMM",
290 "tstVMMFork",
291 "tstVMREQ",
292 'testcase/tstCFGM',
293 'testcase/tstContiguous',
294 'testcase/tstGetPagingMode',
295 'testcase/tstGIP-2',
296 'testcase/tstInit',
297 'testcase/tstLow',
298 'testcase/tstMMHyperHeap',
299 'testcase/tstPage',
300 'testcase/tstPin',
301 'testcase/tstRTTime', 'testcase/tstTime', # GIP test case.
302 'testcase/tstRTTime-2', 'testcase/tstTime-2', # GIP test case.
303 'testcase/tstRTTime-4', 'testcase/tstTime-4', # GIP test case.
304 'testcase/tstSSM',
305 'testcase/tstSupSem-Zombie',
306 ]
307
308 ## Argument lists
309 kdArguments = {
310 'testcase/tstbntest': [ '-out', os.devnull, ], # Very noisy.
311 };
312
313
314 ## Status code translations.
315 ## @{
316 kdExitCodeNames = {
317 0: 'RTEXITCODE_SUCCESS',
318 1: 'RTEXITCODE_FAILURE',
319 2: 'RTEXITCODE_SYNTAX',
320 3: 'RTEXITCODE_INIT',
321 4: 'RTEXITCODE_SKIPPED',
322 };
323 kdExitCodeNamesWin = {
324 -1073741515: 'STATUS_DLL_NOT_FOUND',
325 -1073741512: 'STATUS_ORDINAL_NOT_FOUND',
326 -1073741511: 'STATUS_ENTRYPOINT_NOT_FOUND',
327 -1073741502: 'STATUS_DLL_INIT_FAILED',
328 -1073741500: 'STATUS_UNHANDLED_EXCEPTION',
329 -1073741499: 'STATUS_APP_INIT_FAILURE',
330 -1073741819: 'STATUS_ACCESS_VIOLATION',
331 -1073741571: 'STATUS_STACK_OVERFLOW',
332 };
333 ## @}
334
335 def __init__(self):
336 """
337 Reinitialize child class instance.
338 """
339 vbox.TestDriver.__init__(self);
340
341 # We need to set a default test VM set here -- otherwise the test
342 # driver base class won't let us use the "--test-vms" switch.
343 #
344 # See the "--local" switch in self.parseOption().
345 self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
346
347 # Selected NIC attachment.
348 self.sNicAttachment = '';
349
350 # Session handling stuff.
351 # Only needed for remote tests executed by TxS.
352 self.oSession = None;
353 self.oTxsSession = None;
354
355 self.sVBoxInstallRoot = None;
356
357 # Testing mode being used:
358 # "local": Execute unit tests locally (same host, default).
359 # "remote-copy": Copies unit tests from host to the remote, then executing it.
360 # "remote-exec": Executes unit tests right on the remote from a given source.
361 self.sMode = 'local';
362
363 self.cSkipped = 0;
364 self.cPassed = 0;
365 self.cFailed = 0;
366
367 # The source directory where our unit tests live.
368 # This most likely is our out/ or some staging directory and
369 # also acts the source for copying over the testcases to a remote target.
370 self.sUnitTestsPathSrc = None;
371
372 # Array of environment variables with NAME=VAL entries
373 # to be applied for testcases.
374 #
375 # This is also needed for testcases which are being executed remotely.
376 self.asEnv = [];
377
378 # The destination directory our unit tests live when being
379 # copied over to a remote target (via TxS).
380 self.sUnitTestsPathDst = None;
381
382 self.sExeSuff = '.exe' if utils.getHostOs() in [ 'win', 'dos', 'os2' ] else '';
383
384 self.aiVBoxVer = (4, 3, 0, 0);
385
386 # For testing testcase logic.
387 self.fDryRun = False;
388 self.fOnlyWhiteList = False;
389
390
391 def _detectPaths(self):
392 """
393 Internal worker for actionVerify and actionExecute that detects paths.
394
395 This sets sVBoxInstallRoot and sUnitTestsPathBase and returns True/False.
396 """
397
398 #
399 # We need a VBox install (/ build) to test.
400 #
401 if False is True: ## @todo r=andy WTF?
402 if not self.importVBoxApi():
403 reporter.error('Unabled to import the VBox Python API.')
404 return False
405 else:
406 self._detectBuild();
407 if self.oBuild is None:
408 reporter.error('Unabled to detect the VBox build.');
409 return False;
410
411 #
412 # Where are the files installed?
413 # Solaris requires special handling because of it's multi arch subdirs.
414 #
415 self.sVBoxInstallRoot = self.oBuild.sInstallPath
416 if not self.oBuild.isDevBuild() and utils.getHostOs() == 'solaris':
417 sArchDir = utils.getHostArch();
418 if sArchDir == 'x86': sArchDir = 'i386';
419 self.sVBoxInstallRoot = os.path.join(self.sVBoxInstallRoot, sArchDir);
420
421 # Add the installation root to the PATH on windows so we can get DLLs from it.
422 if utils.getHostOs() == 'win':
423 sPathName = 'PATH';
424 if not sPathName in os.environ:
425 sPathName = 'Path';
426 sPath = os.environ.get(sPathName, '.');
427 if sPath and sPath[-1] != ';':
428 sPath += ';';
429 os.environ[sPathName] = sPath + self.sVBoxInstallRoot + ';';
430
431 #
432 # The unittests are generally not installed, so look for them.
433 #
434 sBinOrDist = 'dist' if utils.getHostOs() in [ 'darwin', ] else 'bin';
435 asCandidates = [
436 self.oBuild.sInstallPath,
437 os.path.join(self.sScratchPath, utils.getHostOsDotArch(), self.oBuild.sType, sBinOrDist),
438 os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'release', sBinOrDist),
439 os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'debug', sBinOrDist),
440 os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'strict', sBinOrDist),
441 os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'dbgopt', sBinOrDist),
442 os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'profile', sBinOrDist),
443 os.path.join(self.sScratchPath, sBinOrDist + '.' + utils.getHostArch()),
444 os.path.join(self.sScratchPath, sBinOrDist, utils.getHostArch()),
445 os.path.join(self.sScratchPath, sBinOrDist),
446 ];
447 if utils.getHostOs() == 'darwin':
448 for i in range(1, len(asCandidates)):
449 asCandidates[i] = os.path.join(asCandidates[i], 'VirtualBox.app', 'Contents', 'MacOS');
450
451 for sCandidat in asCandidates:
452 if os.path.exists(os.path.join(sCandidat, 'testcase', 'tstVMStructSize' + self.sExeSuff)):
453 self.sUnitTestsPathSrc = sCandidat;
454 return True;
455
456 reporter.error('Unable to find unit test dir. Candidates: %s' % (asCandidates,))
457 return False;
458
459 #
460 # Overridden methods.
461 #
462
463 def showUsage(self):
464 """
465 Shows the testdriver usage.
466 """
467 fRc = vbox.TestDriver.showUsage(self);
468 reporter.log('');
469 reporter.log('Unit Test #1 options:');
470 reporter.log(' --dryrun');
471 reporter.log(' Performs a dryrun (no tests being executed).');
472 reporter.log(' --mode <local|remote>');
473 reporter.log(' Specifies the execution mode.');
474 reporter.log(' --only-whitelist');
475 reporter.log(' Only processes the white list.');
476 reporter.log(' --quick');
477 reporter.log(' Very selective testing.');
478 reporter.log(' --unittest-source <dir>');
479 reporter.log(' Sets the unit test source to <dir>.');
480 reporter.log(' Also used for remote execution.');
481 reporter.log(' --vbox-install-root <dir>');
482 reporter.log(' Sets the VBox install root to <dir>.');
483 reporter.log(' Also used for remote execution.');
484 return fRc;
485
486 def parseOption(self, asArgs, iArg):
487 """
488 Parses the testdriver arguments from the command line.
489 """
490 if asArgs[iArg] == '--dryrun':
491 self.fDryRun = True;
492 elif asArgs[iArg] == '--mode':
493 iArg += 1;
494 if iArg >= len(asArgs):
495 raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
496 if asArgs[iArg] == 'local':
497 self.sMode = asArgs[iArg];
498 elif asArgs[iArg] == 'remote':
499 if self.sUnitTestsPathSrc:
500 self.sMode = 'remote-exec';
501 else:
502 self.sMode = 'remote-copy';
503 else:
504 raise base.InvalidOption('Argument "%s" invalid' % (asArgs[iArg]));
505 elif asArgs[iArg] == '--unittest-source':
506 iArg += 1;
507 if iArg >= len(asArgs):
508 raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
509 self.sUnitTestsPathSrc = asArgs[iArg];
510 self.sMode = 'remote-exec';
511 elif asArgs[iArg] == '--only-whitelist':
512 self.fOnlyWhiteList = True;
513 elif asArgs[iArg] == '--quick':
514 self.fOnlyWhiteList = True;
515 elif asArgs[iArg] == '--vbox-install-root':
516 iArg += 1;
517 if iArg >= len(asArgs):
518 raise base.InvalidOption('Option "%s" needs a value' % (asArgs[iArg - 1]));
519 self.sVBoxInstallRoot = asArgs[iArg];
520 else:
521 return vbox.TestDriver.parseOption(self, asArgs, iArg);
522 return iArg + 1;
523
524 def actionVerify(self):
525 if not self._detectPaths():
526 return False;
527
528 if self.oTestVmSet:
529 return vbox.TestDriver.actionVerify(self);
530
531 return True;
532
533 def actionConfig(self):
534 # Make sure vboxapi has been imported so we can use the constants.
535 if not self.importVBoxApi():
536 return False;
537
538 # Do the configuring.
539 if self.sMode.startswith('remote'):
540 if self.sNicAttachment == 'nat': eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
541 elif self.sNicAttachment == 'bridged': eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
542 else: eNic0AttachType = None;
543
544 # Make sure to mount the Validation Kit .ISO so that TxS has the chance
545 # to update itself.
546 #
547 # This is necessary as a lot of our test VMs nowadays have a very old TxS
548 # installed which don't understand commands like uploading files to the guest.
549 # Uploading files is needed for this test driver, however.
550 #
551 ## @todo Get rid of this as soon as we create test VMs in a descriptive (automated) manner.
552 return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, \
553 sDvdImage = self.sVBoxValidationKitIso);
554
555 return True;
556
557 def actionExecute(self):
558 if self.sUnitTestsPathSrc is None and not self._detectPaths():
559 return False;
560 reporter.log2('Unit test source path is "%s"\n' % self.sUnitTestsPathSrc);
561
562 if not self.sUnitTestsPathDst:
563 self.sUnitTestsPathDst = self.sScratchPath;
564 reporter.log2('Unit test destination path is "%s"\n' % self.sUnitTestsPathDst);
565
566 if self.sMode.startswith('remote'): # Run on a test VM (guest).
567 assert self.oTestVmSet is not None;
568 fRc = self.oTestVmSet.actionExecute(self, self.testOneVmConfig);
569 else: # Run locally (host).
570 self._figureVersion();
571 self._makeEnvironmentChanges();
572 fRc = self._testRunUnitTests(None);
573
574 return fRc;
575
576 #
577 # Test execution helpers.
578 #
579
580 def _testRunUnitTests(self, oTestVm):
581 """
582 Main function to execute all unit tests.
583 """
584 self._testRunUnitTestsSet(r'^tst*', 'testcase');
585 self._testRunUnitTestsSet(r'^tst*', '.');
586
587 fRc = self.cFailed == 0;
588
589 reporter.log('');
590 if self.fDryRun:
591 reporter.log('*********************************************************');
592 reporter.log('DRY RUN - DRY RUN - DRY RUN - DRY RUN - DRY RUN - DRY RUN');
593 reporter.log('*********************************************************');
594 reporter.log('*********************************************************');
595 reporter.log(' Target: %s' % (oTestVm.sVmName if oTestVm else 'local'));
596 reporter.log(' Mode: %s' % (self.sMode));
597 reporter.log('Unit tests source: %s %s' % (self.sUnitTestsPathSrc, \
598 '(on remote)' if self.sMode == 'remote-exec' else ''));
599 reporter.log('VBox install root: %s %s' % (self.sVBoxInstallRoot, \
600 '(on remote)' if self.sMode == 'remote-exec' else ''));
601 reporter.log('*********************************************************');
602 reporter.log('*** PASSED: %d' % self.cPassed);
603 reporter.log('*** FAILED: %d' % self.cFailed);
604 reporter.log('*** SKIPPED: %d' % self.cSkipped);
605 reporter.log('*** TOTAL: %d' % (self.cPassed + self.cFailed + self.cSkipped));
606
607 return fRc;
608
609
610 def testOneVmConfig(self, oVM, oTestVm):
611 """
612 Runs the specified VM thru test #1.
613 """
614
615 # Simple test.
616 self.logVmInfo(oVM);
617
618 if not self.fDryRun:
619 # Try waiting for a bit longer (5 minutes) until the CD is available to avoid running into timeouts.
620 self.oSession, self.oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, \
621 fCdWait = not self.fDryRun, \
622 cMsCdWait = 5 * 60 * 1000);
623 if self.oSession is None:
624 return False;
625
626 self.addTask(self.oTxsSession);
627
628 # Determine the unit tests destination path.
629 self.sUnitTestsPathDst = oTestVm.pathJoin(self.getGuestTempDir(oTestVm), 'testUnitTests');
630
631 # Run the unit tests.
632 self._testRunUnitTests(oTestVm);
633
634 # Cleanup.
635 if self.oSession is not None:
636 self.removeTask(self.oTxsSession);
637 self.terminateVmBySession(self.oSession);
638 return True;
639
640 #
641 # Test execution helpers.
642 #
643
644 def _figureVersion(self):
645 """ Tries to figure which VBox version this is, setting self.aiVBoxVer. """
646 try:
647 sVer = utils.processOutputChecked(['VBoxManage', '--version'])
648
649 sVer = sVer.strip();
650 sVer = re.sub(r'_BETA.*r', '.', sVer);
651 sVer = re.sub(r'_ALPHA.*r', '.', sVer);
652 sVer = re.sub(r'_RC.*r', '.', sVer);
653 sVer = re.sub('_SPB', '', sVer)
654 sVer = sVer.replace('r', '.');
655
656 self.aiVBoxVer = [int(sComp) for sComp in sVer.split('.')];
657
658 reporter.log('VBox version: %s' % (self.aiVBoxVer,));
659 except:
660 reporter.logXcpt();
661 return False;
662 return True;
663
664 def _compareVersion(self, aiVer):
665 """
666 Compares the give version string with the vbox version string,
667 returning a result similar to C strcmp(). aiVer is on the right side.
668 """
669 cComponents = min(len(self.aiVBoxVer), len(aiVer));
670 for i in range(cComponents):
671 if self.aiVBoxVer[i] < aiVer[i]:
672 return -1;
673 if self.aiVBoxVer[i] > aiVer[i]:
674 return 1;
675 return len(self.aiVBoxVer) - len(aiVer);
676
677 def _isExcluded(self, sTest, dExclList):
678 """ Checks if the testcase is excluded or not. """
679 if sTest in dExclList:
680 sFullExpr = dExclList[sTest].replace(' ', '').strip();
681 if sFullExpr == '':
682 return True;
683
684 # Consider each exclusion expression. These are generally ranges,
685 # either open ended or closed: "<4.3.51r12345", ">=4.3.0 && <=4.3.4".
686 asExprs = sFullExpr.split(';');
687 for sExpr in asExprs:
688
689 # Split it on the and operator and process each sub expression.
690 fResult = True;
691 for sSubExpr in sExpr.split('&&'):
692 # Split out the comparison operator and the version value.
693 if sSubExpr.startswith('<=') or sSubExpr.startswith('>='):
694 sOp = sSubExpr[:2];
695 sValue = sSubExpr[2:];
696 elif sSubExpr.startswith('<') or sSubExpr.startswith('>') or sSubExpr.startswith('='):
697 sOp = sSubExpr[:1];
698 sValue = sSubExpr[1:];
699 else:
700 sOp = sValue = '';
701
702 # Convert the version value, making sure we've got a valid one.
703 try: aiValue = [int(sComp) for sComp in sValue.replace('r', '.').split('.')];
704 except: aiValue = ();
705 if not aiValue or len(aiValue) > 4:
706 reporter.error('Invalid exclusion expression for %s: "%s" [%s]' % (sTest, sSubExpr, dExclList[sTest]));
707 return True;
708
709 # Do the compare.
710 iCmp = self._compareVersion(aiValue);
711 if sOp == '>=' and iCmp < 0:
712 fResult = False;
713 elif sOp == '>' and iCmp <= 0:
714 fResult = False;
715 elif sOp == '<' and iCmp >= 0:
716 fResult = False;
717 elif sOp == '>=' and iCmp < 0:
718 fResult = False;
719 reporter.log2('iCmp=%s; %s %s %s -> %s' % (iCmp, self.aiVBoxVer, sOp, aiValue, fResult));
720
721 # Did the expression match?
722 if fResult:
723 return True;
724
725 return False;
726
727 def _sudoExecuteSync(self, asArgs):
728 """
729 Executes a sudo child process synchronously.
730 Returns True if the process executed successfully and returned 0,
731 otherwise False is returned.
732 """
733 reporter.log2('Executing [sudo]: %s' % (asArgs, ));
734 if self.sMode.startswith('remote'):
735 iRc = -1; ## @todo Not used remotely yet.
736 else:
737 try:
738 iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
739 except:
740 reporter.errorXcpt();
741 return False;
742 reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
743 return iRc == 0;
744
745 def _wrapPathExists(self, sPath):
746 """
747 Creates the directory specified sPath (including parents).
748 """
749 reporter.log2('_wrapPathExists: %s' % (sPath,));
750 if self.fDryRun:
751 return True;
752 fRc = False;
753 if self.sMode.startswith('remote'):
754 fRc = self.txsIsDir(self.oSession, self.oTxsSession, sPath, fIgnoreErrors = True);
755 if not fRc:
756 fRc = self.txsIsFile(self.oSession, self.oTxsSession, sPath, fIgnoreErrors = True);
757 else:
758 fRc = os.path.exists(sPath);
759 return fRc;
760
761 def _wrapMkDir(self, sPath):
762 """
763 Creates the directory specified sPath (including parents).
764 """
765 reporter.log2('_wrapMkDir: %s' % (sPath,));
766 if self.fDryRun:
767 return True;
768 fRc = True;
769 if self.sMode.startswith('remote'):
770 fRc = self.txsMkDirPath(self.oSession, self.oTxsSession, sPath, fMode = 0o755);
771 else:
772 if utils.getHostOs() in [ 'win', 'os2' ]:
773 os.makedirs(sPath, 0o755);
774 else:
775 fRc = self._sudoExecuteSync(['/bin/mkdir', '-p', '-m', '0755', sPath]);
776 if fRc is not True:
777 raise Exception('Failed to create dir "%s".' % (sPath,));
778 return True;
779
780 def _wrapCopyFile(self, sSrc, sDst, iMode):
781 """
782 Copies a file.
783 """
784 reporter.log2('_wrapCopyFile: %s -> %s (mode: %o)' % (sSrc, sDst, iMode,));
785 if self.fDryRun:
786 return True;
787 fRc = True;
788 if self.sMode.startswith('remote'):
789 if self.sMode == 'remote-exec':
790 self.oTxsSession.txsCopyFile(self.oSession, self.oTxsSession, sSrc, sDst, iMode);
791 else:
792 fRc = self.txsUploadFile(self.oSession, self.oTxsSession, sSrc, sDst);
793 if fRc:
794 self.oTxsSession.syncChMod(sDst, iMode);
795 else:
796 if utils.getHostOs() in [ 'win', 'os2' ]:
797 utils.copyFileSimple(sSrc, sDst);
798 os.chmod(sDst, iMode);
799 else:
800 fRc = self._sudoExecuteSync(['/bin/cp', sSrc, sDst]);
801 if fRc:
802 fRc = self._sudoExecuteSync(['/bin/chmod', '%o' % (iMode,), sDst]);
803 if fRc is not True:
804 raise Exception('Failed to chmod "%s".' % (sDst,));
805 if fRc is not True:
806 raise Exception('Failed to copy "%s" to "%s".' % (sSrc, sDst,));
807 return True;
808
809 def _wrapDeleteFile(self, sPath):
810 """
811 Deletes a file.
812 """
813 reporter.log2('_wrapDeleteFile: %s' % (sPath,));
814 if self.fDryRun:
815 return True;
816 fRc = True;
817 if self.sMode.startswith('remote'):
818 if self.txsIsFile(self.oSession, self.oTxsSession, sPath):
819 fRc = self.txsRmFile(self.oSession, self.oTxsSession, sPath);
820 else:
821 if os.path.exists(sPath):
822 if utils.getHostOs() in [ 'win', 'os2' ]:
823 os.remove(sPath);
824 else:
825 fRc = self._sudoExecuteSync(['/bin/rm', sPath]);
826 if fRc is not True:
827 raise Exception('Failed to remove "%s".' % (sPath,));
828 return True;
829
830 def _wrapRemoveDir(self, sPath):
831 """
832 Removes a directory.
833 """
834 reporter.log2('_wrapRemoveDir: %s' % (sPath,));
835 if self.fDryRun:
836 return True;
837 fRc = True;
838 if self.sMode.startswith('remote'):
839 if self.txsIsDir(self.oSession, self.oTxsSession, sPath):
840 fRc = self.txsRmDir(self.oSession, self.oTxsSession, sPath);
841 else:
842 if os.path.exists(sPath):
843 if utils.getHostOs() in [ 'win', 'os2' ]:
844 os.rmdir(sPath);
845 else:
846 fRc = self._sudoExecuteSync(['/bin/rmdir', sPath]);
847 if fRc is not True:
848 raise Exception('Failed to remove "%s".' % (sPath,));
849 return True;
850
851 def _envSet(self, sName, sValue):
852 if self.sMode.startswith('remote'):
853 # For remote execution we cache the environment block and pass it
854 # right when the process execution happens.
855 self.asEnv.append([ sName, sValue ]);
856 else:
857 os.environ[sName] = sValue;
858 return True;
859
860 def _executeTestCase(self, sName, sFullPath, sTestCaseSubDir, oDevNull): # pylint: disable=too-many-locals,too-many-statements
861 """
862 Executes a test case.
863 """
864
865 fSkipped = False;
866
867 #
868 # If hardening is enabled, some test cases and their dependencies
869 # needs to be copied to and execute from the source
870 # directory in order to work. They also have to be executed as
871 # root, i.e. via sudo.
872 #
873 fHardened = False;
874 fCopyToRemote = False;
875 fCopyDeps = False;
876 asFilesToRemove = []; # Stuff to clean up.
877 asDirsToRemove = []; # Ditto.
878
879 if sName in self.kasHardened \
880 and self.sUnitTestsPathSrc != self.sVBoxInstallRoot:
881 fHardened = True;
882
883 if self.sMode.startswith('remote'):
884 fCopyToRemote = True;
885 fCopyDeps = True;
886
887 if fHardened \
888 or fCopyToRemote:
889 if fCopyToRemote:
890 sDstDir = os.path.join(self.sUnitTestsPathDst, sTestCaseSubDir);
891 else:
892 sDstDir = os.path.join(self.sVBoxInstallRoot, sTestCaseSubDir);
893 if not self._wrapPathExists(sDstDir):
894 self._wrapMkDir(sDstDir);
895 asDirsToRemove.append(sDstDir);
896
897 sDst = os.path.join(sDstDir, os.path.basename(sFullPath));
898 self._wrapCopyFile(sFullPath, sDst, 0o755);
899 asFilesToRemove.append(sDst);
900
901 # Copy required dependencies to destination.
902 if fCopyDeps:
903 for sLib in self.kdTestCaseDepsLibs:
904 for sSuff in [ '.dll', '.so', '.dylib' ]:
905 assert self.sVBoxInstallRoot is not None;
906 sSrc = os.path.join(self.sVBoxInstallRoot, sLib + sSuff);
907 if self._wrapPathExists(sSrc):
908 sDst = os.path.join(sDstDir, os.path.basename(sSrc));
909 self._wrapCopyFile(sSrc, sDst, 0o644);
910 asFilesToRemove.append(sDst);
911
912 # Copy any associated .dll/.so/.dylib.
913 for sSuff in [ '.dll', '.so', '.dylib' ]:
914 sSrc = os.path.splitext(sFullPath)[0] + sSuff;
915 if os.path.exists(sSrc):
916 sDst = os.path.join(sDstDir, os.path.basename(sSrc));
917 self._wrapCopyFile(sSrc, sDst, 0o644);
918 asFilesToRemove.append(sDst);
919
920 # Copy any associated .r0, .rc and .gc modules.
921 offDriver = sFullPath.rfind('Driver')
922 if offDriver > 0:
923 for sSuff in [ '.r0', 'RC.rc', 'RC.gc' ]:
924 sSrc = sFullPath[:offDriver] + sSuff;
925 if os.path.exists(sSrc):
926 sDst = os.path.join(sDstDir, os.path.basename(sSrc));
927 self._wrapCopyFile(sSrc, sDst, 0o644);
928 asFilesToRemove.append(sDst);
929
930 sFullPath = os.path.join(sDstDir, os.path.basename(sFullPath));
931
932 #
933 # Set up arguments and environment.
934 #
935 asArgs = [sFullPath,]
936 if sName in self.kdArguments:
937 asArgs.extend(self.kdArguments[sName]);
938
939 sXmlFile = os.path.join(self.sUnitTestsPathDst, 'result.xml');
940
941 self._envSet('IPRT_TEST_OMIT_TOP_TEST', '1');
942 self._envSet('IPRT_TEST_FILE', sXmlFile);
943
944 if self._wrapPathExists(sXmlFile):
945 try: os.unlink(sXmlFile);
946 except: self._wrapDeleteFile(sXmlFile);
947
948 #
949 # Execute the test case.
950 #
951 # Windows is confusing output. Trying a few things to get rid of this.
952 # First, flush both stderr and stdout before running the child. Second,
953 # assign the child stderr to stdout. If this doesn't help, we'll have
954 # to capture the child output.
955 #
956 reporter.log('*** Executing %s%s...' % (asArgs, ' [hardened]' if fHardened else ''));
957 try: sys.stdout.flush();
958 except: pass;
959 try: sys.stderr.flush();
960 except: pass;
961
962 iRc = 0;
963
964 if not self.fDryRun:
965 if fCopyToRemote:
966 fRc = self.txsRunTest(self.oTxsSession, sName, 30 * 60 * 1000, asArgs[0], asArgs, self.asEnv, \
967 fCheckSessionStatus = True);
968 if fRc:
969 iRc = 0;
970 else:
971 (_, sOpcode, abPayload) = self.oTxsSession.getLastReply();
972 if sOpcode.startswith('PROC NOK '): # Extract process rc.
973 iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
974 if iRc == 0: # Might happen if the testcase misses some dependencies. Set it to -42 then.
975 iRc = -42;
976 else:
977 iRc = -1; ## @todo
978 else:
979 oChild = None;
980 try:
981 if fHardened:
982 oChild = utils.sudoProcessPopen(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
983 else:
984 oChild = utils.processPopenSafe(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
985 except:
986 if sName in [ 'tstAsmStructsRC', # 32-bit, may fail to start on 64-bit linux. Just ignore.
987 ]:
988 reporter.logXcpt();
989 fSkipped = True;
990 else:
991 reporter.errorXcpt();
992 iRc = 1023;
993 oChild = None;
994
995 if oChild is not None:
996 self.pidFileAdd(oChild.pid, sName, fSudo = fHardened);
997 iRc = oChild.wait();
998 self.pidFileRemove(oChild.pid);
999 #
1000 # Clean up
1001 #
1002 for sPath in asFilesToRemove:
1003 self._wrapDeleteFile(sPath);
1004 for sPath in asDirsToRemove:
1005 self._wrapRemoveDir(sPath);
1006
1007 #
1008 # Report.
1009 #
1010 if os.path.exists(sXmlFile):
1011 reporter.addSubXmlFile(sXmlFile);
1012 if fHardened:
1013 self._wrapDeleteFile(sXmlFile);
1014 else:
1015 os.unlink(sXmlFile);
1016
1017 if iRc == 0:
1018 reporter.log('*** %s: exit code %d' % (sFullPath, iRc));
1019 self.cPassed += 1;
1020
1021 elif iRc == 4: # RTEXITCODE_SKIPPED
1022 reporter.log('*** %s: exit code %d (RTEXITCODE_SKIPPED)' % (sFullPath, iRc));
1023 fSkipped = True;
1024 self.cSkipped += 1;
1025
1026 elif fSkipped:
1027 reporter.log('*** %s: exit code %d (Skipped)' % (sFullPath, iRc));
1028 self.cSkipped += 1;
1029
1030 else:
1031 sName = self.kdExitCodeNames.get(iRc, '');
1032 if iRc in self.kdExitCodeNamesWin and utils.getHostOs() == 'win':
1033 sName = self.kdExitCodeNamesWin[iRc];
1034 if sName != '':
1035 sName = ' (%s)' % (sName);
1036
1037 if iRc != 1:
1038 reporter.testFailure('Exit status: %d%s' % (iRc, sName));
1039 reporter.log( '!*! %s: exit code %d%s' % (sFullPath, iRc, sName));
1040 else:
1041 reporter.error('!*! %s: exit code %d%s' % (sFullPath, iRc, sName));
1042 self.cFailed += 1;
1043
1044 return fSkipped;
1045
1046 def _testRunUnitTestsSet(self, sTestCasePattern, sTestCaseSubDir):
1047 """
1048 Run subset of the unit tests set.
1049 """
1050
1051 # Open /dev/null for use as stdin further down.
1052 try:
1053 oDevNull = open(os.path.devnull, 'w+');
1054 except:
1055 oDevNull = None;
1056
1057 # Determin the host OS specific exclusion lists.
1058 dTestCasesBuggyForHostOs = self.kdTestCasesBuggyPerOs.get(utils.getHostOs(), []);
1059 dTestCasesBuggyForHostOs.update(self.kdTestCasesBuggyPerOs.get(utils.getHostOsDotArch(), []));
1060
1061 ## @todo Add filtering for more specific OSes (like OL server, doesn't have X installed) by adding a separate
1062 # black list + using utils.getHostOsVersion().
1063
1064 #
1065 # Process the file list and run everything looking like a testcase.
1066 #
1067 if not self.fOnlyWhiteList:
1068 if self.sMode == 'local' \
1069 or self.sMode == 'remote-copy':
1070 asFiles = sorted(os.listdir(os.path.join(self.sUnitTestsPathSrc, sTestCaseSubDir)));
1071 else: # 'remote-exec'
1072 ## @todo Implement remote file enumeration / directory listing.
1073 reporter.error('Sorry, no remote file enumeration implemented yet!\nUse --only-whitelist instead.');
1074 return;
1075 else:
1076 # Transform our dict into a list, where the keys are the list elements.
1077 asFiles = list(self.kdTestCasesWhiteList.keys());
1078 # Make sure to only keep the list item's base name so that the iteration down below works
1079 # with our white list without any additional modification.
1080 asFiles = [os.path.basename(s) for s in asFiles];
1081
1082 for sFilename in asFiles:
1083 # Separate base and suffix and morph the base into something we
1084 # can use for reporting and array lookups.
1085 sBaseName = os.path.basename(sFilename);
1086 sName, sSuffix = os.path.splitext(sBaseName);
1087 if sTestCaseSubDir != '.':
1088 sName = sTestCaseSubDir + '/' + sName;
1089
1090 reporter.log2('sTestCasePattern=%s, sBaseName=%s, sName=%s, sFileName=%s' \
1091 % (sTestCasePattern, sBaseName, sName, sFilename,));
1092
1093 # Process white list first, if set.
1094 if self.fOnlyWhiteList \
1095 and not self._isExcluded(sName, self.kdTestCasesWhiteList):
1096 reporter.log('%s: SKIPPED (not in white list)' % (sName,));
1097 continue;
1098
1099 # Basic exclusion.
1100 if not re.match(sTestCasePattern, sBaseName) \
1101 or sSuffix in self.kasSuffixBlackList:
1102 reporter.log2('"%s" is not a test case.' % (sName,));
1103 continue;
1104
1105 # Check if the testcase is black listed or buggy before executing it.
1106 if self._isExcluded(sName, self.kdTestCasesBlackList):
1107 # (No testStart/Done or accounting here!)
1108 reporter.log('%s: SKIPPED (blacklisted)' % (sName,));
1109
1110 elif self._isExcluded(sName, self.kdTestCasesBuggy):
1111 reporter.testStart(sName);
1112 reporter.log('%s: Skipping, buggy in general.' % (sName,));
1113 reporter.testDone(fSkipped = True);
1114 self.cSkipped += 1;
1115
1116 elif self._isExcluded(sName, dTestCasesBuggyForHostOs):
1117 reporter.testStart(sName);
1118 reporter.log('%s: Skipping, buggy on %s.' % (sName, utils.getHostOs(),));
1119 reporter.testDone(fSkipped = True);
1120 self.cSkipped += 1;
1121
1122 else:
1123 sFullPath = os.path.normpath(os.path.join(self.sUnitTestsPathSrc, os.path.join(sTestCaseSubDir, sFilename)));
1124 reporter.testStart(sName);
1125 try:
1126 fSkipped = self._executeTestCase(sName, sFullPath, sTestCaseSubDir, oDevNull);
1127 except:
1128 reporter.errorXcpt('!*!');
1129 self.cFailed += 1;
1130 fSkipped = False;
1131 reporter.testDone(fSkipped);
1132
1133
1134
1135if __name__ == '__main__':
1136 sys.exit(tdUnitTest1().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