VirtualBox

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

Last change on this file since 103286 was 103286, checked in by vboxsync, 13 months ago

ValidationKit/tests/tdUnitTest1: Need to append the revision in order for the version detection to work

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