VirtualBox

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

Last change on this file since 105767 was 105767, checked in by vboxsync, 9 months ago

Validation Kit: Included tstRTSystemQueryFirmware to tdUnitTest's white list to get more coverage on older stuff.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 59.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdUnitTest1.py 105767 2024-08-21 13:45:19Z 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.215389.xyz.
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: 105767 $"
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 'tstInt': '>=7.0.0', # Driverless package.
74 'testcase/tstLow': '>=7.0.0', # Driverless package.
75 'testcase/tstPin': '>=7.0.0', # Driverless package.
76 'testcase/tstIntNet-1': '>=7.0.0', # Driverless package.
77 'testcase/tstRTR0DbgKrnlInfoDriver': '>=7.0.0', # Driverless package.
78 'testcase/tstRTR0MemUserKernelDriver': '>=7.0.0', # Driverless package.
79 'testcase/tstRTR0SemMutexDriver': '>=7.0.0', # Driverless package.
80 'testcase/tstRTR0ThreadPreemptionDriver': '>=7.0.0', # Driverless package.
81 'testcase/tstRTR0TimerDriver': '>=7.0.0', # Driverless package.
82 'testcase/tstDarwinKeyboard': '', # Fails for unknown reason.
83 'testcase/tstVBoxAPIXPCOM': '', # Can't instantiate the VirtualBox object
84 # (binary would need moving to the VirtualBox installation
85 # directory, merely a compile time test anyway)
86 },
87 'darwin.arm64': {
88 'testcase/tstRTDarwinMachKernel': '', # Not supported on arm64 right now (and not required due to driverless).
89 'testcase/tstAsmStructs': '', # Fails on arm64 due to different sizes, also not required as there is no
90 # assembly code which needs to match with structs.
91 'testcase/tstRTTime': '', # Needs more work first.
92 },
93 'linux': {
94 'testcase/tstRTFileAio': '', # See xTracker #8035.
95 },
96 'linux.amd64': {
97 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
98 # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
99 },
100 'solaris': {
101 'testcase/tstIntNet-1': '', # Fails opening rge0, probably a generic issue figuring which nic to use.
102 'testcase/tstIprtList': '', # Crashes in the multithreaded test, I think.
103 'testcase/tstRTCritSect': '', # Fairness/whatever issue here.
104 'testcase/tstRTR0MemUserKernelDriver': '', # Failes when kernel to kernel buffers.
105 'testcase/tstRTSemRW': '', # line 338: RTSemRWReleaseRead(hSemRW): got VERR_ACCESS_DENIED
106 'testcase/tstRTStrAlloc': '', # VERR_NO_STR_MEMORY!
107 'testcase/tstRTFileQuerySize-1': '', # VERR_DEV_IO_ERROR on /dev/null!
108 'testcase/tstLow' : '', # VERR_NOT_SUPPORTED - allocating kernel memory with physical backing
109 # below 4GB (RTR0MemObjAllocLow) for running code (fExecutable=true)
110 # isn't implemented.
111 'testcase/tstContiguous' : '', # VERR_NOT_SUPPORTED - allocating kernel memory with contiguous physical
112 # backing below 4GB (RTR0MemObjAllocCont) for running code
113 # (fExecutable=true) isn't implemented.
114 'tstPDMQueue' : '' # VERR_NOT_SUPPORTED - running without the support driver (vboxdrv) isn't
115 # supported on Solaris (VMCREATE_F_DRIVERLESS/SUPR3INIT_F_DRIVERLESS).
116 },
117 'solaris.amd64': {
118 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
119 # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
120 },
121 'win': {
122 'testcase/tstFile': '', # ??
123 'testcase/tstIntNet-1': '', # possibly same issue as solaris.
124 'testcase/tstMouseImpl': '', # STATUS_ACCESS_VIOLATION
125 'testcase/tstRTR0ThreadPreemptionDriver': '', # ??
126 'testcase/tstRTPath': '<4.3.51r89894',
127 'testcase/tstRTPipe': '', # ??
128 'testcase/tstRTR0MemUserKernelDriver': '', # ??
129 'testcase/tstRTR0SemMutexDriver': '', # ??
130 'testcase/tstRTStrAlloc': '', # ??
131 'testcase/tstRTStrFormat': '', # ??
132 'testcase/tstRTSystemQueryOsInfo': '', # ??
133 'testcase/tstRTTemp': '', # ??
134 'testcase/tstRTTime': '', # ??
135 'testcase/tstTime-2': '', # Total time differs too much! ... delta=-10859859
136 'testcase/tstTime-4': '', # Needs to be converted to DLL; ditto for tstTime-2.
137 'testcase/tstUtf8': '', # ??
138 'testcase/tstVMMR0CallHost-2': '', # STATUS_STACK_OVERFLOW
139 'testcase/tstX86-1': '', # Fails on win.x86.
140 'tscpasswd': '', # ??
141 'tstVMREQ': '', # ?? Same as darwin.x86?
142 },
143 'win.x86': {
144 'testcase/tstRTR0TimerDriver': '', # See xTracker #8041.
145 }
146 };
147
148 kdTestCasesBuggy = {
149 'testcase/tstGuestPropSvc': '', # GET_NOTIFICATION fails on testboxlin5.de.oracle.com and others.
150 'testcase/tstTimer': '', # Sometimes fails on linux, not important atm.
151 'testcase/tstGIP-2': '', # 2015-09-10: Fails regularly. E.g. TestSetID 2744205 (testboxsh2),
152 # 2743961 (wei01-b6kc-6). The responsible engineer should reenable
153 # it once it has been fixed.
154 };
155
156 ## The permanent exclude list.
157 # @note Stripped of extensions!
158 kdTestCasesBlackList = {
159 'testcase/tstClipboardX11Smoke': '', # (Old naming, deprecated) Needs X, not available on all test boxes.
160 'testcase/tstClipboardGH-X11Smoke': '', # (New name) Ditto.
161 'testcase/tstClipboardHttpServerX11': '', # Ditto.
162 'testcase/tstClipboardMockHGCM': '', # Ditto.
163 'tstClipboardQt': '', # Is interactive and needs Qt, needed for Qt clipboard bugfixing.
164 'testcase/tstClipboardQt': '', # In case it moves here.
165 'tstDragAndDropQt': '', # Is interactive and needs Qt, needed for Qt drag'n drop bugfixing.
166 'testcase/tstDragAndDropQt': '', # In case it moves here.
167 'testcase/tstFileLock': '',
168 'testcase/tstDisasm-2': '', # without parameters it will disassembler 1GB starting from 0
169 'testcase/tstFileAppendWin-1': '',
170 'testcase/tstDir': '', # useless without parameters
171 'testcase/tstDir-2': '', # useless without parameters
172 'testcase/tstGlobalConfig': '',
173 'testcase/tstHostHardwareLinux': '', # must be killed with CTRL-C
174 'testcase/tstHttp': '', # Talks to outside servers.
175 'testcase/tstRTHttp': '', # parameters required
176 'testcase/tstLdr-2': '', # parameters required
177 'testcase/tstLdr-3': '', # parameters required
178 'testcase/tstLdr': '', # parameters required
179 'testcase/tstLdrLoad': '', # parameters required
180 'testcase/tstMove': '', # parameters required
181 'testcase/tstRTR0Timer': '', # loads 'tstRTR0Timer.r0'
182 'testcase/tstRTR0ThreadDriver': '', # loads 'tstRTR0Thread.r0'
183 'testcase/tstRunTestcases': '', # that's a script like this one
184 'testcase/tstRTReqPool': '', # fails sometimes, testcase buggy
185 'testcase/tstRTS3': '', # parameters required
186 'testcase/tstSDL': '', # graphics test
187 'testcase/tstSupLoadModule': '', # Needs parameters and vboxdrv access. Covered elsewhere.
188 'testcase/tstSeamlessX11': '', # graphics test
189 'testcase/tstTime-3': '', # parameters required
190 'testcase/tstVBoxControl': '', # works only inside a guest
191 'testcase/tstVDCopy': '', # parameters required
192 'testcase/tstVDFill': '', # parameters required
193 'tstAnimate': '', # parameters required
194 'testcase/tstAPI': '', # user interaction required
195 'tstCollector': '', # takes forever
196 'testcase/tstHeadless': '', # parameters required
197 'tstHeadless': '', # parameters required
198 'tstMicroRC': '', # required by tstMicro
199 'tstVBoxDbg': '', # interactive test
200 'testcase/tstTestServMgr': '', # some strange xpcom18a4 test, does not work
201 'tstTestServMgr': '', # some strange xpcom18a4 test, does not work
202 'tstPDMAsyncCompletion': '', # parameters required
203 'testcase/tstXptDump': '', # parameters required
204 'tstXptDump': '', # parameters required
205 'testcase/tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
206 'tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
207 'testcase/tstSimpleTypeLib': '', # parameters required
208 'tstSimpleTypeLib': '', # parameters required
209 'testcase/tstTestAtoms': '', # additional test file (words.txt) required
210 'tstTestAtoms': '', # additional test file (words.txt) required
211 'testcase/tstXptLink': '', # parameters required
212 'tstXptLink': '', # parameters required
213 'tstXPCOMCGlue': '', # user interaction required
214 'testcase/tstXPCOMCGlue': '', # user interaction required
215 'testcase/tstCAPIGlue': '', # user interaction required
216 'testcase/tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
217 'tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
218 'testcase/tstRTFilesystem': '', # parameters required
219 'testcase/tstRTDvm': '', # parameters required
220 'tstSSLCertDownloads': '', # Obsolete.
221 # later
222 'testcase/tstIntNetR0': '', # RTSPINLOCK_FLAGS_INTERRUPT_SAFE == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE
223 # slow stuff
224 'testcase/tstAvl': '', # SLOW!
225 'testcase/tstRTAvl': '', # SLOW! (new name)
226 'testcase/tstVD': '', # 8GB fixed-sized vmdk
227 # failed or hang
228 'testcase/tstCryptoPkcs7Verify': '', # hang
229 'tstOVF': '', # hang (only ancient version, now in new place)
230 'testcase/tstRTLockValidator': '', # Lock validation is not enabled for critical sections
231 'testcase/tstGuestControlSvc': '', # failed: line 288: testHost(&svcTable):
232 # expected VINF_SUCCESS, got VERR_NOT_FOUND
233 'testcase/tstRTMemEf': '', # failed w/o error message
234 'testcase/tstSupSem': '', # failed: SRE Timeout Accuracy (ms) : FAILED (1 errors)
235 'testcase/tstCryptoPkcs7Sign': '', # failed: 29330:
236 # error:02001002:lib(2):func(1):reason(2):NA:0:fopen('server.pem': '','r')
237 'testcase/tstCompressionBenchmark': '', # failed: error: RTZipBlockCompress failed
238 # for 'RTZipBlock/LZJB' (#4): VERR_NOT_SUPPORTED
239 'tstPDMAsyncCompletionStress': '', # VERR_INVALID_PARAMETER (cbSize = 0)
240 'tstMicro': '', # doesn't work on solaris, fix later if we care.
241 'tstVMM-HwAccm': '', # failed: Only checked AMD-V on linux
242 'tstVMM-HM': '', # failed: Only checked AMD-V on linux
243 'tstVMMFork': '', # failed: xtracker 6171
244 'tstTestFactory': '', # some strange xpcom18a4 test, does not work
245 'testcase/tstRTSemXRoads': '', # sporadically failed: Traffic - 8 threads per direction, 10 sec :
246 # FAILED (8 errors)
247 'tstVBoxAPILinux': '', # creates VirtualBox directories for root user because of sudo
248 # (should be in vbox)
249 'testcase/tstVMStructDTrace': '', # This is a D-script generator.
250 'tstVMStructRC': '', # This is a C-code generator.
251 'tstDeviceStructSizeRC': '', # This is a C-code generator.
252 'testcase/tstTSC': '', # Doesn't test anything and might fail with HT or/and too many cores.
253 'testcase/tstOpenUSBDev': '', # Not a useful testcase.
254 'testcase/tstX86-1': '', # Really more guest side.
255 'testcase/tstX86-FpuSaveRestore': '', # Experiments, could be useful for the guest not the host.
256 'tstAsmStructsRC': '', # Testcase run during build time (fails to find libstdc++.so.6 on some
257 # Solaris testboxes).
258 };
259
260 ## The permanent exclude list for ASAN builds because they trigger false-positives.
261 # @note Stripped of extensions!
262 kdTestCasesBlackListAsan = {
263 'testcase/tstVMMR0CallHost-1': '', # Triggers a stack overflow error on linux.amd64
264 }
265
266 # Suffix exclude list.
267 kasSuffixBlackList = [
268 '.r0',
269 '.gc',
270 '.debug',
271 '.rel',
272 '.sys',
273 '.ko',
274 '.o',
275 '.obj',
276 '.lib',
277 '.a',
278 '.so',
279 '.dll',
280 '.dylib',
281 '.tmp',
282 '.log',
283 '.py',
284 '.pyc',
285 '.pyo',
286 '.pdb',
287 '.dSYM',
288 '.sym',
289 '.template',
290 '.expected',
291 '.expect',
292 ];
293
294 # White list, which contains tests considered to be safe to execute,
295 # even on remote targets (guests).
296 #
297 # When --only-whitelist is specified, this is the only list being checked for.
298 kdTestCasesWhiteList = {
299 'testcase/tstFile': '',
300 'testcase/tstFileLock': '',
301 'testcase/tstClipboardMockHGCM': '', # Requires X on Linux OSes. Execute on remote targets only (guests).
302 'testcase/tstRTFsQueries': '',
303 'testcase/tstRTLocalIpc': '',
304 'testcase/tstRTPathQueryInfo': '',
305 'testcase/tstRTPipe': '',
306 'testcase/tstRTProcCreateEx': '',
307 'testcase/tstRTProcCreatePrf': '',
308 'testcase/tstRTProcIsRunningByName': '',
309 'testcase/tstRTProcQueryUsername': '',
310 'testcase/tstRTProcWait': '',
311 'testcase/tstRTSystemQueryFirmware': '',
312 'testcase/tstTime-2': '',
313 'testcase/tstTime-3': '',
314 'testcase/tstTime-4': '',
315 'testcase/tstTimer': '',
316 'testcase/tstThread-1': '',
317 'testcase/tstUtf8': ''
318 };
319
320 # Test dependency list -- libraries.
321 # Needed in order to execute testcases on remote targets which don't have a VBox installation present.
322 kdTestCaseDepsLibs = [
323 "VBoxRT"
324 ];
325
326 ## The exclude list.
327 # @note Stripped extensions!
328 kasHardened = [
329 "testcase/tstIntNet-1",
330 "testcase/tstR0ThreadPreemptionDriver", # VBox 4.3
331 "testcase/tstRTR0ThreadPreemptionDriver",
332 "testcase/tstRTR0MemUserKernelDriver",
333 "testcase/tstRTR0SemMutexDriver",
334 "testcase/tstRTR0TimerDriver",
335 "testcase/tstRTR0ThreadDriver",
336 'testcase/tstRTR0DbgKrnlInfoDriver',
337 "tstInt",
338 "tstPDMQueue", # Comment in testcase says its driverless, but it needs driver access.
339 "tstVMM",
340 "tstVMMFork",
341 "tstVMREQ",
342 'testcase/tstCFGM',
343 'testcase/tstContiguous',
344 'testcase/tstGetPagingMode',
345 'testcase/tstGIP-2',
346 'testcase/tstInit',
347 'testcase/tstLow',
348 'testcase/tstMMHyperHeap',
349 'testcase/tstPage',
350 'testcase/tstPin',
351 'testcase/tstRTTime', 'testcase/tstTime', # GIP test case.
352 'testcase/tstRTTime-2', 'testcase/tstTime-2', # GIP test case.
353 'testcase/tstRTTime-4', 'testcase/tstTime-4', # GIP test case.
354 'testcase/tstSSM',
355 'testcase/tstSupSem-Zombie',
356 ]
357
358 ## Argument lists
359 kdArguments = {
360 'testcase/tstbntest': [ '-out', os.devnull, ], # Very noisy.
361 };
362
363
364 ## Status code translations.
365 ## @{
366 kdExitCodeNames = {
367 0: 'RTEXITCODE_SUCCESS',
368 1: 'RTEXITCODE_FAILURE',
369 2: 'RTEXITCODE_SYNTAX',
370 3: 'RTEXITCODE_INIT',
371 4: 'RTEXITCODE_SKIPPED',
372 };
373 kdExitCodeNamesWin = {
374 -1073741515: 'STATUS_DLL_NOT_FOUND',
375 -1073741512: 'STATUS_ORDINAL_NOT_FOUND',
376 -1073741511: 'STATUS_ENTRYPOINT_NOT_FOUND',
377 -1073741502: 'STATUS_DLL_INIT_FAILED',
378 -1073741500: 'STATUS_UNHANDLED_EXCEPTION',
379 -1073741499: 'STATUS_APP_INIT_FAILURE',
380 -1073741819: 'STATUS_ACCESS_VIOLATION',
381 -1073741571: 'STATUS_STACK_OVERFLOW',
382 };
383 ## @}
384
385 def __init__(self):
386 """
387 Reinitialize child class instance.
388 """
389 vbox.TestDriver.__init__(self);
390
391 # We need to set a default test VM set here -- otherwise the test
392 # driver base class won't let us use the "--test-vms" switch.
393 #
394 # See the "--local" switch in self.parseOption().
395 self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
396
397 # Selected NIC attachment.
398 self.sNicAttachment = '';
399
400 # Session handling stuff.
401 # Only needed for remote tests executed by TxS.
402 self.oSession = None;
403 self.oTxsSession = None;
404
405 # The VirtualBox installation root directory.
406 self.sVBoxInstallRoot = None;
407
408 ## Testing mode being used:
409 # "local": Execute unit tests locally (same host, default).
410 # "remote": Executes unit tests right on the remote from a given source.
411 self.sMode = 'local';
412
413 self.cSkipped = 0;
414 self.cPassed = 0;
415 self.cFailed = 0;
416
417 ## The source directory where our unit tests live.
418 #
419 # For local mode this is our out/ or some staging directory and
420 # also acts the source for copying over the testcases to a remote target.
421 #
422 # For remote remote this is the ${CDROM} directory where we ship the included
423 # testcases on the Validation Kit ISO.
424 self.sUnitTestsPathSrc = None;
425
426 # The destination directory our unit tests live when being
427 # copied over to a remote target (via TxS).
428 self.sUnitTestsPathDst = None;
429
430 # The executable suffix to use for the executing the actual testcases.
431 # Will be re-set when executing the testcases on a remote (VM) once we know
432 # what type of suffix to use then (based on guest OS).
433 self.sExeSuff = base.exeSuff();
434
435 self.aiVBoxVer = (4, 3, 0, 0);
436
437 # For testing testcase logic.
438 self.fDryRun = False;
439 self.fOnlyWhiteList = False;
440
441 @staticmethod
442 def _sanitizePath(sPath):
443 """
444 Does a little bit of sanitizing a given path by removing quoting, if any.
445
446 This is needed because handed-in paths via command line arguments can contain variables like "${CDROM}"
447 which might need to get processed by TXS on the guest side first.
448
449 Returns the sanitized path.
450 """
451 if sPath is None: # Keep uninitialized strings as-is.
452 return None;
453 return sPath.strip('\"').strip('\'');
454
455 def _detectPaths(self):
456 """
457 Internal worker for actionVerify and actionExecute that detects paths.
458
459 This sets sVBoxInstallRoot and sUnitTestsPathBase and returns True/False.
460 """
461
462 reporter.log2('Detecting paths (mode is "%s") ...' % ("remot" if self.isRemoteMode() else "local",));
463
464 #
465 # We need a VBox install (/ build) to test.
466 #
467 if not self._detectBuild():
468 reporter.error('Unabled to detect the VBox build.');
469 return False;
470
471 #
472 # Where are the files installed?
473 # Solaris requires special handling because of it's multi arch subdirs.
474 #
475 if not self.sVBoxInstallRoot and self.isRemoteMode():
476 self.sVBoxInstallRoot = '${CDROM}/${OS}/${ARCH}';
477
478 elif not self.sVBoxInstallRoot:
479 self.sVBoxInstallRoot = self.oBuild.sInstallPath;
480 if not self.oBuild.isDevBuild() and utils.getHostOs() == 'solaris':
481 sArchDir = utils.getHostArch();
482 if sArchDir == 'x86': sArchDir = 'i386';
483 self.sVBoxInstallRoot = os.path.join(self.sVBoxInstallRoot, sArchDir);
484
485 ## @todo r=andy Make sure the install root really exists and is accessible.
486
487 # Add the installation root to the PATH on windows so we can get DLLs from it.
488 if utils.getHostOs() == 'win':
489 sPathName = 'PATH';
490 if not sPathName in os.environ:
491 sPathName = 'Path';
492 sPath = os.environ.get(sPathName, '.');
493 if sPath and sPath[-1] != ';':
494 sPath += ';';
495 os.environ[sPathName] = sPath + self.sVBoxInstallRoot + ';';
496 else:
497 reporter.log2('VBox installation root already set to "%s"' % (self.sVBoxInstallRoot));
498
499 reporter.log('VBox installation root path: %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.isRemoteMode():
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
548 reporter.log('Unit test source dir path: %s' % (self.sUnitTestsPathSrc,));
549
550 self.sUnitTestsPathSrc = self._sanitizePath(self.sUnitTestsPathSrc);
551
552 return True;
553
554 #
555 # Overridden methods.
556 #
557
558 def showUsage(self):
559 """
560 Shows the testdriver usage.
561 """
562 fRc = vbox.TestDriver.showUsage(self);
563 reporter.log('');
564 reporter.log('Unit Test #1 options:');
565 reporter.log(' --dryrun');
566 reporter.log(' Performs a dryrun (no tests being executed).');
567 reporter.log(' --mode <local|remote>');
568 reporter.log(' Specifies the test execution mode:');
569 reporter.log(' local: Locally on the same machine.');
570 reporter.log(' remote: 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',):
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.isRemoteMode() else '',));
733 reporter.log('VBox install root: %s %s'
734 % (self.sVBoxInstallRoot, '(on remote)' if self.isRemoteMode() 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.isRemoteMode():
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 fHardened = sName in self.kasHardened and self.sUnitTestsPathSrc != self.sVBoxInstallRoot;
1022 asFilesToRemove = []; # Stuff to clean up.
1023 asDirsToRemove = []; # Ditto.
1024
1025 if fHardened or self.isRemoteMode():
1026 if self.isRemoteMode():
1027 sDstDir = os.path.join(self.sUnitTestsPathDst, sTestCaseSubDir);
1028 else:
1029 sDstDir = os.path.join(self.sVBoxInstallRoot, sTestCaseSubDir);
1030 if not self._wrapPathExists(sDstDir):
1031 self._wrapMkDir(sDstDir);
1032 asDirsToRemove.append(sDstDir);
1033
1034 sSrc = sFilePathAbs;
1035 # If the testcase source does not exist for whatever reason, just mark it as skipped
1036 # instead of reporting an error.
1037 if not self._wrapPathExists(sSrc):
1038 self.cSkipped += 1;
1039 return True;
1040
1041 sDst = os.path.join(sDstDir, os.path.basename(sFilePathAbs));
1042 fModeExe = 0;
1043 fModeDeps = 0;
1044 if not oTestVm or (oTestVm and not oTestVm.isWindows()): ## @todo NT4 does not like the chmod. Investigate this!
1045 fModeExe = 0o755;
1046 fModeDeps = 0o644;
1047 self._wrapCopyFile(sSrc, sDst, fModeExe);
1048 asFilesToRemove.append(sDst);
1049
1050 # Copy required dependencies to destination.
1051 # Note! The testcases are statically linked, so there are no VBoxRT.dll/so/dylib
1052 # to copy here. This code can be currently be ignored.
1053 if self.isRemoteMode():
1054 for sLib in self.kdTestCaseDepsLibs:
1055 for sSuff in [ '.dll', '.so', '.dylib' ]:
1056 assert self.sVBoxInstallRoot is not None;
1057 sSrc = os.path.join(self.sVBoxInstallRoot, sLib + sSuff);
1058 if self._wrapPathExists(sSrc):
1059 sDst = os.path.join(sDstDir, os.path.basename(sSrc));
1060 self._wrapCopyFile(sSrc, sDst, fModeDeps);
1061 asFilesToRemove.append(sDst);
1062
1063 ## @todo r=bird: The next two are checks for _local_ files matching the remote path when in remote-mode.
1064 ## It makes for very confusing reading and is a potential for trouble.
1065
1066 # Copy any associated .dll/.so/.dylib.
1067 for sSuff in [ '.dll', '.so', '.dylib' ]:
1068 sSrc = os.path.splitext(sFilePathAbs)[0] + sSuff;
1069 if os.path.exists(sSrc):
1070 sDst = os.path.join(sDstDir, os.path.basename(sSrc));
1071 self._wrapCopyFile(sSrc, sDst, fModeDeps);
1072 asFilesToRemove.append(sDst);
1073
1074 # Copy any associated .r0, .rc and .gc modules.
1075 offDriver = sFilePathAbs.rfind('Driver')
1076 if offDriver > 0:
1077 for sSuff in [ '.r0', 'RC.rc', 'RC.gc' ]:
1078 sSrc = sFilePathAbs[:offDriver] + sSuff;
1079 if os.path.exists(sSrc):
1080 sDst = os.path.join(sDstDir, os.path.basename(sSrc));
1081 self._wrapCopyFile(sSrc, sDst, fModeDeps);
1082 asFilesToRemove.append(sDst);
1083
1084 sFilePathAbs = os.path.join(sDstDir, os.path.basename(sFilePathAbs));
1085
1086 #
1087 # Set up arguments.
1088 #
1089 asArgs = [sFilePathAbs,]
1090 if sName in self.kdArguments:
1091 asArgs.extend(self.kdArguments[sName]);
1092
1093 #
1094 # Set up the environment.
1095 #
1096 # - We set IPRT_TEST_OMIT_TOP_TEST to avoid the unnecessary top-test
1097 # entry when running the inner tests, as it'll just add an unnecessary
1098 # result nesting.
1099 #
1100 # - IPRT_TEST_FILE is set to a result.xml file when running locally.
1101 # This is not necessary when executing via TxS as it sets IPRT_TEST_PIPE,
1102 # which overrides IPRT_TEST_FILE, to collect the XML output.
1103 #
1104 dEnvChanges = {
1105 'IPRT_TEST_OMIT_TOP_TEST': '1',
1106 };
1107
1108 sXmlFile = os.path.join(self.sUnitTestsPathDst, 'result.xml') if not self.isRemoteMode() else None;
1109 if sXmlFile:
1110 dEnvChanges['IPRT_TEST_FILE'] = sXmlFile;
1111 if self._wrapPathExists(sXmlFile):
1112 try: os.unlink(sXmlFile);
1113 except: self._wrapDeleteFile(sXmlFile);
1114
1115 #
1116 # Execute the test case.
1117 #
1118 # Windows is confusing output. Trying a few things to get rid of this.
1119 # First, flush both stderr and stdout before running the child. Second,
1120 # assign the child stderr to stdout. If this doesn't help, we'll have
1121 # to capture the child output.
1122 #
1123 reporter.log('*** Executing %s%s...' % (asArgs, ' [hardened]' if fHardened else ''));
1124 try: sys.stdout.flush();
1125 except: pass;
1126 try: sys.stderr.flush();
1127 except: pass;
1128
1129 iRc = 0;
1130
1131 if not self.fDryRun:
1132 if self.isRemoteMode():
1133 asRemoteEnvChg = ['%s=%s' % (sKey, sValue) for sKey, sValue in utils.iteritems(dEnvChanges)];
1134
1135 fRc = self.txsRunTest(self.oTxsSession, sName, cMsTimeout = 30 * 60 * 1000, sExecName = asArgs[0],
1136 asArgs = asArgs, asAddEnv = asRemoteEnvChg, fCheckSessionStatus = True);
1137 if fRc:
1138 iRc = 0;
1139 else:
1140 (_, sOpcode, abPayload) = self.oTxsSession.getLastReply();
1141 if sOpcode.startswith('PROC NOK '): # Extract process rc.
1142 iRc = abPayload[0]; # ASSUMES 8-bit rc for now.
1143 if iRc == 0: # Might happen if the testcase misses some dependencies. Set it to -42 then.
1144 iRc = -42;
1145 else:
1146 iRc = -1; ## @todo
1147 else:
1148 for sKey, sValue in utils.iteritems(dEnvChanges):
1149 os.environ[sKey] = sValue;
1150
1151 oChild = None;
1152 try:
1153 if fHardened:
1154 oChild = utils.sudoProcessPopen(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
1155 else:
1156 oChild = utils.processPopenSafe(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
1157 except:
1158 if sName in [ 'tstAsmStructsRC', # 32-bit, may fail to start on 64-bit linux. Just ignore.
1159 ]:
1160 reporter.logXcpt();
1161 fSkipped = True;
1162 else:
1163 reporter.errorXcpt();
1164 iRc = 1023;
1165 oChild = None;
1166
1167 if oChild is not None:
1168 self.pidFileAdd(oChild.pid, sName, fSudo = fHardened);
1169 iRc = oChild.wait();
1170 self.pidFileRemove(oChild.pid);
1171 #
1172 # Clean up
1173 #
1174 for sPath in asFilesToRemove:
1175 self._wrapDeleteFile(sPath);
1176 for sPath in asDirsToRemove:
1177 self._wrapRemoveDir(sPath);
1178
1179 #
1180 # Report (sXmlFile is None when in remote mode).
1181 #
1182 if sXmlFile and os.path.exists(sXmlFile):
1183 reporter.addSubXmlFile(sXmlFile);
1184 if fHardened:
1185 self._wrapDeleteFile(sXmlFile);
1186 else:
1187 os.unlink(sXmlFile);
1188
1189 if iRc == 0:
1190 reporter.log('*** %s: exit code %d' % (sFilePathAbs, iRc));
1191 self.cPassed += 1;
1192
1193 elif iRc == 4: # RTEXITCODE_SKIPPED
1194 reporter.log('*** %s: exit code %d (RTEXITCODE_SKIPPED)' % (sFilePathAbs, iRc));
1195 fSkipped = True;
1196 self.cSkipped += 1;
1197
1198 elif fSkipped:
1199 reporter.log('*** %s: exit code %d (Skipped)' % (sFilePathAbs, iRc));
1200 self.cSkipped += 1;
1201
1202 else:
1203 sName = self.kdExitCodeNames.get(iRc, '');
1204 if iRc in self.kdExitCodeNamesWin and utils.getHostOs() == 'win':
1205 sName = self.kdExitCodeNamesWin[iRc];
1206 if sName != '':
1207 sName = ' (%s)' % (sName);
1208
1209 if iRc != 1:
1210 reporter.testFailure('Exit status: %d%s' % (iRc, sName));
1211 reporter.log( '!*! %s: exit code %d%s' % (sFilePathAbs, iRc, sName));
1212 else:
1213 reporter.error('!*! %s: exit code %d%s' % (sFilePathAbs, iRc, sName));
1214 self.cFailed += 1;
1215
1216 return fSkipped;
1217
1218 def _testRunUnitTestsSet(self, oTestVm, sTestCasePattern, sTestCaseSubDir):
1219 """
1220 Run subset of the unit tests set.
1221 """
1222
1223 # Open /dev/null for use as stdin further down.
1224 try:
1225 oDevNull = open(os.path.devnull, 'w+'); # pylint: disable=consider-using-with,unspecified-encoding
1226 except:
1227 oDevNull = None;
1228
1229 # Determin the host OS specific exclusion lists.
1230 dTestCasesBuggyForHostOs = self.kdTestCasesBuggyPerOs.get(utils.getHostOs(), []);
1231 dTestCasesBuggyForHostOs.update(self.kdTestCasesBuggyPerOs.get(utils.getHostOsDotArch(), []));
1232
1233 ## @todo Add filtering for more specific OSes (like OL server, doesn't have X installed) by adding a separate
1234 # black list + using utils.getHostOsVersion().
1235
1236 #
1237 # Process the file list and run everything looking like a testcase.
1238 #
1239 if not self.fOnlyWhiteList:
1240 if not self.isRemoteMode():
1241 asFiles = sorted(os.listdir(os.path.join(self.sUnitTestsPathSrc, sTestCaseSubDir)));
1242 else: # 'remote'
1243 ## @todo Implement remote file enumeration / directory listing.
1244 reporter.error('Sorry, no remote file enumeration implemented yet!\nUse --only-whitelist instead.');
1245 return;
1246 else:
1247 # Transform our dict into a list, where the keys are the list elements.
1248 asFiles = list(self.kdTestCasesWhiteList.keys());
1249 # Make sure to only keep the list item's base name so that the iteration down below works
1250 # with our white list without any additional modification.
1251 asFiles = [os.path.basename(s) for s in asFiles];
1252
1253 for sFilename in asFiles:
1254 # When executing in remote execution mode, make sure to append the executable suffix here, as
1255 # the (white / black) lists do not contain any OS-specific executable suffixes.
1256 if self.isRemoteMode():
1257 sFilename = sFilename + self.sExeSuff;
1258 # Separate base and suffix and morph the base into something we
1259 # can use for reporting and array lookups.
1260 sBaseName = os.path.basename(sFilename);
1261 sName, sSuffix = os.path.splitext(sBaseName);
1262 if sTestCaseSubDir != '.':
1263 sName = sTestCaseSubDir + '/' + sName;
1264
1265 reporter.log2('sTestCasePattern=%s, sBaseName=%s, sName=%s, sSuffix=%s, sFileName=%s'
1266 % (sTestCasePattern, sBaseName, sName, sSuffix, sFilename,));
1267
1268 # Process white list first, if set.
1269 if self.fOnlyWhiteList \
1270 and not self._isExcluded(sName, self.kdTestCasesWhiteList):
1271 # (No testStart/Done or accounting here!)
1272 reporter.log('%s: SKIPPED (not in white list)' % (sName,));
1273 continue;
1274
1275 # Basic exclusion.
1276 if not re.match(sTestCasePattern, sBaseName) \
1277 or sSuffix in self.kasSuffixBlackList:
1278 reporter.log2('"%s" is not a test case.' % (sName,));
1279 continue;
1280
1281 # When not only processing the white list, do some more checking first.
1282 if not self.fOnlyWhiteList:
1283 # Check if the testcase is black listed or buggy before executing it.
1284 if self._isExcluded(sName, self.kdTestCasesBlackList):
1285 # (No testStart/Done or accounting here!)
1286 reporter.log('%s: SKIPPED (blacklisted)' % (sName,));
1287 continue;
1288
1289 if self._isExcluded(sName, self.kdTestCasesBuggy):
1290 reporter.testStart(sName);
1291 reporter.log('%s: Skipping, buggy in general.' % (sName,));
1292 reporter.testDone(fSkipped = True);
1293 self.cSkipped += 1;
1294 continue;
1295
1296 # Some testcases don't work with ASAN.
1297 if self.getBuildType() == 'asan' \
1298 and self._isExcluded(sName, self.kdTestCasesBlackListAsan):
1299 # (No testStart/Done or accounting here!)
1300 reporter.log('%s: SKIPPED (blacklisted ASAN)' % (sName,));
1301 continue;
1302
1303 if self._isExcluded(sName, dTestCasesBuggyForHostOs):
1304 reporter.testStart(sName);
1305 reporter.log('%s: Skipping, buggy on %s.' % (sName, utils.getHostOs(),));
1306 reporter.testDone(fSkipped = True);
1307 self.cSkipped += 1;
1308 continue;
1309 else:
1310 # Passed the white list check already above.
1311 pass;
1312
1313 sFilePathAbs = os.path.normpath(os.path.join(self.sUnitTestsPathSrc, os.path.join(sTestCaseSubDir, sFilename)));
1314 reporter.log2('sFilePathAbs=%s\n' % (sFilePathAbs,));
1315 reporter.testStart(sName);
1316 try:
1317 fSkipped = self._executeTestCase(oTestVm, sName, sFilePathAbs, sTestCaseSubDir, oDevNull);
1318 except:
1319 reporter.errorXcpt('!*!');
1320 self.cFailed += 1;
1321 fSkipped = False;
1322 reporter.testDone(fSkipped);
1323
1324
1325if __name__ == '__main__':
1326 sys.exit(tdUnitTest1().main(sys.argv))
Note: See TracBrowser for help on using the repository browser.

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