VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testdriver/base.py@ 63884

Last change on this file since 63884 was 62795, checked in by vboxsync, 8 years ago

testdriver/base.py: Set TMPDIR & IPRT_TMPDIR on all platforms, and TMP and TEMP on DOS descendants.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: base.py 62795 2016-08-01 09:03:19Z vboxsync $
3# pylint: disable=C0302
4
5"""
6Base testdriver module.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2016 Oracle Corporation
12
13This file is part of VirtualBox Open Source Edition (OSE), as
14available from http://www.virtualbox.org. This file is free software;
15you can redistribute it and/or modify it under the terms of the GNU
16General Public License (GPL) as published by the Free Software
17Foundation, in version 2 as it comes in the "COPYING" file of the
18VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20
21The contents of this file may alternatively be used under the terms
22of the Common Development and Distribution License Version 1.0
23(CDDL) only, as it comes in the "COPYING.CDDL" file of the
24VirtualBox OSE distribution, in which case the provisions of the
25CDDL are applicable instead of those of the GPL.
26
27You may elect to license modified versions of this file under the
28terms and conditions of either the GPL or the CDDL or both.
29"""
30__version__ = "$Revision: 62795 $"
31
32
33# Standard Python imports.
34import os
35import os.path
36import signal
37import socket
38import stat
39import subprocess
40import sys
41import time
42import thread
43import threading
44import traceback
45import tempfile;
46import unittest;
47
48# Validation Kit imports.
49from common import utils;
50from common.constants import rtexitcode;
51from testdriver import reporter;
52if sys.platform == 'win32':
53 from testdriver import winbase;
54
55# Figure where we are.
56try: __file__
57except: __file__ = sys.argv[0];
58g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
59
60
61#
62# Some utility functions.
63#
64
65def exeSuff():
66 """
67 Returns the executable suffix.
68 """
69 if os.name == 'nt' or os.name == 'os2':
70 return '.exe';
71 return '';
72
73def searchPath(sExecName):
74 """
75 Searches the PATH for the specified executable name, returning the first
76 existing file/directory/whatever. The return is abspath'ed.
77 """
78 sSuff = exeSuff();
79
80 sPath = os.getenv('PATH', os.getenv('Path', os.path.defpath));
81 aPaths = sPath.split(os.path.pathsep)
82 for sDir in aPaths:
83 sFullExecName = os.path.join(sDir, sExecName);
84 if os.path.exists(sFullExecName):
85 return os.path.abspath(sFullExecName);
86 sFullExecName += sSuff;
87 if os.path.exists(sFullExecName):
88 return os.path.abspath(sFullExecName);
89 return sExecName;
90
91def getEnv(sVar, sLocalAlternative = None):
92 """
93 Tries to get an environment variable, optionally with a local run alternative.
94 Will raise an exception if sLocalAlternative is None and the variable is
95 empty or missing.
96 """
97 try:
98 sVal = os.environ.get(sVar, None);
99 if sVal is None:
100 raise GenError('environment variable "%s" is missing' % (sVar));
101 if sVal == "":
102 raise GenError('environment variable "%s" is empty' % (sVar));
103 except:
104 if sLocalAlternative is None or not reporter.isLocal():
105 raise
106 sVal = sLocalAlternative;
107 return sVal;
108
109def getDirEnv(sVar, sAlternative = None, fLocalReq = False, fTryCreate = False):
110 """
111 Tries to get an environment variable specifying a directory path.
112
113 Resolves it into an absolute path and verifies its existance before
114 returning it.
115
116 If the environment variable is empty or isn't set, or if the directory
117 doesn't exist or isn't a directory, sAlternative is returned instead.
118 If sAlternative is None, then we'll raise a GenError. For local runs we'll
119 only do this if fLocalReq is True.
120 """
121 assert sAlternative is None or fTryCreate is False;
122 try:
123 sVal = os.environ.get(sVar, None);
124 if sVal is None:
125 raise GenError('environment variable "%s" is missing' % (sVar));
126 if sVal == "":
127 raise GenError('environment variable "%s" is empty' % (sVar));
128
129 sVal = os.path.abspath(sVal);
130 if not os.path.isdir(sVal):
131 if not fTryCreate or os.path.exists(sVal):
132 reporter.error('the value of env.var. "%s" is not a dir: "%s"' % (sVar, sVal));
133 raise GenError('the value of env.var. "%s" is not a dir: "%s"' % (sVar, sVal));
134 try:
135 os.makedirs(sVal, 0700);
136 except:
137 reporter.error('makedirs failed on the value of env.var. "%s": "%s"' % (sVar, sVal));
138 raise GenError('makedirs failed on the value of env.var. "%s": "%s"' % (sVar, sVal));
139 except:
140 if sAlternative is None:
141 if reporter.isLocal() and fLocalReq:
142 raise;
143 sVal = None;
144 else:
145 sVal = os.path.abspath(sAlternative);
146 return sVal;
147
148def timestampMilli():
149 """
150 Gets a millisecond timestamp.
151 """
152 if sys.platform == 'win32':
153 return long(time.clock() * 1000);
154 return long(time.time() * 1000);
155
156def timestampNano():
157 """
158 Gets a nanosecond timestamp.
159 """
160 if sys.platform == 'win32':
161 return long(time.clock() * 1000000000);
162 return long(time.time() * 1000000000);
163
164def tryGetHostByName(sName):
165 """
166 Wrapper around gethostbyname.
167 """
168 if sName is not None:
169 try:
170 sIpAddr = socket.gethostbyname(sName);
171 except:
172 reporter.errorXcpt('gethostbyname(%s)' % (sName));
173 else:
174 if sIpAddr != '0.0.0.0':
175 sName = sIpAddr;
176 else:
177 reporter.error('gethostbyname(%s) -> %s' % (sName, sIpAddr));
178 return sName;
179
180def processInterrupt(uPid):
181 """
182 Sends a SIGINT or equivalent to interrupt the specified process.
183 Returns True on success, False on failure.
184
185 On Windows hosts this may not work unless the process happens to be a
186 process group leader.
187 """
188 if sys.platform == 'win32':
189 fRc = winbase.processInterrupt(uPid)
190 else:
191 try:
192 os.kill(uPid, signal.SIGINT);
193 fRc = True;
194 except:
195 reporter.logXcpt('uPid=%s' % (uPid,));
196 fRc = False;
197 return fRc;
198
199def sendUserSignal1(uPid):
200 """
201 Sends a SIGUSR1 or equivalent to nudge the process into shutting down
202 (VBoxSVC) or something.
203 Returns True on success, False on failure or if not supported (win).
204
205 On Windows hosts this may not work unless the process happens to be a
206 process group leader.
207 """
208 if sys.platform == 'win32':
209 fRc = False;
210 else:
211 try:
212 os.kill(uPid, signal.SIGUSR1); # pylint: disable=E1101
213 fRc = True;
214 except:
215 reporter.logXcpt('uPid=%s' % (uPid,));
216 fRc = False;
217 return fRc;
218
219def processTerminate(uPid):
220 """
221 Terminates the process in a nice manner (SIGTERM or equivalent).
222 Returns True on success, False on failure (logged).
223 """
224 fRc = False;
225 if sys.platform == 'win32':
226 fRc = winbase.processTerminate(uPid);
227 else:
228 try:
229 os.kill(uPid, signal.SIGTERM);
230 fRc = True;
231 except:
232 reporter.logXcpt('uPid=%s' % (uPid,));
233 return fRc;
234
235def processKill(uPid):
236 """
237 Terminates the process with extreme prejudice (SIGKILL).
238 Returns True on success, False on failure.
239 """
240 fRc = False;
241 if sys.platform == 'win32':
242 fRc = winbase.processKill(uPid);
243 else:
244 try:
245 os.kill(uPid, signal.SIGKILL); # pylint: disable=E1101
246 fRc = True;
247 except:
248 reporter.logXcpt('uPid=%s' % (uPid,));
249 return fRc;
250
251def processKillWithNameCheck(uPid, sName):
252 """
253 Like processKill(), but checks if the process name matches before killing
254 it. This is intended for killing using potentially stale pid values.
255
256 Returns True on success, False on failure.
257 """
258
259 if processCheckPidAndName(uPid, sName) is not True:
260 return False;
261 return processKill(uPid);
262
263
264def processExists(uPid):
265 """
266 Checks if the specified process exits.
267 This will only work if we can signal/open the process.
268
269 Returns True if it positively exists, False otherwise.
270 """
271 if sys.platform == 'win32':
272 fRc = winbase.processExists(uPid);
273 else:
274 try:
275 os.kill(uPid, 0);
276 fRc = True;
277 except:
278 reporter.logXcpt('uPid=%s' % (uPid,));
279 fRc = False;
280 return fRc;
281
282def processCheckPidAndName(uPid, sName):
283 """
284 Checks if a process PID and NAME matches.
285 """
286 if sys.platform == 'win32':
287 fRc = winbase.processCheckPidAndName(uPid, sName);
288 else:
289 if sys.platform in ('linux2', ):
290 asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
291 elif sys.platform in ('sunos5',):
292 asPsCmd = ['/usr/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
293 elif sys.platform in ('darwin',):
294 asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'ucomm='];
295 else:
296 asPsCmd = None;
297
298 if asPsCmd is not None:
299 try:
300 oPs = subprocess.Popen(asPsCmd, stdout=subprocess.PIPE);
301 sCurName = oPs.communicate()[0];
302 iExitCode = oPs.wait();
303 except:
304 reporter.logXcpt();
305 return False;
306
307 # ps fails with non-zero exit code if the pid wasn't found.
308 if iExitCode is not 0:
309 return False;
310 if sCurName is None:
311 return False;
312 sCurName = sCurName.strip();
313 if sCurName is '':
314 return False;
315
316 if os.path.basename(sName) == sName:
317 sCurName = os.path.basename(sCurName);
318 elif os.path.basename(sCurName) == sCurName:
319 sName = os.path.basename(sName);
320
321 if sCurName != sName:
322 return False;
323
324 fRc = True;
325 return fRc;
326
327
328
329#
330# Classes
331#
332
333class GenError(Exception):
334 """
335 Exception class which only purpose it is to allow us to only catch our own
336 exceptions. Better design later.
337 """
338
339 def __init__(self, sWhat = "whatever"):
340 Exception.__init__(self);
341 self.sWhat = sWhat
342
343 def str(self):
344 """Get the message string."""
345 return self.sWhat;
346
347
348class InvalidOption(GenError):
349 """
350 Exception thrown by TestDriverBase.parseOption(). It contains the error message.
351 """
352 def __init__(self, sWhat):
353 GenError.__init__(self, sWhat);
354
355
356class QuietInvalidOption(GenError):
357 """
358 Exception thrown by TestDriverBase.parseOption(). Error already printed, just
359 return failure.
360 """
361 def __init__(self):
362 GenError.__init__(self, "");
363
364
365class TdTaskBase(object):
366 """
367 The base task.
368 """
369
370 def __init__(self, sCaller):
371 self.sDbgCreated = '%s: %s' % (utils.getTimePrefix(), sCaller);
372 self.fSignalled = False;
373 self.__oRLock = threading.RLock();
374 self.oCv = threading.Condition(self.__oRLock);
375 self.oOwner = None;
376 self.msStart = timestampMilli();
377 self.oLocker = None;
378
379 def __del__(self):
380 """In case we need it later on."""
381 pass;
382
383 def toString(self):
384 """
385 Stringifies the object, mostly as a debug aid.
386 """
387 return '<%s: fSignalled=%s, __oRLock=%s, oCv=%s, oOwner=%s, oLocker=%s, msStart=%s, sDbgCreated=%s>' \
388 % (type(self).__name__, self.fSignalled, self.__oRLock, self.oCv, repr(self.oOwner), self.oLocker, self.msStart,
389 self.sDbgCreated,);
390
391 def __str__(self):
392 return self.toString();
393
394 def lockTask(self):
395 """ Wrapper around oCv.acquire(). """
396 if True is True: # change to False for debugging deadlocks.
397 self.oCv.acquire();
398 else:
399 msStartWait = timestampMilli();
400 while self.oCv.acquire(0) is False:
401 if timestampMilli() - msStartWait > 30*1000:
402 reporter.error('!!! timed out waiting for %s' % (self, ));
403 traceback.print_stack();
404 reporter.logAllStacks()
405 self.oCv.acquire();
406 break;
407 time.sleep(0.5);
408 self.oLocker = thread.get_ident()
409 return None;
410
411 def unlockTask(self):
412 """ Wrapper around oCv.release(). """
413 self.oLocker = None;
414 self.oCv.release();
415 return None;
416
417 def getAgeAsMs(self):
418 """
419 Returns the number of milliseconds the task has existed.
420 """
421 return timestampMilli() - self.msStart;
422
423 def setTaskOwner(self, oOwner):
424 """
425 Sets or clears the task owner. (oOwner can be None.)
426
427 Returns the previous owner, this means None if not owned.
428 """
429 self.lockTask();
430 oOldOwner = self.oOwner;
431 self.oOwner = oOwner;
432 self.unlockTask();
433 return oOldOwner;
434
435 def signalTaskLocked(self):
436 """
437 Variant of signalTask that can be called while owning the lock.
438 """
439 fOld = self.fSignalled;
440 if not fOld:
441 reporter.log2('signalTaskLocked(%s)' % (self,));
442 self.fSignalled = True;
443 self.oCv.notifyAll()
444 if self.oOwner is not None:
445 self.oOwner.notifyAboutReadyTask(self);
446 return fOld;
447
448 def signalTask(self):
449 """
450 Signals the task, internal use only.
451
452 Returns the previous state.
453 """
454 self.lockTask();
455 fOld = self.signalTaskLocked();
456 self.unlockTask();
457 return fOld
458
459 def resetTaskLocked(self):
460 """
461 Variant of resetTask that can be called while owning the lock.
462 """
463 fOld = self.fSignalled;
464 self.fSignalled = False;
465 return fOld;
466
467 def resetTask(self):
468 """
469 Resets the task signal, internal use only.
470
471 Returns the previous state.
472 """
473 self.lockTask();
474 fOld = self.resetTaskLocked();
475 self.unlockTask();
476 return fOld
477
478 def pollTask(self, fLocked = False):
479 """
480 Poll the signal status of the task.
481 Returns True if signalled, False if not.
482
483 Override this method.
484 """
485 if not fLocked:
486 self.lockTask();
487 fState = self.fSignalled;
488 if not fLocked:
489 self.unlockTask();
490 return fState
491
492 def waitForTask(self, cMsTimeout = 0):
493 """
494 Waits for the task to be signalled.
495
496 Returns True if the task is/became ready before the timeout expired.
497 Returns False if the task is still not after cMsTimeout have elapsed.
498
499 Overriable.
500 """
501 self.lockTask();
502
503 fState = self.pollTask(True);
504 if not fState:
505 # Don't wait more than 1s. This allow lazy state polling.
506 msStart = timestampMilli();
507 while not fState:
508 cMsElapsed = timestampMilli() - msStart;
509 if cMsElapsed >= cMsTimeout:
510 break;
511
512 cMsWait = cMsTimeout - cMsElapsed
513 if cMsWait > 1000:
514 cMsWait = 1000;
515 try:
516 self.oCv.wait(cMsWait / 1000.0);
517 except:
518 pass;
519 reporter.doPollWork('TdTaskBase.waitForTask');
520 fState = self.pollTask(True);
521
522 self.unlockTask();
523 return fState;
524
525
526class Process(TdTaskBase):
527 """
528 Child Process.
529 """
530
531 def __init__(self, sName, asArgs, uPid, hWin = None, uTid = None):
532 TdTaskBase.__init__(self, utils.getCallerName());
533 self.sName = sName;
534 self.asArgs = asArgs;
535 self.uExitCode = -127;
536 self.uPid = uPid;
537 self.hWin = hWin;
538 self.uTid = uTid;
539 self.sKindCrashReport = None;
540 self.sKindCrashDump = None;
541
542 def toString(self):
543 return '<%s uExitcode=%s, uPid=%s, sName=%s, asArgs=%s, hWin=%s, uTid=%s>' \
544 % (TdTaskBase.toString(self), self.uExitCode, self.uPid, self.sName, self.asArgs, self.hWin, self.uTid);
545
546 #
547 # Instantiation methods.
548 #
549
550 @staticmethod
551 def spawn(sName, *asArgsIn):
552 """
553 Similar to os.spawnl(os.P_NOWAIT,).
554
555 """
556 # Make argument array (can probably use asArgsIn directly, but wtf).
557 asArgs = [];
558 for sArg in asArgsIn:
559 asArgs.append(sArg);
560
561 # Special case: Windows.
562 if sys.platform == 'win32':
563 (uPid, hProcess, uTid) = winbase.processCreate(searchPath(sName), asArgs);
564 if uPid == -1:
565 return None;
566 return Process(sName, asArgs, uPid, hProcess, uTid);
567
568 # Unixy.
569 try:
570 uPid = os.spawnv(os.P_NOWAIT, sName, asArgs);
571 except:
572 reporter.logXcpt('sName=%s' % (sName,));
573 return None;
574 return Process(sName, asArgs, uPid);
575
576 @staticmethod
577 def spawnp(sName, *asArgsIn):
578 """
579 Similar to os.spawnlp(os.P_NOWAIT,).
580
581 """
582 return Process.spawn(searchPath(sName), *asArgsIn);
583
584 #
585 # Task methods
586 #
587
588 def pollTask(self, fLocked = False):
589 """
590 Overridden pollTask method.
591 """
592 if not fLocked:
593 self.lockTask();
594
595 fRc = self.fSignalled;
596 if not fRc:
597 if sys.platform == 'win32':
598 if winbase.processPollByHandle(self.hWin):
599 try:
600 (uPid, uStatus) = os.waitpid(self.hWin, 0);
601 if uPid == self.hWin or uPid == self.uPid:
602 self.hWin = None; # waitpid closed it, so it's now invalid.
603 uPid = self.uPid;
604 except:
605 reporter.logXcpt();
606 uPid = self.uPid;
607 uStatus = 0xffffffff;
608 else:
609 uPid = 0;
610 uStatus = 0; # pylint: disable=redefined-variable-type
611 else:
612 try:
613 (uPid, uStatus) = os.waitpid(self.uPid, os.WNOHANG); # pylint: disable=E1101
614 except:
615 reporter.logXcpt();
616 uPid = self.uPid;
617 uStatus = 0xffffffff;
618
619 # Got anything?
620 if uPid == self.uPid:
621 self.uExitCode = uStatus;
622 reporter.log('Process %u -> %u (%#x)' % (uPid, uStatus, uStatus));
623 self.signalTaskLocked();
624 if self.uExitCode != 0 and (self.sKindCrashReport is not None or self.sKindCrashDump is not None):
625 reporter.error('Process "%s" returned/crashed with a non-zero status code!! rc=%u sig=%u%s (raw=%#x)'
626 % ( self.sName, self.uExitCode >> 8, self.uExitCode & 0x7f,
627 ' w/ core' if self.uExitCode & 0x80 else '', self.uExitCode))
628 utils.processCollectCrashInfo(self.uPid, reporter.log, self._addCrashFile);
629
630 fRc = self.fSignalled;
631 if not fLocked:
632 self.unlockTask();
633 return fRc;
634
635 def _addCrashFile(self, sFile, fBinary):
636 """
637 Helper for adding a crash report or dump to the test report.
638 """
639 sKind = self.sKindCrashDump if fBinary else self.sKindCrashReport;
640 if sKind is not None:
641 reporter.addLogFile(sFile, sKind);
642 return None;
643
644
645 #
646 # Methods
647 #
648
649 def enableCrashReporting(self, sKindCrashReport, sKindCrashDump):
650 """
651 Enabling (or disables) automatic crash reporting on systems where that
652 is possible. The two file kind parameters are on the form
653 'crash/log/client' and 'crash/dump/client'. If both are None,
654 reporting will be disabled.
655 """
656 self.sKindCrashReport = sKindCrashReport;
657 self.sKindCrashDump = sKindCrashDump;
658 return True;
659
660 def isRunning(self):
661 """
662 Returns True if the process is still running, False if not.
663 """
664 return not self.pollTask();
665
666 def wait(self, cMsTimeout = 0):
667 """
668 Wait for the process to exit.
669
670 Returns True if the process exited withint the specified wait period.
671 Returns False if still running.
672 """
673 return self.waitForTask(cMsTimeout);
674
675 def getExitCode(self):
676 """
677 Returns the exit code of the process.
678 The process must have exited or the result will be wrong.
679 """
680 if self.isRunning():
681 return -127;
682 return self.uExitCode >> 8;
683
684 def interrupt(self):
685 """
686 Sends a SIGINT or equivalent to interrupt the process.
687 Returns True on success, False on failure.
688
689 On Windows hosts this may not work unless the process happens to be a
690 process group leader.
691 """
692 if sys.platform == 'win32':
693 return winbase.postThreadMesssageQuit(self.uTid);
694 return processInterrupt(self.uPid);
695
696 def sendUserSignal1(self):
697 """
698 Sends a SIGUSR1 or equivalent to nudge the process into shutting down
699 (VBoxSVC) or something.
700 Returns True on success, False on failure.
701
702 On Windows hosts this may not work unless the process happens to be a
703 process group leader.
704 """
705 #if sys.platform == 'win32':
706 # return winbase.postThreadMesssageClose(self.uTid);
707 return sendUserSignal1(self.uPid);
708
709 def terminate(self):
710 """
711 Terminates the process in a nice manner (SIGTERM or equivalent).
712 Returns True on success, False on failure (logged).
713 """
714 if sys.platform == 'win32':
715 return winbase.processTerminateByHandle(self.hWin);
716 return processTerminate(self.uPid);
717
718 def getPid(self):
719 """ Returns the process id. """
720 return self.uPid;
721
722
723class SubTestDriverBase(object):
724 """
725 The base sub-test driver.
726
727 It helps thinking of these as units/sets/groups of tests, where the test
728 cases are (mostly) realized in python.
729
730 The sub-test drivers are subordinates of one or more test drivers. They
731 can be viewed as test code libraries that is responsible for parts of a
732 test driver run in different setups. One example would be testing a guest
733 additions component, which is applicable both to freshly installed guest
734 additions and VMs with old guest.
735
736 The test drivers invokes the sub-test drivers in a private manner during
737 test execution, but some of the generic bits are done automagically by the
738 base class: options, help, various other actions.
739 """
740
741 def __init__(self, sName, oTstDrv):
742 self.sName = sName;
743 self.oTstDrv = oTstDrv;
744
745
746 def showUsage(self):
747 """
748 Show usage information if any.
749
750 The default implementation only prints the name.
751 """
752 reporter.log('');
753 reporter.log('Options for sub-test driver %s:' % (self.sName,));
754 return True;
755
756 def parseOption(self, asArgs, iArg):
757 """
758 Parse an option. Override this.
759
760 @param asArgs The argument vector.
761 @param iArg The index of the current argument.
762
763 @returns The index of the next argument if consumed, @a iArg if not.
764
765 @throws InvalidOption or QuietInvalidOption on syntax error or similar.
766 """
767 _ = asArgs;
768 return iArg;
769
770
771class TestDriverBase(object): # pylint: disable=R0902
772 """
773 The base test driver.
774 """
775
776 def __init__(self):
777 self.fInterrupted = False;
778
779 # Actions.
780 self.asSpecialActions = ['extract', 'abort'];
781 self.asNormalActions = ['cleanup-before', 'verify', 'config', 'execute', 'cleanup-after' ];
782 self.asActions = [];
783 self.sExtractDstPath = None;
784
785 # Options.
786 self.fNoWipeClean = False;
787
788 # Tasks - only accessed by one thread atm, so no need for locking.
789 self.aoTasks = [];
790
791 # Host info.
792 self.sHost = utils.getHostOs();
793 self.sHostArch = utils.getHostArch();
794
795 #
796 # Get our bearings and adjust the environment.
797 #
798 if not utils.isRunningFromCheckout():
799 self.sBinPath = os.path.join(g_ksValidationKitDir, utils.getHostOs(), utils.getHostArch());
800 else:
801 self.sBinPath = os.path.join(g_ksValidationKitDir, os.pardir, os.pardir, os.pardir, 'out', utils.getHostOsDotArch(),
802 os.environ.get('KBUILD_TYPE', os.environ.get('BUILD_TYPE', 'debug')),
803 'validationkit', utils.getHostOs(), utils.getHostArch());
804 self.sOrgShell = os.environ.get('SHELL');
805 self.sOurShell = os.path.join(self.sBinPath, 'vts_shell' + exeSuff()); # No shell yet.
806 os.environ['SHELL'] = self.sOurShell;
807
808 self.sScriptPath = getDirEnv('TESTBOX_PATH_SCRIPTS');
809 if self.sScriptPath is None:
810 self.sScriptPath = os.path.abspath(os.path.join(os.getcwd(), '..'));
811 os.environ['TESTBOX_PATH_SCRIPTS'] = self.sScriptPath;
812
813 self.sScratchPath = getDirEnv('TESTBOX_PATH_SCRATCH', fTryCreate = True);
814 if self.sScratchPath is None:
815 sTmpDir = tempfile.gettempdir();
816 if sTmpDir == '/tmp': # /var/tmp is generally more suitable on all platforms.
817 sTmpDir = '/var/tmp';
818 self.sScratchPath = os.path.abspath(os.path.join(sTmpDir, 'VBoxTestTmp'));
819 if not os.path.isdir(self.sScratchPath):
820 os.makedirs(self.sScratchPath, 0700);
821 os.environ['TESTBOX_PATH_SCRATCH'] = self.sScratchPath;
822
823 self.sTestBoxName = getEnv( 'TESTBOX_NAME', 'local');
824 self.sTestSetId = getEnv( 'TESTBOX_TEST_SET_ID', 'local');
825 self.sBuildPath = getDirEnv('TESTBOX_PATH_BUILDS');
826 self.sUploadPath = getDirEnv('TESTBOX_PATH_UPLOAD');
827 self.sResourcePath = getDirEnv('TESTBOX_PATH_RESOURCES');
828 if self.sResourcePath is None:
829 if self.sHost == 'darwin': self.sResourcePath = "/Volumes/testrsrc/";
830 elif self.sHost == 'freebsd': self.sResourcePath = "/mnt/testrsrc/";
831 elif self.sHost == 'linux': self.sResourcePath = "/mnt/testrsrc/";
832 elif self.sHost == 'os2': self.sResourcePath = "T:/";
833 elif self.sHost == 'solaris': self.sResourcePath = "/mnt/testrsrc/";
834 elif self.sHost == 'win': self.sResourcePath = "T:/";
835 else: raise GenError('unknown host OS "%s"' % (self.sHost));
836
837 # PID file for the testdriver.
838 self.sPidFile = os.path.join(self.sScratchPath, 'testdriver.pid');
839
840 # Some stuff for the log...
841 reporter.log('scratch: %s' % (self.sScratchPath,));
842
843 # Get the absolute timeout (seconds since epoch, see
844 # utils.timestampSecond()). None if not available.
845 self.secTimeoutAbs = os.environ.get('TESTBOX_TIMEOUT_ABS', None);
846 if self.secTimeoutAbs is not None:
847 self.secTimeoutAbs = long(self.secTimeoutAbs);
848 reporter.log('secTimeoutAbs: %s' % (self.secTimeoutAbs,));
849 else:
850 reporter.log('TESTBOX_TIMEOUT_ABS not found in the environment');
851
852 # Distance from secTimeoutAbs that timeouts should be adjusted to.
853 self.secTimeoutFudge = 30;
854
855 # List of sub-test drivers (SubTestDriverBase derivatives).
856 self.aoSubTstDrvs = [];
857
858 # Use the scratch path for temporary files.
859 if self.sHost in ['win', 'os2']:
860 os.environ['TMP'] = self.sScratchPath;
861 os.environ['TEMP'] = self.sScratchPath;
862 os.environ['TMPDIR'] = self.sScratchPath;
863 os.environ['IPRT_TMPDIR'] = self.sScratchPath; # IPRT/VBox specific.
864
865
866 def dump(self):
867 """
868 For debugging. --> __str__?
869 """
870 print >> sys.stderr, "testdriver.base: sBinPath = '%s'" % self.sBinPath;
871 print >> sys.stderr, "testdriver.base: sScriptPath = '%s'" % self.sScriptPath;
872 print >> sys.stderr, "testdriver.base: sScratchPath = '%s'" % self.sScratchPath;
873 print >> sys.stderr, "testdriver.base: sTestBoxName = '%s'" % self.sTestBoxName;
874 print >> sys.stderr, "testdriver.base: sBuildPath = '%s'" % self.sBuildPath;
875 print >> sys.stderr, "testdriver.base: sResourcePath = '%s'" % self.sResourcePath;
876 print >> sys.stderr, "testdriver.base: sUploadPath = '%s'" % self.sUploadPath;
877 print >> sys.stderr, "testdriver.base: sTestSetId = '%s'" % self.sTestSetId;
878 print >> sys.stderr, "testdriver.base: sHost = '%s'" % self.sHost;
879 print >> sys.stderr, "testdriver.base: sHostArch = '%s'" % self.sHostArch;
880 print >> sys.stderr, "testdriver.base: asSpecialActions = '%s'" % self.asSpecialActions;
881 print >> sys.stderr, "testdriver.base: asNormalActions = '%s'" % self.asNormalActions;
882 print >> sys.stderr, "testdriver.base: asActions = '%s'" % self.asActions;
883 print >> sys.stderr, "testdriver.base: secTimeoutAbs = '%s'" % self.secTimeoutAbs;
884 for sVar in sorted(os.environ.keys()):
885 print >> sys.stderr, "os.environ[%s] = '%s'" % (sVar, os.environ[sVar],);
886
887 #
888 # Resource utility methods.
889 #
890
891 def isResourceFile(self, sFile):
892 """
893 Checks if sFile is in in the resource set.
894 """
895 ## @todo need to deal with stuff in the validationkit.zip and similar.
896 asRsrcs = self.getResourceSet();
897 if sFile in asRsrcs:
898 return os.path.isfile(os.path.join(self.sResourcePath, sFile));
899 for sRsrc in asRsrcs:
900 if sFile.startswith(sRsrc):
901 sFull = os.path.join(self.sResourcePath, sRsrc);
902 if os.path.isdir(sFull):
903 return os.path.isfile(os.path.join(self.sResourcePath, sRsrc));
904 return False;
905
906 def getFullResourceName(self, sName):
907 """
908 Returns the full resource name.
909 """
910 if os.path.isabs(sName): ## @todo Hack. Need to deal properly with stuff in the validationkit.zip and similar.
911 return sName;
912 return os.path.join(self.sResourcePath, sName);
913
914 #
915 # Scratch related utility methods.
916 #
917
918 def __wipeScratchRecurse(self, sDir):
919 """
920 Deletes all file and sub-directories in sDir.
921 Returns the number of errors.
922 """
923 try:
924 asNames = os.listdir(sDir);
925 except:
926 reporter.errorXcpt('os.listdir("%s")' % (sDir));
927 return False;
928
929 cErrors = 0;
930 for sName in asNames:
931 # Build full path and lstat the object.
932 sFullName = os.path.join(sDir, sName)
933 try:
934 oStat = os.lstat(sFullName);
935 except:
936 reporter.errorXcpt('lstat("%s")' % (sFullName));
937 cErrors = cErrors + 1;
938 continue;
939
940 if stat.S_ISDIR(oStat.st_mode):
941 # Directory - recurse and try remove it.
942 cErrors = cErrors + self.__wipeScratchRecurse(sFullName);
943 try:
944 os.rmdir(sFullName);
945 except:
946 reporter.errorXcpt('rmdir("%s")' % (sFullName));
947 cErrors = cErrors + 1;
948 else:
949 # File, symlink, fifo or something - remove/unlink.
950 try:
951 os.remove(sFullName);
952 except:
953 reporter.errorXcpt('remove("%s")' % (sFullName));
954 cErrors = cErrors + 1;
955 return cErrors;
956
957 def wipeScratch(self):
958 """
959 Removes the content of the scratch directory.
960 Returns True on no errors, False + log entries on errors.
961 """
962 cErrors = self.__wipeScratchRecurse(self.sScratchPath);
963 return cErrors == 0;
964
965 #
966 # Sub-test driver related methods.
967 #
968
969 def addSubTestDriver(self, oSubTstDrv):
970 """
971 Adds a sub-test driver.
972
973 Returns True on success, false on failure.
974 """
975 assert isinstance(oSubTstDrv, SubTestDriverBase);
976 if oSubTstDrv in self.aoSubTstDrvs:
977 reporter.error('Attempt at adding sub-test driver %s twice.' % (oSubTstDrv.sName,));
978 return False;
979 self.aoSubTstDrvs.append(oSubTstDrv);
980 return True;
981
982 def showSubTstDrvUsage(self):
983 """
984 Shows the usage of the sub-test drivers.
985 """
986 for oSubTstDrv in self.aoSubTstDrvs:
987 oSubTstDrv.showUsage();
988 return True;
989
990 def subTstDrvParseOption(self, asArgs, iArgs):
991 """
992 Lets the sub-test drivers have a go at the option.
993 Returns the index of the next option if handled, otherwise iArgs.
994 """
995 for oSubTstDrv in self.aoSubTstDrvs:
996 iNext = oSubTstDrv.parseOption(asArgs, iArgs)
997 if iNext != iArgs:
998 assert iNext > iArgs;
999 assert iNext <= len(asArgs);
1000 return iNext;
1001 return iArgs;
1002
1003
1004 #
1005 # Task related methods.
1006 #
1007
1008 def addTask(self, oTask):
1009 """
1010 Adds oTask to the task list.
1011
1012 Returns True if the task was added.
1013
1014 Returns False if the task was already in the task list.
1015 """
1016 if oTask in self.aoTasks:
1017 return False;
1018 #reporter.log2('adding task %s' % (oTask,));
1019 self.aoTasks.append(oTask);
1020 oTask.setTaskOwner(self);
1021 #reporter.log2('tasks now in list: %d - %s' % (len(self.aoTasks), self.aoTasks));
1022 return True;
1023
1024 def removeTask(self, oTask):
1025 """
1026 Removes oTask to the task list.
1027
1028 Returns oTask on success and None on failure.
1029 """
1030 try:
1031 #reporter.log2('removing task %s' % (oTask,));
1032 self.aoTasks.remove(oTask);
1033 except:
1034 return None;
1035 else:
1036 oTask.setTaskOwner(None);
1037 #reporter.log2('tasks left: %d - %s' % (len(self.aoTasks), self.aoTasks));
1038 return oTask;
1039
1040 def removeAllTasks(self):
1041 """
1042 Removes all the task from the task list.
1043
1044 Returns None.
1045 """
1046 aoTasks = self.aoTasks;
1047 self.aoTasks = [];
1048 for oTask in aoTasks:
1049 oTask.setTaskOwner(None);
1050 return None;
1051
1052 def notifyAboutReadyTask(self, oTask):
1053 """
1054 Notificiation that there is a ready task. May be called owning the
1055 task lock, so be careful wrt deadlocks.
1056
1057 Remember to call super when overriding this.
1058 """
1059 if oTask is None: pass; # lint
1060 return None;
1061
1062 def pollTasks(self):
1063 """
1064 Polls the task to see if any of them are ready.
1065 Returns the ready task, None if none are ready.
1066 """
1067 for oTask in self.aoTasks:
1068 if oTask.pollTask():
1069 return oTask;
1070 return None;
1071
1072 def waitForTasksSleepWorker(self, cMsTimeout):
1073 """
1074 Overriable method that does the sleeping for waitForTask().
1075
1076 cMsTimeout will not be larger than 1000, so there is normally no need
1077 to do any additional splitting up of the polling interval.
1078
1079 Returns True if cMillieSecs elapsed.
1080 Returns False if some exception was raised while we waited or
1081 there turned out to be nothing to wait on.
1082 """
1083 try:
1084 self.aoTasks[0].waitForTask(cMsTimeout);
1085 return True;
1086 except Exception, oXcpt:
1087 reporter.log("waitForTasksSleepWorker: %s" % (str(oXcpt),));
1088 return False;
1089
1090 def waitForTasks(self, cMsTimeout):
1091 """
1092 Waits for any of the tasks to require attention or a KeyboardInterrupt.
1093 Returns the ready task on success, None on timeout or interrupt.
1094 """
1095 try:
1096 #reporter.log2('waitForTasks: cMsTimeout=%d' % (cMsTimeout,));
1097
1098 if cMsTimeout == 0:
1099 return self.pollTasks();
1100
1101 if len(self.aoTasks) == 0:
1102 return None;
1103
1104 fMore = True;
1105 if cMsTimeout < 0:
1106 while fMore:
1107 oTask = self.pollTasks();
1108 if oTask is not None:
1109 return oTask;
1110 fMore = self.waitForTasksSleepWorker(1000);
1111 else:
1112 msStart = timestampMilli();
1113 while fMore:
1114 oTask = self.pollTasks();
1115 if oTask is not None:
1116 #reporter.log2('waitForTasks: returning %s, msStart=%d' % \
1117 # (oTask, msStart));
1118 return oTask;
1119
1120 cMsElapsed = timestampMilli() - msStart;
1121 if cMsElapsed > cMsTimeout: # not ==, we want the final waitForEvents.
1122 break;
1123 cMsSleep = cMsTimeout - cMsElapsed;
1124 if cMsSleep > 1000:
1125 cMsSleep = 1000;
1126 fMore = self.waitForTasksSleepWorker(cMsSleep);
1127 except KeyboardInterrupt:
1128 self.fInterrupted = True;
1129 reporter.errorXcpt('KeyboardInterrupt', 6);
1130 except:
1131 reporter.errorXcpt(None, 6);
1132 return None;
1133
1134 #
1135 # PID file management methods.
1136 #
1137
1138 def pidFileRead(self):
1139 """
1140 Worker that reads the PID file.
1141 Returns list of PID, empty if no file.
1142 """
1143 aiPids = [];
1144 if os.path.isfile(self.sPidFile):
1145 try:
1146 oFile = utils.openNoInherit(self.sPidFile, 'r');
1147 sContent = str(oFile.read());
1148 oFile.close();
1149 except:
1150 reporter.errorXcpt();
1151 return aiPids;
1152
1153 sContent = str(sContent).strip().replace('\n', ' ').replace('\r', ' ').replace('\t', ' ');
1154 for sPid in sContent.split(' '):
1155 if sPid.isdigit():
1156 try:
1157 aiPids.append(int(sPid));
1158 except:
1159 reporter.logXcpt('sPid=%s' % (sPid,));
1160 else:
1161 reporter.log('%s: "%s"' % (self.sPidFile, sPid));
1162
1163 return aiPids;
1164
1165 def pidFileAdd(self, iPid, fSudo = False):
1166 """
1167 Adds a PID to the PID file, creating the file if necessary.
1168 """
1169 _ = fSudo; ## @todo remember sudo (root) children.
1170 try:
1171 oFile = utils.openNoInherit(self.sPidFile, 'a');
1172 oFile.write(str(iPid) + '\n');
1173 oFile.close();
1174 except:
1175 reporter.errorXcpt();
1176 return False;
1177 reporter.log2('pidFileAdd: added PID %d (new content: %s)' % (iPid, self.pidFileRead(),));
1178 return True;
1179
1180 def pidFileRemove(self, iPid, fQuiet = False):
1181 """
1182 Removes a PID from the PID file.
1183 """
1184 aiPids = self.pidFileRead();
1185 if iPid not in aiPids:
1186 if not fQuiet:
1187 reporter.log('pidFileRemove could not find %s in the PID file (content: %s)' % (iPid, aiPids));
1188 return False;
1189
1190 aiPids.remove(iPid);
1191 sPid = '';
1192 for iPid2 in aiPids:
1193 sPid += '%s\n' % (iPid2,);
1194
1195 try:
1196 oFile = utils.openNoInherit(self.sPidFile, 'w');
1197 oFile.write(sPid);
1198 oFile.close();
1199 except:
1200 reporter.errorXcpt();
1201 return False;
1202
1203 reporter.log2('pidFileRemove: removed PID %d (new content: %s)' % (iPid, self.pidFileRead(),));
1204 return True;
1205
1206 def pidFileDelete(self):
1207 """Creates the testdriver PID file."""
1208 if os.path.isfile(self.sPidFile):
1209 try:
1210 os.unlink(self.sPidFile);
1211 except:
1212 reporter.logXcpt();
1213 return False;
1214 return True;
1215
1216 #
1217 # Misc helper methods.
1218 #
1219
1220 def requireMoreArgs(self, cMinNeeded, asArgs, iArg):
1221 """
1222 Checks that asArgs has at least cMinNeeded args following iArg.
1223
1224 Returns iArg + 1 if it checks out fine.
1225 Raise appropritate exception if not, ASSUMING that the current argument
1226 is found at iArg.
1227 """
1228 assert cMinNeeded >= 1;
1229 if iArg + cMinNeeded > len(asArgs):
1230 if cMinNeeded > 1:
1231 raise InvalidOption('The "%s" option takes %s values' % (asArgs[iArg], cMinNeeded,));
1232 raise InvalidOption('The "%s" option takes 1 value' % (asArgs[iArg],));
1233 return iArg + 1;
1234
1235 def getBinTool(self, sName):
1236 """
1237 Returns the full path to the given binary validation kit tool.
1238 """
1239 return os.path.join(self.sBinPath, sName) + exeSuff();
1240
1241 def adjustTimeoutMs(self, cMsTimeout, cMsMinimum = None):
1242 """
1243 Adjusts the given timeout (milliseconds) to take TESTBOX_TIMEOUT_ABS
1244 and cMsMinimum (optional) into account.
1245
1246 Returns adjusted timeout.
1247 Raises no exceptions.
1248 """
1249 if self.secTimeoutAbs is not None:
1250 cMsToDeadline = self.secTimeoutAbs * 1000 - utils.timestampMilli();
1251 if cMsToDeadline >= 0:
1252 # Adjust for fudge and enforce the minimum timeout
1253 cMsToDeadline -= self.secTimeoutFudge * 1000;
1254 if cMsToDeadline < (cMsMinimum if cMsMinimum is not None else 10000):
1255 cMsToDeadline = cMsMinimum if cMsMinimum is not None else 10000;
1256
1257 # Is the timeout beyond the (adjusted) deadline, if so change it.
1258 if cMsTimeout > cMsToDeadline:
1259 reporter.log('adjusting timeout: %s ms -> %s ms (deadline)\n' % (cMsTimeout, cMsToDeadline,));
1260 return cMsToDeadline;
1261 reporter.log('adjustTimeoutMs: cMsTimeout (%s) > cMsToDeadline (%s)' % (cMsTimeout, cMsToDeadline,));
1262 else:
1263 # Don't bother, we've passed the deadline.
1264 reporter.log('adjustTimeoutMs: ooops! cMsToDeadline=%s (%s), timestampMilli()=%s, timestampSecond()=%s'
1265 % (cMsToDeadline, cMsToDeadline*1000, utils.timestampMilli(), utils.timestampSecond()));
1266
1267 # Only enforce the minimum timeout if specified.
1268 if cMsMinimum is not None and cMsTimeout < cMsMinimum:
1269 reporter.log('adjusting timeout: %s ms -> %s ms (minimum)\n' % (cMsTimeout, cMsMinimum,));
1270 cMsTimeout = cMsMinimum;
1271
1272 return cMsTimeout;
1273
1274 def prepareResultFile(self, sName = 'results.xml'):
1275 """
1276 Given a base name (no path, but extension if required), a scratch file
1277 name is computed and any previous file removed.
1278
1279 Returns the full path to the file sName.
1280 Raises exception on failure.
1281 """
1282 sXmlFile = os.path.join(self.sScratchPath, sName);
1283 if os.path.exists(sXmlFile):
1284 os.unlink(sXmlFile);
1285 return sXmlFile;
1286
1287
1288 #
1289 # Overridable methods.
1290 #
1291
1292 def showUsage(self):
1293 """
1294 Shows the usage.
1295
1296 When overriding this, call super first.
1297 """
1298 sName = os.path.basename(sys.argv[0]);
1299 reporter.log('Usage: %s [options] <action(s)>' % (sName,));
1300 reporter.log('');
1301 reporter.log('Actions (in execution order):');
1302 reporter.log(' cleanup-before');
1303 reporter.log(' Cleanups done at the start of testing.');
1304 reporter.log(' verify');
1305 reporter.log(' Verify that all necessary resources are present.');
1306 reporter.log(' config');
1307 reporter.log(' Configure the tests.');
1308 reporter.log(' execute');
1309 reporter.log(' Execute the tests.');
1310 reporter.log(' cleanup-after');
1311 reporter.log(' Cleanups done at the end of the testing.');
1312 reporter.log('');
1313 reporter.log('Special Actions:');
1314 reporter.log(' all');
1315 reporter.log(' Alias for: %s' % (' '.join(self.asNormalActions),));
1316 reporter.log(' extract <path>');
1317 reporter.log(' Extract the test resources and put them in the specified');
1318 reporter.log(' path for off side/line testing.');
1319 reporter.log(' abort');
1320 reporter.log(' Aborts the test.');
1321 reporter.log('');
1322 reporter.log('Base Options:');
1323 reporter.log(' -h, --help');
1324 reporter.log(' Show this help message.');
1325 reporter.log(' -v, --verbose');
1326 reporter.log(' Increase logging verbosity, repeat for more logging.');
1327 reporter.log(' -d, --debug');
1328 reporter.log(' Increase the debug logging level, repeat for more info.');
1329 reporter.log(' --no-wipe-clean');
1330 reporter.log(' Do not wipe clean the scratch area during the two clean up');
1331 reporter.log(' actions. This is for facilitating nested test driver execution.');
1332 return True;
1333
1334 def parseOption(self, asArgs, iArg):
1335 """
1336 Parse an option. Override this.
1337
1338 Keyword arguments:
1339 asArgs -- The argument vector.
1340 iArg -- The index of the current argument.
1341
1342 Returns iArg if the option was not recognized.
1343 Returns the index of the next argument when something is consumed.
1344 In the event of a syntax error, a InvalidOption or QuietInvalidOption
1345 should be thrown.
1346 """
1347
1348 if asArgs[iArg] in ('--help', '-help', '-h', '-?', '/?', '/help', '/H', '-H'):
1349 self.showUsage();
1350 self.showSubTstDrvUsage();
1351 raise QuietInvalidOption();
1352
1353 # options
1354 if asArgs[iArg] in ('--verbose', '-v'):
1355 reporter.incVerbosity()
1356 elif asArgs[iArg] in ('--debug', '-d'):
1357 reporter.incDebug()
1358 elif asArgs[iArg] == '--no-wipe-clean':
1359 self.fNoWipeClean = True;
1360 elif (asArgs[iArg] == 'all' or asArgs[iArg] in self.asNormalActions) \
1361 and self.asActions in self.asSpecialActions:
1362 raise InvalidOption('selected special action "%s" already' % (self.asActions[0], ));
1363 # actions
1364 elif asArgs[iArg] == 'all':
1365 self.asActions = [ 'all' ];
1366 elif asArgs[iArg] in self.asNormalActions:
1367 self.asActions.append(asArgs[iArg])
1368 elif asArgs[iArg] in self.asSpecialActions:
1369 if self.asActions != []:
1370 raise InvalidOption('selected special action "%s" already' % (self.asActions[0], ));
1371 self.asActions = [ asArgs[iArg] ];
1372 # extact <destination>
1373 if asArgs[iArg] == 'extract':
1374 iArg = iArg + 1;
1375 if iArg >= len(asArgs): raise InvalidOption('The "extract" action requires a destination directory');
1376 self.sExtractDstPath = asArgs[iArg];
1377 else:
1378 return iArg;
1379 return iArg + 1;
1380
1381 def completeOptions(self):
1382 """
1383 This method is called after parsing all the options.
1384 Returns success indicator. Use the reporter to complain.
1385
1386 Overriable, call super.
1387 """
1388 return True;
1389
1390 def getResourceSet(self):
1391 """
1392 Returns a set of file and/or directory names relative to
1393 TESTBOX_PATH_RESOURCES.
1394
1395 Override this.
1396 """
1397 return [];
1398
1399 def actionExtract(self):
1400 """
1401 Handle the action that extracts the test resources for off site use.
1402 Returns a success indicator and error details with the reporter.
1403
1404 Usually no need to override this.
1405 """
1406 reporter.error('the extract action is not implemented')
1407 return False;
1408
1409 def actionVerify(self):
1410 """
1411 Handle the action that verify the test resources.
1412 Returns a success indicator and error details with the reporter.
1413
1414 There is usually no need to override this.
1415 """
1416
1417 asRsrcs = self.getResourceSet();
1418 for sRsrc in asRsrcs:
1419 # Go thru some pain to catch escape sequences.
1420 if sRsrc.find("//") >= 0:
1421 reporter.error('Double slash test resource name: "%s"' % (sRsrc));
1422 return False;
1423 if sRsrc == ".." \
1424 or sRsrc.startswith("../") \
1425 or sRsrc.find("/../") >= 0 \
1426 or sRsrc.endswith("/.."):
1427 reporter.error('Relative path in test resource name: "%s"' % (sRsrc));
1428 return False;
1429
1430 sFull = os.path.normpath(os.path.abspath(os.path.join(self.sResourcePath, sRsrc)));
1431 if not sFull.startswith(os.path.normpath(self.sResourcePath)):
1432 reporter.error('sFull="%s" self.sResourcePath=%s' % (sFull, self.sResourcePath));
1433 reporter.error('The resource "%s" seems to specify a relative path' % (sRsrc));
1434 return False;
1435
1436 reporter.log2('Checking for resource "%s" at "%s" ...' % (sRsrc, sFull));
1437 if os.path.isfile(sFull):
1438 try:
1439 oFile = utils.openNoInherit(sFull, "rb");
1440 oFile.close();
1441 except Exception, oXcpt:
1442 reporter.error('The file resource "%s" cannot be accessed: %s' % (sFull, oXcpt));
1443 return False;
1444 elif os.path.isdir(sFull):
1445 if not os.path.isdir(os.path.join(sFull, '.')):
1446 reporter.error('The directory resource "%s" cannot be accessed' % (sFull));
1447 return False;
1448 elif os.path.exists(sFull):
1449 reporter.error('The resource "%s" is not a file or directory' % (sFull));
1450 return False;
1451 else:
1452 reporter.error('The resource "%s" was not found' % (sFull));
1453 return False;
1454 return True;
1455
1456 def actionConfig(self):
1457 """
1458 Handle the action that configures the test.
1459 Returns True (success), False (failure) or None (skip the test),
1460 posting complaints and explanations with the reporter.
1461
1462 Override this.
1463 """
1464 return True;
1465
1466 def actionExecute(self):
1467 """
1468 Handle the action that executes the test.
1469
1470 Returns True (success), False (failure) or None (skip the test),
1471 posting complaints and explanations with the reporter.
1472
1473 Override this.
1474 """
1475 return True;
1476
1477 def actionCleanupBefore(self):
1478 """
1479 Handle the action that cleans up spills from previous tests before
1480 starting the tests. This is mostly about wiping the scratch space
1481 clean in local runs. On a testbox the testbox script will use the
1482 cleanup-after if the test is interrupted.
1483
1484 Returns True (success), False (failure) or None (skip the test),
1485 posting complaints and explanations with the reporter.
1486
1487 Override this, but call super to wipe the scratch directory.
1488 """
1489 if self.fNoWipeClean is False:
1490 self.wipeScratch();
1491 return True;
1492
1493 def actionCleanupAfter(self):
1494 """
1495 Handle the action that cleans up all spills from executing the test.
1496
1497 Returns True (success) or False (failure) posting complaints and
1498 explanations with the reporter.
1499
1500 Override this, but call super to wipe the scratch directory.
1501 """
1502 if self.fNoWipeClean is False:
1503 self.wipeScratch();
1504 return True;
1505
1506 def actionAbort(self):
1507 """
1508 Handle the action that aborts a (presumed) running testdriver, making
1509 sure to include all it's children.
1510
1511 Returns True (success) or False (failure) posting complaints and
1512 explanations with the reporter.
1513
1514 Override this, but call super to kill the testdriver script and any
1515 other process covered by the testdriver PID file.
1516 """
1517
1518 aiPids = self.pidFileRead();
1519 reporter.log('The pid file contained: %s' % (aiPids,));
1520
1521 #
1522 # Try convince the processes to quit with increasing impoliteness.
1523 #
1524 if sys.platform == 'win32':
1525 afnMethods = [ processInterrupt, processTerminate ];
1526 else:
1527 afnMethods = [ sendUserSignal1, processInterrupt, processTerminate, processKill ];
1528 for fnMethod in afnMethods:
1529 for iPid in aiPids:
1530 fnMethod(iPid);
1531
1532 for i in range(10):
1533 if i > 0:
1534 time.sleep(1);
1535
1536 for j in range(len(aiPids) - 1, -1, -1):
1537 iPid = aiPids[j];
1538 if not processExists(iPid):
1539 reporter.log('%s terminated' % (iPid,));
1540 self.pidFileRemove(iPid, fQuiet = True);
1541 aiPids.pop(j);
1542
1543 if len(aiPids) == 0:
1544 reporter.log('All done.');
1545 return True;
1546
1547 if i in [4, 8]:
1548 reporter.log('Still waiting for: %s (method=%s)' % (aiPids, fnMethod,));
1549
1550 reporter.log('Failed to terminate the following processes: %s' % (aiPids,));
1551 return False;
1552
1553
1554 def onExit(self, iRc):
1555 """
1556 Hook for doing very important cleanups on the way out.
1557
1558 iRc is the exit code or -1 in the case of an unhandled exception.
1559 Returns nothing and shouldn't raise exceptions (will be muted+ignored).
1560 """
1561 _ = iRc;
1562 return None;
1563
1564
1565 #
1566 # main() - don't override anything!
1567 #
1568
1569 def main(self, asArgs = None):
1570 """
1571 The main function of the test driver.
1572
1573 Keyword arguments:
1574 asArgs -- The argument vector. Defaults to sys.argv.
1575
1576 Returns exit code. No exceptions.
1577 """
1578
1579 #
1580 # Wrap worker in exception handler and always call a 'finally' like
1581 # method to do crucial cleanups on the way out.
1582 #
1583 try:
1584 iRc = self.innerMain(asArgs);
1585 except:
1586 try:
1587 self.onExit(-1);
1588 except:
1589 reporter.logXcpt();
1590 raise;
1591 self.onExit(iRc);
1592 return iRc;
1593
1594
1595 def innerMain(self, asArgs = None): # pylint: disable=R0915
1596 """
1597 Exception wrapped main() worker.
1598 """
1599
1600 # parse the arguments.
1601 if asArgs is None:
1602 asArgs = list(sys.argv);
1603 iArg = 1;
1604 try:
1605 while iArg < len(asArgs):
1606 iNext = self.parseOption(asArgs, iArg);
1607 if iNext == iArg:
1608 iNext = self.subTstDrvParseOption(asArgs, iArg);
1609 if iNext == iArg:
1610 raise InvalidOption('unknown option: %s' % (asArgs[iArg]))
1611 iArg = iNext;
1612 except QuietInvalidOption, oXcpt:
1613 return rtexitcode.RTEXITCODE_SYNTAX;
1614 except InvalidOption, oXcpt:
1615 reporter.error(oXcpt.str());
1616 return rtexitcode.RTEXITCODE_SYNTAX;
1617 except:
1618 reporter.error('unexpected exception while parsing argument #%s' % (iArg));
1619 traceback.print_exc();
1620 return rtexitcode.RTEXITCODE_SYNTAX;
1621
1622 if not self.completeOptions():
1623 return rtexitcode.RTEXITCODE_SYNTAX;
1624
1625 if self.asActions == []:
1626 reporter.error('no action was specified');
1627 reporter.error('valid actions: %s' % (self.asNormalActions + self.asSpecialActions + ['all']));
1628 return rtexitcode.RTEXITCODE_SYNTAX;
1629
1630 # execte the actions.
1631 fRc = True; # Tristate - True (success), False (failure), None (skipped).
1632 asActions = self.asActions;
1633 if 'extract' in asActions:
1634 reporter.log('*** extract action ***');
1635 asActions.remove('extract');
1636 fRc = self.actionExtract();
1637 reporter.log('*** extract action completed (fRc=%s) ***' % (fRc));
1638 elif 'abort' in asActions:
1639 reporter.log('*** abort action ***');
1640 asActions.remove('abort');
1641 fRc = self.actionAbort();
1642 reporter.log('*** abort action completed (fRc=%s) ***' % (fRc));
1643 else:
1644 if asActions == [ 'all' ]:
1645 asActions = self.asNormalActions;
1646
1647 if 'verify' in asActions:
1648 reporter.log('*** verify action ***');
1649 asActions.remove('verify');
1650 fRc = self.actionVerify();
1651 if fRc is True: reporter.log("verified succeeded");
1652 else: reporter.log("verified failed (fRc=%s)" % (fRc,));
1653 reporter.log('*** verify action completed (fRc=%s) ***' % (fRc,));
1654
1655 if 'cleanup-before' in asActions:
1656 reporter.log('*** cleanup-before action ***');
1657 asActions.remove('cleanup-before');
1658 fRc2 = self.actionCleanupBefore();
1659 if fRc2 is not True: reporter.log("cleanup-before failed");
1660 if fRc2 is not True and fRc is True: fRc = fRc2;
1661 reporter.log('*** cleanup-before action completed (fRc2=%s, fRc=%s) ***' % (fRc2, fRc,));
1662
1663 self.pidFileAdd(os.getpid());
1664
1665 if 'config' in asActions and fRc is True:
1666 asActions.remove('config');
1667 reporter.log('*** config action ***');
1668 fRc = self.actionConfig();
1669 if fRc is True: reporter.log("config succeeded");
1670 elif fRc is None: reporter.log("config skipping test");
1671 else: reporter.log("config failed");
1672 reporter.log('*** config action completed (fRc=%s) ***' % (fRc,));
1673
1674 if 'execute' in asActions and fRc is True:
1675 asActions.remove('execute');
1676 reporter.log('*** execute action ***');
1677 fRc = self.actionExecute();
1678 if fRc is True: reporter.log("execute succeeded");
1679 elif fRc is None: reporter.log("execute skipping test");
1680 else: reporter.log("execute failed (fRc=%s)" % (fRc,));
1681 reporter.testCleanup();
1682 reporter.log('*** execute action completed (fRc=%s) ***' % (fRc,));
1683
1684 if 'cleanup-after' in asActions:
1685 reporter.log('*** cleanup-after action ***');
1686 asActions.remove('cleanup-after');
1687 fRc2 = self.actionCleanupAfter();
1688 if fRc2 is not True: reporter.log("cleanup-after failed");
1689 if fRc2 is not True and fRc is True: fRc = fRc2;
1690 reporter.log('*** cleanup-after action completed (fRc2=%s, fRc=%s) ***' % (fRc2, fRc,));
1691
1692 self.pidFileRemove(os.getpid());
1693
1694 if asActions != [] and fRc is True:
1695 reporter.error('unhandled actions: %s' % (asActions,));
1696 fRc = False;
1697
1698 # Done
1699 if fRc is None:
1700 reporter.log('*****************************************');
1701 reporter.log('*** The test driver SKIPPED the test. ***');
1702 reporter.log('*****************************************');
1703 return rtexitcode.RTEXITCODE_SKIPPED;
1704 if fRc is not True:
1705 reporter.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
1706 reporter.error('!!! The test driver FAILED (in case we forgot to mention it). !!!');
1707 reporter.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
1708 return rtexitcode.RTEXITCODE_FAILURE;
1709 reporter.log('*******************************************');
1710 reporter.log('*** The test driver exits successfully. ***');
1711 reporter.log('*******************************************');
1712 return rtexitcode.RTEXITCODE_SUCCESS;
1713
1714# The old, deprecated name.
1715TestDriver = TestDriverBase; # pylint: disable=C0103
1716
1717
1718#
1719# Unit testing.
1720#
1721
1722# pylint: disable=C0111
1723class TestDriverBaseTestCase(unittest.TestCase):
1724 def setUp(self):
1725 self.oTstDrv = TestDriverBase();
1726 self.oTstDrv.pidFileDelete();
1727
1728 def tearDown(self):
1729 pass; # clean up scratch dir and such.
1730
1731 def testPidFile(self):
1732 aiPids = [os.getpid() + 1, os.getpid() + 2];
1733 self.assertTrue(self.oTstDrv.pidFileAdd(aiPids[0]));
1734 self.assertEqual(self.oTstDrv.pidFileRead(), aiPids[0:1]);
1735 self.assertTrue(self.oTstDrv.pidFileAdd(aiPids[1]));
1736 self.assertEqual(self.oTstDrv.pidFileRead(), aiPids[0:2]);
1737 self.assertTrue(self.oTstDrv.pidFileDelete());
1738
1739if __name__ == '__main__':
1740 unittest.main();
1741 # not reached.
1742
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