1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
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
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
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
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
190
192 """Setup method which may optionally be overridden to perform custom setup operations prior to test execution.
193
194 """
195 pass
196
197
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
207 """Validate method which may optionally be overridden to group all validation steps.
208
209 """
210 pass
211
212
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
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
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
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
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
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
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
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
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
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
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
469
470
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
481
482
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
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
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
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