VirtualBox

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

Last change on this file since 102666 was 102666, checked in by vboxsync, 12 months ago

Validation Kit: Added tstRTFsQueries to the tdUnitTest1 white list, so that it gets testing on older OSes as well. ​bugref:10415

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