Package pysys :: Module basetest
[hide private]
[frames] | no frames]

Source Code for Module pysys.basetest

  1  #!/usr/bin/env python 
  2  # PySys System Test Framework, Copyright (C) 2006-2013  M.B.Grieve 
  3   
  4  # This library is free software; you can redistribute it and/or 
  5  # modify it under the terms of the GNU Lesser General Public 
  6  # License as published by the Free Software Foundation; either 
  7  # version 2.1 of the License, or (at your option) any later version. 
  8   
  9  # This library is distributed in the hope that it will be useful, 
 10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 12  # Lesser General Public License for more details. 
 13   
 14  # You should have received a copy of the GNU Lesser General Public 
 15  # License along with this library; if not, write to the Free Software 
 16  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 
 17   
 18  # Contact: moraygrieve@users.sourceforge.net 
 19  """ 
 20  Contains the base test class for test execution and validation.  
 21   
 22  For more information see the L{pysys.basetest.BaseTest} API documentation.  
 23   
 24  """ 
 25  import sys, os, os.path, re, string, time, thread, logging, copy, math, stat 
 26   
 27  from pysys import log 
 28  from pysys.constants import * 
 29  from pysys.exceptions import * 
 30  from pysys.utils.filecopy import filecopy 
 31  from pysys.utils.filegrep import filegrep 
 32  from pysys.utils.filegrep import lastgrep 
 33  from pysys.utils.filediff import filediff 
 34  from pysys.utils.filegrep import orderedgrep 
 35  from pysys.utils.linecount import linecount 
 36  from pysys.utils.allocport import TCPPortOwner 
 37  from pysys.process.user import ProcessUser 
 38  from pysys.process.helper import ProcessWrapper 
 39  from pysys.process.monitor import ProcessMonitor 
 40  from pysys.manual.ui import ManualTester 
 41  from pysys.process.user import ProcessUser 
 42   
 43   
 44  TEST_TEMPLATE = '''%s 
 45  %s 
 46   
 47  class %s(%s): 
 48          def execute(self): 
 49                  pass 
 50   
 51          def validate(self): 
 52                  pass 
 53  ''' 
 54   
 55   
56 -class BaseTest(ProcessUser):
57 """The base class for all PySys testcases. 58 59 BaseTest is the parent class of all PySys system testcases. The class provides utility functions for 60 cross-platform process management and manipulation, test timing, and test validation. Any PySys testcase 61 should inherit from the base test and provide an implementation of the abstract L{execute} method 62 defined in this class. Child classes can also overide the L{setup}, L{cleanup} and L{validate} 63 methods of the class to provide custom setup and cleanup actions for a particual test, and to perform 64 all validation steps in a single method should this prove logically more simple. 65 66 Execution of a PySys testcase is performed through an instance of the L{pysys.baserunner.BaseRunner} 67 class, or a subclass thereof. The base runner instantiates an instance of the testcase, and then calls 68 the C{setup}, C{execute}, C{validate} and C{cleanup} methods of the instance. All processes started during 69 the test execution are reference counted within the base test, and terminated within the C{cleanup} method. 70 71 Validation of the testcase is through the C{assert*} methods. Execution of each method appends an outcome 72 to an internal data structure thus building up a record of the individual validation outcomes. Several 73 potential outcomes are supported by the PySys framework (C{SKIPPED}, C{BLOCKED}, C{DUMPEDCORE}, C{TIMEDOUT}, 74 C{FAILED}, C{NOTVERIFIED}, and C{PASSED}) and the overall outcome of the testcase is determined using a 75 precedence order of the individual outcomes. All C{assert*} methods support variable argument lists for 76 common non-default parameters. Currently this only includes the C{assertMessage} parameter, to override the 77 default statement logged by the framework to stdout and the run log. 78 79 @ivar mode: The user defined mode the test is running within. Subclasses can use this in conditional checks 80 to modify the test execution based upon the mode. 81 @type mode: string 82 @ivar input: Full path to the input directory of the testcase. This is used both by the class and its 83 subclasses to locate the default directory containing all input data to the testcase, as defined 84 in the testcase descriptor. 85 @type input: string 86 @ivar output: Full path to the output sub-directory of the testcase. This is used both by the class and its 87 subclasses to locate the default directory for output produced by the testcase. Note that this 88 is the actual directory where all output is written, as modified from that defined in the testcase 89 descriptor to accomodate for the sub-directory used within this location to sandbox concurrent 90 execution of the test, and/or to denote the run number. 91 @type output: string 92 @ivar reference: Full path to the reference directory of the testcase. This is used both by the class and its 93 subclasses to locate the default directory containing all reference data to the testcase, as defined 94 in the testcase descriptor. 95 @type reference: string 96 @ivar log: Reference to the logger instance of this class 97 @type log: logging.Logger 98 @ivar project: Reference to the project details as set on the module load of the launching executable 99 @type project: L{Project} 100 101 """ 102
103 - def __init__ (self, descriptor, outsubdir, runner):
104 """Create an instance of the BaseTest class. 105 106 @param descriptor: The descriptor for the test giving all test details 107 @param outsubdir: The output subdirectory the test output will be written to 108 @param runner: Reference to the runner responsable for executing the testcase 109 110 """ 111 ProcessUser.__init__(self) 112 self.descriptor = descriptor 113 self.input = descriptor.input 114 self.output = os.path.join(descriptor.output, outsubdir) 115 self.reference = descriptor.reference 116 self.runner = runner 117 self.mode = runner.mode 118 self.setKeywordArgs(runner.xargs) 119 self.monitorList = [] 120 self.manualTester = None 121 self.outcome = [] 122 self.log = log 123 self.project = PROJECT 124 self.resources = []
125 126
127 - def setKeywordArgs(self, xargs):
128 """Set the xargs as data attributes of the test class. 129 130 Values in the xargs dictionary are set as data attributes using the builtin C{setattr} method. 131 Thus an xargs dictionary of the form C{{'foo': 'bar'}} will result in a data attribute of the 132 form C{self.foo} with C{value bar}. This is used so that subclasses can define default values of 133 data attributes, which can be overriden on instantiation e.g. using the -X options to the 134 runTest.py launch executable. 135 136 @param xargs: A dictionary of the user defined extra arguments 137 138 """ 139 for key in xargs.keys(): 140 setattr(self, key, xargs[key])
141 142 143 # methods to add to and obtain the test outcome
144 - def addOutcome(self, outcome):
145 """Add a test validation outcome to the validation list. 146 147 The method provides the ability to add a validation outcome to the internal data structure 148 storing the list of test validation outcomes. In a single test run multiple validations may 149 be performed. The currently supported validation outcomes are:: 150 151 SKIPPED: An execution/validation step of the test was skipped (e.g. deliberately) 152 BLOCKED: An execution/validation step of the test could not be run (e.g. a missing resource) 153 DUMPEDCORE: A process started by the test produced a core file (unix only) 154 TIMEDOUT: An execution/validation step of the test timed out (e.g. process deadlock) 155 FAILED: A validation step of the test failed 156 NOTVERIFIED: No validation steps were performed 157 INSPECT: A validation step of the test requires manual inspection 158 PASSED: A validation step of the test passed 159 160 The outcomes are considered to have a precedence order, as defined by the order of the outcomes listed 161 above. Thus a C{BLOCKED} outcome has a higher precedence than a C{PASSED} outcome. The outcomes are defined 162 in L{pysys.constants}. 163 164 @param outcome: The outcome to add 165 166 """ 167 self.outcome.append(outcome)
168 169
170 - def getOutcome(self):
171 """Get the overall outcome of the test based on the precedence order. 172 173 The method returns the overal outcome of the test based on the outcomes stored in the internal data 174 structure. The precedence order of the possible outcomes is used to determined the overall outcome 175 of the test, e.g. if C{PASSED}, C{BLOCKED} and C{FAILED} were recorded during the execution of the test, 176 the overall outcome would be C{BLOCKED}. 177 178 The method returns the integer value of the outcome as defined in L{pysys.constants}. To convert this 179 to a string representation use the C{LOOKUP} dictionary i.e. C{LOOKUP}[test.getOutcome()] 180 181 @return: The overall test outcome 182 @rtype: integer 183 184 """ 185 if len(self.outcome) == 0: return NOTVERIFIED 186 return sorted(self.outcome, key=lambda x: PRECEDENT.index(x))[0]
187 188 189 # test methods for execution, validation and cleanup. The execute method is 190 # abstract and must be implemented by a subclass.
191 - def setup(self):
192 """Setup method which may optionally be overridden to perform custom setup operations prior to test execution. 193 194 """ 195 pass
196 197
198 - def execute(self):
199 """Execute method which must be overridden to perform the test execution steps. 200 201 @raises NotImplementedError: Raised exeception should the method not be overridden 202 """ 203 raise NotImplementedError, "The execute method of the BaseTest class must be implemented in a subclass"
204 205
206 - def validate(self):
207 """Validate method which may optionally be overridden to group all validation steps. 208 209 """ 210 pass
211 212
213 - def cleanup(self):
214 """Cleanup method which performs cleanup actions after execution and validation of the test. 215 216 The cleanup method performs actions to stop all processes started in the background and not 217 explicitly killed during the test execution. It also stops all process monitors running in 218 seperate threads, and any instances of the manual tester user interface. Should a custom cleanup 219 for a subclass be required, the BaseTest cleanup method should first be called. e.g. :: 220 221 class MyTest(BaseTest): 222 223 def cleanup(self): 224 # call base test cleanup first 225 BaseTest.cleanup(self) 226 227 # perform custom cleanup actions 228 ... 229 230 """ 231 ProcessUser.__del__(self) 232 233 if self.manualTester and self.manualTester.running(): 234 self.stopManualTester() 235 236 for monitor in self.monitorList: 237 if monitor.running(): monitor.stop() 238 239 while len(self.resources) > 0: 240 self.resources.pop()
241
242 - def addResource(self, resource):
243 """Add a resource which is owned by the test and is therefore 244 cleaned up (deleted) when the test is cleaned up 245 """ 246 self.resources.append(resource)
247 248 249 # process manipulation methods of ProcessUser
250 - def startProcess(self, command, arguments, environs=None, workingDir=None, state=FOREGROUND, timeout=None, stdout=None, stderr=None, displayName=None):
251 """Start a process running in the foreground or background, and return the process handle. 252 253 The method allows spawning of new processes in a platform independent way. The command, arguments, environment and 254 working directory to run the process in can all be specified in the arguments to the method, along with the filenames 255 used for capturing the stdout and stderr of the process. Processes may be started in the C{FOREGROUND}, in which case 256 the method does not return until the process has completed or a time out occurs, or in the C{BACKGROUND} in which case 257 the method returns immediately to the caller returning a handle to the process to allow manipulation at a later stage. 258 All processes started in the C{BACKGROUND} and not explicitly killed using the returned process handle are automatically 259 killed on completion of the test via the L{cleanup} method of the BaseTest. 260 261 This method uses the L{pysys.process.helper} module to start the process. On failure conditions the method may append 262 C{BLOCKED} or C{TIMEDOUT} outcomes to the test validation data structure when it was not possible to start the process 263 (e.g. command does not exist etc), or the timeout period expired (indicating a potential deadlock or livelock in the 264 process). 265 266 @param command: The command to start the process (should include the full path) 267 @param arguments: A list of arguments to pass to the command 268 @param environs: A dictionary of the environment to run the process in (defaults to clean environment) 269 @param workingDir: The working directory for the process to run in (defaults to the testcase output subdirectory) 270 @param state: Run the process either in the C{FOREGROUND} or C{BACKGROUND} (defaults to C{FOREGROUND}) 271 @param timeout: The timeout period after which to termintate processes running in the C{FOREGROUND} 272 @param stdout: The filename used to capture the stdout of the process 273 @param stderr: The filename user to capture the stderr of the process 274 @param displayName: Logical name of the process used for display and reference counting (defaults to the basename of the command) 275 @return: The process handle of the process (L{pysys.process.helper.ProcessWrapper}) 276 @rtype: handle 277 278 """ 279 if workingDir is None: workingDir = r'%s' % self.output 280 if displayName is None: displayName = os.path.basename(command) 281 if environs is None: environs = {} 282 283 try: 284 process = ProcessWrapper(command, arguments, environs, workingDir, state, timeout, stdout, stderr) 285 process.start() 286 if state == FOREGROUND: 287 log.info("Executed %s in foreground with exit status = %d", displayName, process.exitStatus) 288 elif state == BACKGROUND: 289 log.info("Started %s in background with process id %d", displayName, process.pid) 290 except ProcessError: 291 log.warn("%s", sys.exc_info()[1], exc_info=0) 292 self.addOutcome(BLOCKED) 293 except ProcessTimeout: 294 log.warn("Process timedout after %d seconds, stopping process", timeout) 295 process.stop() 296 self.addOutcome(TIMEDOUT) 297 else: 298 self.processList.append(process) 299 try: 300 if self.processCount.has_key(displayName): 301 self.processCount[displayName] = self.processCount[displayName] + 1 302 else: 303 self.processCount[displayName] = 1 304 except: 305 pass 306 return process
307 308
309 - def stopProcess(self, process):
310 """Send a soft or hard kill to a running process to stop its execution. 311 312 This method uses the L{pysys.process.helper} module to stop a running process. 313 Should the request to stop the running process fail, a C{BLOCKED} outcome will 314 be added to the test outcome list. 315 316 @param process: The process handle returned from the L{startProcess} method 317 318 """ 319 if process.running(): 320 try: 321 process.stop() 322 log.info("Stopped process with process id %d", process.pid) 323 except ProcessError: 324 log.warn("Unable to stop process") 325 self.addOutcome(BLOCKED)
326 327
328 - def signalProcess(self, process, signal):
329 """Send a signal to a running process (Unix only). 330 331 This method uses the L{pysys.process.helper} module to send a signal to a running 332 process. Should the request to send the signal to the running process fail, a 333 C{BLOCKED} outcome will be added to the test outcome list. 334 335 @param process: The process handle returned from the L{startProcess} method 336 @param signal: The integer value of the signal to send 337 338 """ 339 if process.running(): 340 try: 341 process.signal(signal) 342 log.info("Sent %d signal to process with process id %d", signal, process.pid) 343 except ProcessError: 344 log.warn("Unable to send signal to process") 345 self.addOutcome(BLOCKED)
346 347
348 - def waitProcess(self, process, timeout):
349 """Wait for a process to terminate, return on termination or expiry of the timeout. 350 351 @param process: The process handle returned from the L{startProcess} method 352 @param timeout: The timeout value in seconds to wait before returning 353 354 """ 355 try: 356 log.info("Waiting %d secs for process with process id %d", timeout, process.pid) 357 process.wait(timeout) 358 except ProcessTimeout: 359 log.warn("Timedout waiting for process") 360 self.addOutcome(TIMEDOUT)
361 362
363 - def startProcessMonitor(self, process, interval, file, **kwargs):
364 """Start a separate thread to log process statistics to logfile, and return a handle to the process monitor. 365 366 This method uses the L{pysys.process.monitor} module to perform logging of the process statistics, 367 starting the monitor as a seperate background thread. Should the request to log the statistics fail 368 a C{BLOCKED} outcome will be added to the test outcome list. All process monitors not explicitly 369 stopped using the returned handle are automatically stopped on completion of the test via the L{cleanup} 370 method of the BaseTest. 371 372 @param process: The process handle returned from the L{startProcess} method 373 @param interval: The interval in seconds between collecting and logging the process statistics 374 @param file: The full path to the filename used for logging the process statistics 375 @param kwargs: Keyword arguments to allow platform specific configurations 376 377 @return: A handle to the process monitor (L{pysys.process.monitor.ProcessMonitor}) 378 @rtype: handle 379 380 """ 381 monitor = ProcessMonitor(process.pid, interval, file, **kwargs) 382 try: 383 self.log.info("Starting process monitor on process with id = %d", process.pid) 384 monitor.start() 385 except ProcessError: 386 self.log.warn("Unable to start process monitor") 387 self.addOutcome(BLOCKED) 388 else: 389 self.monitorList.append(monitor) 390 return monitor
391 392
393 - def stopProcessMonitor(self, monitor):
394 """Stop a process monitor. 395 396 @param monitor: The process monitor handle returned from the L{startProcessMonitor} method 397 398 """ 399 if monitor.running: monitor.stop()
400 401 402 # methods to control the manual tester user interface
403 - def startManualTester(self, file, filedir=None, state=FOREGROUND, timeout=TIMEOUTS['ManualTester']):
404 """Start the manual tester. 405 406 The manual tester user interface (UI) is used to describe a series of manual steps to be performed 407 to execute and validate a test. Only a single instance of the UI can be running at any given time, and 408 can be run either in the C{FOREGROUND} (method will not return until the UI is closed or the timeout 409 occurs) or in the C{BACKGROUND} (method will return straight away so automated actions may be performed 410 concurrently). Should the UI be terminated due to expiry of the timeout, a C{TIMEDOUT} outcome will be 411 added to the outcome list. The UI can be stopped via the L{stopManualTester} method. An instance of the 412 UI not explicitly stopped within a test will automatically be stopped via the L{cleanup} method of the 413 BaseTest. 414 415 @param file: The name of the manual test xml input file (see L{pysys.xml.manual} for details on the DTD) 416 @param filedir: The directory containing the manual test xml input file (defaults to the output subdirectory) 417 @param state: Start the manual tester either in the C{FOREGROUND} or C{BACKGROUND} (defaults to C{FOREGROUND}) 418 @param timeout: The timeout period after which to termintate a manual tester running in the C{FOREGROUND} 419 420 """ 421 if filedir is None: filedir = self.input 422 423 if not self.manualTester or self.manualTester.running() == 0: 424 self.manualTester = ManualTester(self, os.path.join(filedir, file)) 425 thread.start_new_thread(self.manualTester.start, ()) 426 427 if state == FOREGROUND: 428 startTime = time.time() 429 while self.manualTester.running() == 1: 430 currentTime = time.time() 431 if currentTime > startTime + timeout: 432 self.addOutcome(TIMEDOUT) 433 self.manualTester.stop() 434 return 435 time.sleep(1) 436 else: 437 time.sleep(1) 438 else: 439 self.addOutcome(BLOCKED)
440 441
442 - def stopManualTester(self):
443 """Stop the manual tester if running. 444 445 """ 446 if self.manualTester and self.manualTester.running(): 447 self.manualTester.stop() 448 time.sleep(1) 449 else: 450 self.addOutcome(BLOCKED)
451 452
453 - def waitManualTester(self, timeout=TIMEOUTS['ManualTester']):
454 """Wait for the manual tester to be stopped via user interaction. 455 456 """ 457 if self.manualTester and self.manualTester.running(): 458 startTime = time.time() 459 while self.manualTester.running() == 1: 460 currentTime = time.time() 461 if currentTime > startTime + timeout: 462 self.addOutcome(TIMEDOUT) 463 self.manualTester.stop() 464 return 465 time.sleep(1)
466 467 468 # test timing methods. These allow control flow of the test to be set 469 # on various conditions i.e. a socket becoming available for connections, 470 # a file to exist etc
471 - def wait(self, interval):
472 """Wait for a specified period of time. 473 474 @param interval: The time interval in seconds to wait 475 476 """ 477 time.sleep(interval)
478 479 480 # test validation methods. These methods provide means to validate the outcome of 481 # a test based on the occurrence of regular expressions in text files. All methods 482 # directly append to the test outcome list
483 - def assertTrue(self, expr, **xargs):
484 """Perform a validation assert on the supplied expression evaluating to true. 485 486 If the supplied expression evaluates to true a C{PASSED} outcome is added to the 487 outcome list. Should the expression evaluate to false, a C{FAILED} outcome is added. 488 489 @param expr: The expression, as a string, to check for the true | false value 490 @param xargs: Variable argument list (see class description for supported parameters) 491 492 """ 493 if expr == True: 494 self.addOutcome(PASSED) 495 log.info('%s ... passed' % self.__assertMsg(xargs, 'Assertion on boolean expression equal to true')) 496 else: 497 self.addOutcome(FAILED) 498 log.info('%s ... failed' % self.__assertMsg(xargs, 'Assertion on boolean expression equal to true'))
499 500
501 - def assertFalse(self, expr, **xargs):
502 """Perform a validation assert on the supplied expression evaluating to false. 503 504 If the supplied expression evaluates to false a C{PASSED} outcome is added to the 505 outcome list. Should the expression evaluate to true, a C{FAILED} outcome is added. 506 507 @param expr: The expression to check for the true | false value 508 @param xargs: Variable argument list (see class description for supported parameters) 509 510 """ 511 if expr == False: 512 self.addOutcome(PASSED) 513 log.info('%s ... passed' % self.__assertMsg(xargs, 'Assertion on boolean expression equal to false')) 514 else: 515 self.addOutcome(FAILED) 516 log.info('%s ... failed' % self.__assertMsg(xargs, 'Assertion on boolean expression equal to false'))
517 518
519 - def assertDiff(self, file1, file2, filedir1=None, filedir2=None, ignores=[], sort=False, replace=[], includes=[], **xargs):
520 """Perform a validation assert on the comparison of two input text files. 521 522 This method performs a file comparison on two input files. The files are pre-processed prior to the 523 comparison to either ignore particular lines, sort their constituent lines, replace matches to regular 524 expressions in a line with an alternate value, or to only include particular lines. Should the files 525 after pre-processing be equivalent a C{PASSED} outcome is added to the test outcome list, otherwise 526 a C{FAILED} outcome is added. 527 528 @param file1: The basename of the first file used in the file comparison 529 @param file2: The basename of the second file used in the file comparison 530 @param filedir1: The dirname of the first file (defaults to the testcase output subdirectory) 531 @param filedir2: The dirname of the second file (defaults to the testcase reference directory) 532 @param ignores: A list of regular expressions used to denote lines in the files which should be ignored 533 @param sort: Boolean flag to indicate if the lines in the files should be sorted prior to the comparison 534 @param replace: List of tuples of the form ('regexpr', 'replacement'). For each regular expression in the 535 list, any occurences in the files is replaced with the replacement value prior to the comparison being 536 carried out. This is often useful to replace timestamps in logfiles etc. 537 @param includes: A list of regular expressions used to denote lines in the files which should be used in the 538 comparison. Only lines which match an expression in the list are used for the comparison 539 @param xargs: Variable argument list (see class description for supported parameters) 540 541 """ 542 if filedir1 is None: filedir1 = self.output 543 if filedir2 is None: filedir2 = self.reference 544 f1 = os.path.join(filedir1, file1) 545 f2 = os.path.join(filedir2, file2) 546 547 log.debug("Performing file comparison:") 548 log.debug(" file1: %s" % file1) 549 log.debug(" filedir1: %s" % filedir1) 550 log.debug(" file2: %s" % file2) 551 log.debug(" filedir2: %s" % filedir2) 552 553 try: 554 result = filediff(f1, f2, ignores, sort, replace, includes) 555 except: 556 log.warn("caught %s: %s", sys.exc_info()[0], sys.exc_info()[1], exc_info=1) 557 self.addOutcome(BLOCKED) 558 else: 559 logOutcome = log.info 560 if result == True: 561 result = PASSED 562 else: 563 result = FAILED 564 logOutcome = log.warn 565 self.outcome.append(result) 566 logOutcome("%s ... %s", self.__assertMsg(xargs, 'File comparison between %s and %s' % (file1, file2)), LOOKUP[result].lower())
567 568
569 - def assertGrep(self, file, filedir=None, expr='', contains=True, **xargs):
570 """Perform a validation assert on a regular expression occurring in a text file. 571 572 When the C{contains} input argument is set to true, this method will add a C{PASSED} outcome 573 to the test outcome list if the supplied regular expression is seen in the file; otherwise a 574 C{FAILED} outcome is added. Should C{contains} be set to false, a C{PASSED} outcome will only 575 be added should the regular expression not be seen in the file. 576 577 @param file: The basename of the file used in the grep 578 @param filedir: The dirname of the file (defaults to the testcase output subdirectory) 579 @param expr: The regular expression to check for in the file 580 @param contains: Boolean flag to denote if the expression should or should not be seen in the file 581 @param xargs: Variable argument list (see class description for supported parameters) 582 583 """ 584 if filedir is None: filedir = self.output 585 f = os.path.join(filedir, file) 586 587 log.debug("Performing grep on file:") 588 log.debug(" file: %s" % file) 589 log.debug(" filedir: %s" % filedir) 590 log.debug(" expr: %s" % expr) 591 log.debug(" contains: %s" % LOOKUP[contains]) 592 593 try: 594 result = filegrep(f, expr) 595 except: 596 log.warn("caught %s: %s", sys.exc_info()[0], sys.exc_info()[1], exc_info=1) 597 self.addOutcome(BLOCKED) 598 else: 599 logOutcome = log.info 600 if result == contains: 601 result = PASSED 602 else: 603 result = FAILED 604 logOutcome = log.warn 605 self.outcome.append(result) 606 logOutcome("%s ... %s", self.__assertMsg(xargs, 'Grep on input file %s' % file), LOOKUP[result].lower())
607 608
609 - def assertLastGrep(self, file, filedir=None, expr='', contains=True, ignores=[], includes=[], **xargs):
610 """Perform a validation assert on a regular expression occurring in the last line of a text file. 611 612 When the C{contains} input argument is set to true, this method will add a C{PASSED} outcome 613 to the test outcome list if the supplied regular expression is seen in the file; otherwise a 614 C{FAILED} outcome is added. Should C{contains} be set to false, a C{PASSED} outcome will only 615 be added should the regular expression not be seen in the file. 616 617 @param file: The basename of the file used in the grep 618 @param filedir: The dirname of the file (defaults to the testcase output subdirectory) 619 @param expr: The regular expression to check for in the last line of the file 620 @param contains: Boolean flag to denote if the expression should or should not be seen in the file 621 @param ignores: A list of regular expressions used to denote lines in the file which should be ignored 622 @param includes: A list of regular expressions used to denote lines in the file which should be used in the assertion. 623 @param xargs: Variable argument list (see class description for supported parameters) 624 625 """ 626 if filedir is None: filedir = self.output 627 f = os.path.join(filedir, file) 628 629 log.debug("Performing grep on file:") 630 log.debug(" file: %s" % file) 631 log.debug(" filedir: %s" % filedir) 632 log.debug(" expr: %s" % expr) 633 log.debug(" contains: %s" % LOOKUP[contains]) 634 635 try: 636 result = lastgrep(f, expr, ignores, includes) 637 except: 638 log.warn("caught %s: %s", sys.exc_info()[0], sys.exc_info()[1], exc_info=1) 639 self.addOutcome(BLOCKED) 640 else: 641 logOutcome = log.info 642 if result == contains: 643 result = PASSED 644 else: 645 result = FAILED 646 logOutcome = log.warn 647 self.outcome.append(result) 648 logOutcome("%s ... %s", self.__assertMsg(xargs, 'Grep on input file %s' % file), LOOKUP[result].lower())
649 650
651 - def assertOrderedGrep(self, file, filedir=None, exprList=[], contains=True, **xargs):
652 """Perform a validation assert on a list of regular expressions occurring in specified order in a text file. 653 654 When the C{contains} input argument is set to true, this method will append a C{PASSED} outcome 655 to the test outcome list if the supplied regular expressions in the C{exprList} are seen in the file 656 in the order they appear in the list; otherwise a C{FAILED} outcome is added. Should C{contains} be set 657 to false, a C{PASSED} outcome will only be added should the regular expressions not be seen in the file in 658 the order they appear in the list. 659 660 @param file: The basename of the file used in the ordered grep 661 @param filedir: The dirname of the file (defaults to the testcase output subdirectory) 662 @param exprList: A list of regular expressions which should occur in the file in the order they appear in the list 663 @param contains: Boolean flag to denote if the expressions should or should not be seen in the file in the order specified 664 @param xargs: Variable argument list (see class description for supported parameters) 665 666 """ 667 if filedir is None: filedir = self.output 668 f = os.path.join(filedir, file) 669 670 log.debug("Performing ordered grep on file:") 671 log.debug(" file: %s" % file) 672 log.debug(" filedir: %s" % filedir) 673 for expr in exprList: log.debug(" exprList: %s" % expr) 674 log.debug(" contains: %s" % LOOKUP[contains]) 675 676 try: 677 expr = orderedgrep(f, exprList) 678 except: 679 log.warn("caught %s: %s", sys.exc_info()[0], sys.exc_info()[1], exc_info=1) 680 self.addOutcome(BLOCKED) 681 else: 682 logOutcome = log.info 683 if expr is None and contains: 684 result = PASSED 685 elif expr is None and not contains: 686 result = FAILED 687 logOutcome = log.warn 688 elif expr is not None and not contains: 689 result = PASSED 690 else: 691 result = FAILED 692 logOutcome = log.warn 693 self.outcome.append(result) 694 log.info('%s ... %s' % (self.__assertMsg(xargs, 'Ordered grep on input file %s' % file), LOOKUP[result].lower())) 695 if result == FAILED: logOutcome("Ordered grep failed on expression \"%s\"", expr)
696 697
698 - def assertLineCount(self, file, filedir=None, expr='', condition=">=1", **xargs):
699 """Perform a validation assert on the number of lines in a text file matching a specific regular expression. 700 701 This method will add a C{PASSED} outcome to the outcome list if the number of lines in the 702 input file matching the specified regular expression evaluate to true when evaluated against 703 the supplied condition. 704 705 @param file: The basename of the file used in the line count 706 @param filedir: The dirname of the file (defaults to the testcase output subdirectory) 707 @param expr: The regular expression used to match a line of the input file 708 @param condition: The condition to be met for the number of lines matching the regular expression 709 @param xargs: Variable argument list (see class description for supported parameters) 710 711 """ 712 if filedir is None: filedir = self.output 713 f = os.path.join(filedir, file) 714 715 try: 716 numberLines = linecount(f, expr) 717 log.debug("Number of matching lines is %d"%numberLines) 718 except: 719 log.warn("caught %s: %s", sys.exc_info()[0], sys.exc_info()[1], exc_info=1) 720 self.addOutcome(BLOCKED) 721 else: 722 logOutcome = log.info 723 if (eval("%d %s" % (numberLines, condition))): 724 result = PASSED 725 appender = "" 726 else: 727 result = FAILED 728 appender = "[%d%s]" % (numberLines, condition) 729 logOutcome = log.warn 730 self.outcome.append(result) 731 logOutcome("%s ... %s %s", self.__assertMsg(xargs, 'Line count on input file %s' % file), LOOKUP[result].lower(), appender)
732
733 - def getNextAvailableTCPPort(self):
734 """Allocate a TCP port which is available for a server to be 735 started on. Take ownership of it for the duration of the test 736 """ 737 o = TCPPortOwner() 738 self.addResource(o) 739 return o.port
740
741 - def __assertMsg(self, xargs, default):
742 """Return an assert statement requested to override the default value. 743 744 @param xargs: Variable argument list to an assert method 745 @param default: Default assert statement to return if a parameter is not supplied 746 747 """ 748 if xargs.has_key('assertMessage'): return xargs['assertMessage'] 749 return default
750