![]() |
|
|
Generated: 8 Jan 2009 |
Classes | |
| class | TemporaryEnvironment |
| class | TempDir |
| class | TempFile |
| class | CMT |
| class | BasicOutputValidator |
| class | FilePreprocessor |
| class | FilePreprocessorSequence |
| class | LineSkipper |
| class | RegexpReplacer |
| class | ReferenceFileValidator |
| class | GaudiExeTest |
Functions | |
| def | findReferenceBlock |
| def | countErrorLines |
| def | _parseTTreeSummary |
| def | findTTreeSummaries |
| def | cmpTreesDicts |
| def | getCmpFailingValues |
| def | parseHistosSummary |
| def | findHistosSummaries |
Variables | |
| string | __author__ = 'Marco Clemencic CERN/PH-LBC' |
| string | __version__ = "$Revision: 1.52 $" |
| string | __tag__ = "$Name: $" |
| tuple | maskPointers = RegexpReplacer("0x[0-9a-fA-F]{4,16}","0x########") |
| tuple | normalizeDate |
| tuple | normalizeEOL = FilePreprocessor() |
| tuple | skipEmptyLines = FilePreprocessor() |
| normalizeExamples = maskPointers+normalizeDate | |
| tuple | h_count_re = re.compile(r"SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)") |
| def GaudiTest::_parseTTreeSummary | ( | lines, | ||
| pos | ||||
| ) | [private] |
Parse the TTree summary table in lines, starting from pos. Returns a tuple with the dictionary with the digested informations and the position of the first line after the summary.
Definition at line 557 of file GaudiTest.py.
00557 : 00558 """ 00559 Parse the TTree summary table in lines, starting from pos. 00560 Returns a tuple with the dictionary with the digested informations and the 00561 position of the first line after the summary. 00562 """ 00563 result = {} 00564 i = pos + 1 # first line is a sequence of '*' 00565 count = len(lines) 00566 00567 splitcols = lambda l: [ f.strip() for f in l.strip("*\n").split(':',2) ] 00568 def parseblock(ll): 00569 r = {} 00570 cols = splitcols(ll[0]) 00571 r["Name"], r["Title"] = cols[1:] 00572 00573 cols = splitcols(ll[1]) 00574 r["Entries"] = int(cols[1]) 00575 00576 sizes = cols[2].split() 00577 r["Total size"] = int(sizes[2]) 00578 if sizes[-1] == "memory": 00579 r["File size"] = 0 00580 else: 00581 r["File size"] = int(sizes[-1]) 00582 00583 cols = splitcols(ll[2]) 00584 sizes = cols[2].split() 00585 if cols[0] == "Baskets": 00586 r["Baskets"] = int(cols[1]) 00587 r["Basket size"] = int(sizes[2]) 00588 r["Compression"] = float(sizes[-1]) 00589 return r 00590 00591 if i < (count - 3) and lines[i].startswith("*Tree"): 00592 result = parseblock(lines[i:i+3]) 00593 result["Branches"] = {} 00594 i += 4 00595 while i < (count - 3) and lines[i].startswith("*Br"): 00596 branch = parseblock(lines[i:i+3]) 00597 result["Branches"][branch["Name"]] = branch 00598 i += 4 00599 00600 return (result, i) 00601 def findTTreeSummaries(stdout):
| def GaudiTest::cmpTreesDicts | ( | reference, | ||
| to_check, | ||||
ignore = None | ||||
| ) |
Check that all the keys in reference are in to_check too, with the same value. If the value is a dict, the function is called recursively. to_check can contain more keys than reference, that will not be tested. The function returns at the first difference found.
Definition at line 623 of file GaudiTest.py.
00623 : 00624 """ 00625 Check that all the keys in reference are in to_check too, with the same value. 00626 If the value is a dict, the function is called recursively. to_check can 00627 contain more keys than reference, that will not be tested. 00628 The function returns at the first difference found. 00629 """ 00630 fail_keys = [] 00631 # filter the keys in the reference dictionary 00632 if ignore: 00633 ignore_re = re.compile(ignore) 00634 keys = [ key for key in reference if not ignore_re.match(key) ] 00635 else: 00636 keys = reference.keys() 00637 # loop over the keys (not ignored) in the reference dictionary 00638 for k in keys: 00639 if k in to_check: # the key must be in the dictionary to_check 00640 if (type(reference[k]) is dict) and (type(to_check[k]) is dict): 00641 # if both reference and to_check values are dictionaries, recurse 00642 failed = fail_keys = cmpTreesDicts(reference[k], to_check[k], ignore) 00643 else: 00644 # compare the two values 00645 failed = to_check[k] != reference[k] 00646 else: # handle missing keys in the dictionary to check (i.e. failure) 00647 to_check[k] = None 00648 failed = True 00649 if failed: 00650 fail_keys.insert(0, k) 00651 break # exit from the loop at the first failure 00652 return fail_keys # return the list of keys bringing to the different values 00653 def getCmpFailingValues(reference, to_check, fail_path):
| def GaudiTest::countErrorLines | ( | expected = {'ERROR':0, |
||
| FATAL | ||||
| ) |
Definition at line 521 of file GaudiTest.py.
00521 {'ERROR':0, 'FATAL':0}, **kwargs): 00522 """ 00523 Count the number of messages with required severity (by default ERROR and FATAL) 00524 and check if their numbers match the expected ones (0 by default). 00525 The dictionary "expected" can be used to tune the number of errors and fatals 00526 allowed, or to limit the number of expected warnings etc. 00527 """ 00528 stdout = kwargs["stdout"] 00529 result = kwargs["result"] 00530 causes = kwargs["causes"] 00531 00532 # prepare the dictionary to record the extracted lines 00533 errors = {} 00534 for sev in expected: 00535 errors[sev] = [] 00536 00537 outlines = stdout.splitlines() 00538 from math import log10 00539 fmt = "%%%dd - %%s" % (int(log10(len(outlines))+1)) 00540 00541 linecount = 0 00542 for l in outlines: 00543 linecount += 1 00544 words = l.split() 00545 if len(words) >= 2 and words[1] in errors: 00546 errors[words[1]].append(fmt%(linecount,l.rstrip())) 00547 00548 for e in errors: 00549 if len(errors[e]) != expected[e]: 00550 causes.append('%s(%d)'%(e,len(errors[e]))) 00551 result["GaudiTest.lines.%s"%e] = result.Quote('\n'.join(errors[e])) 00552 result["GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e])) 00553 00554 return causes 00555 00556
| def GaudiTest::findHistosSummaries | ( | stdout | ) |
Scan stdout to find ROOT TTree summaries and digest them.
Definition at line 726 of file GaudiTest.py.
00726 : 00727 """ 00728 Scan stdout to find ROOT TTree summaries and digest them. 00729 """ 00730 outlines = stdout.splitlines() 00731 nlines = len(outlines) - 1 00732 summaries = {} 00733 global h_count_re 00734 00735 pos = 0 00736 while pos < nlines: 00737 summ = {} 00738 # find first line of block: 00739 match = h_count_re.search(outlines[pos]) 00740 while pos < nlines and not match: 00741 pos += 1 00742 match = h_count_re.search(outlines[pos]) 00743 if match: 00744 summ, pos = parseHistosSummary(outlines, pos) 00745 summaries.update(summ) 00746 return summaries 00747 ########################################################################
| def GaudiTest::findReferenceBlock | ( | reference, | ||
| stdout, | ||||
| result, | ||||
| causes, | ||||
signature_offset = 0, |
||||
signature = None, |
||||
id = None | ||||
| ) |
Given a block of text, tries to find it in the output. The block had to be identified by a signature line. By default, the first line is used as signature, or the line pointed to by signature_offset. If signature_offset points outside the block, a signature line can be passed as signature argument. Note: if 'signature' is None (the default), a negative signature_offset is interpreted as index in a list (e.g. -1 means the last line), otherwise the it is interpreted as the number of lines before the first one of the block the signature must appear. The parameter 'id' allow to distinguish between different calls to this function in the same validation code.
Definition at line 474 of file GaudiTest.py.
00475 : 00476 """ 00477 Given a block of text, tries to find it in the output. 00478 The block had to be identified by a signature line. By default, the first 00479 line is used as signature, or the line pointed to by signature_offset. If 00480 signature_offset points outside the block, a signature line can be passed as 00481 signature argument. Note: if 'signature' is None (the default), a negative 00482 signature_offset is interpreted as index in a list (e.g. -1 means the last 00483 line), otherwise the it is interpreted as the number of lines before the 00484 first one of the block the signature must appear. 00485 The parameter 'id' allow to distinguish between different calls to this 00486 function in the same validation code. 00487 """ 00488 # split reference file, sanitize EOLs and remove empty lines 00489 reflines = filter(None,map(lambda s: s.rstrip(), reference.splitlines())) 00490 if not reflines: 00491 raise RuntimeError("Empty (or null) reference") 00492 # the same on standard output 00493 outlines = filter(None,map(lambda s: s.rstrip(), stdout.splitlines())) 00494 00495 res_field = "GaudiTest.RefBlock" 00496 if id: 00497 res_field += "_%s" % id 00498 00499 if signature is None: 00500 if signature_offset < 0: 00501 signature_offset = len(reference)+signature_offset 00502 signature = reflines[signature_offset] 00503 # find the reference block in the output file 00504 try: 00505 pos = outlines.index(signature) 00506 outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset] 00507 if reflines != outlines: 00508 msg = "standard output" 00509 # I do not want 2 messages in causes if teh function is called twice 00510 if not msg in causes: 00511 causes.append(msg) 00512 result[res_field + ".observed"] = result.Quote("\n".join(outlines)) 00513 except ValueError: 00514 causes.append("missing signature") 00515 result[res_field + ".signature"] = result.Quote(signature) 00516 if len(reflines) > 1 or signature != reflines[0]: 00517 result[res_field + ".expected"] = result.Quote("\n".join(reflines)) 00518 00519 return causes 00520 def countErrorLines(expected = {'ERROR':0, 'FATAL':0}, **kwargs):
| def GaudiTest::findTTreeSummaries | ( | stdout | ) |
Scan stdout to find ROOT TTree summaries and digest them.
Definition at line 602 of file GaudiTest.py.
00602 : 00603 """ 00604 Scan stdout to find ROOT TTree summaries and digest them. 00605 """ 00606 stars = re.compile(r"^\*+$") 00607 outlines = stdout.splitlines() 00608 nlines = len(outlines) 00609 trees = {} 00610 00611 i = 0 00612 while i < nlines: #loop over the output 00613 # look for 00614 while i < nlines and not stars.match(outlines[i]): 00615 i += 1 00616 if i < nlines: 00617 tree, i = _parseTTreeSummary(outlines, i) 00618 if tree: 00619 trees[tree["Name"]] = tree 00620 00621 return trees 00622 def cmpTreesDicts(reference, to_check, ignore = None):
| def GaudiTest::getCmpFailingValues | ( | reference, | ||
| to_check, | ||||
| fail_path | ||||
| ) |
Definition at line 654 of file GaudiTest.py.
00654 : 00655 c = to_check 00656 r = reference 00657 for k in fail_path: 00658 c = c.get(k,None) 00659 r = r.get(k,None) 00660 if c is None or r is None: 00661 break # one of the dictionaries is not deep enough 00662 return (fail_path, r, c) 00663 00664 # signature of the print-out of the histograms h_count_re = re.compile(r"SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")
| def GaudiTest::parseHistosSummary | ( | lines, | ||
| pos | ||||
| ) |
Extract the histograms infos from the lines starting at pos. Returns the position of the first line after the summary block.
Definition at line 667 of file GaudiTest.py.
00667 : 00668 """ 00669 Extract the histograms infos from the lines starting at pos. 00670 Returns the position of the first line after the summary block. 00671 """ 00672 global h_count_re 00673 h_table_head = re.compile(r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"') 00674 h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)") 00675 00676 nlines = len(lines) 00677 00678 # decode header 00679 m = h_count_re.search(lines[pos]) 00680 total = int(m.group(1)) 00681 partials = {} 00682 for k, v in [ x.split("=") for x in m.group(2).split() ]: 00683 partials[k] = int(v) 00684 pos += 1 00685 00686 summ = {} 00687 while pos < nlines: 00688 m = h_table_head.search(lines[pos]) 00689 if m: 00690 t, d = m.groups(1) # type and directory 00691 t = t.replace(" profile", "Prof") 00692 pos += 1 00693 if pos < nlines: 00694 l = lines[pos] 00695 else: 00696 l = "" 00697 cont = {} 00698 if l.startswith(" | ID"): 00699 # table format 00700 titles = [ x.strip() for x in l.split("|")][1:] 00701 pos += 1 00702 while pos < nlines and lines[pos].startswith(" |"): 00703 l = lines[pos] 00704 values = [ x.strip() for x in l.split("|")][1:] 00705 hcont = {} 00706 for i in range(len(titles)): 00707 hcont[titles[i]] = values[i] 00708 cont[hcont["ID"]] = hcont 00709 pos += 1 00710 elif l.startswith(" ID="): 00711 while pos < nlines and lines[pos].startswith(" ID="): 00712 values = [ x.strip() for x in h_short_summ.search(lines[pos]).groups() ] 00713 cont[values[0]] = values 00714 pos += 1 00715 else: # not interpreted 00716 raise RuntimeError("Cannot understand line %d: '%s'" % (pos, l)) 00717 if not d in summ: 00718 summ[d] = {} 00719 summ[d][t] = cont 00720 summ[d]["header"] = partials 00721 summ[d]["header"]["Total"] = total 00722 else: 00723 break 00724 return summ, pos 00725 def findHistosSummaries(stdout):
| string GaudiTest::__author__ = 'Marco Clemencic CERN/PH-LBC' |
Definition at line 24 of file GaudiTest.py.
| string GaudiTest::__tag__ = "$Name: $" |
Definition at line 26 of file GaudiTest.py.
| string GaudiTest::__version__ = "$Revision: 1.52 $" |
Definition at line 25 of file GaudiTest.py.
| tuple GaudiTest::h_count_re = re.compile(r"SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)") |
Definition at line 665 of file GaudiTest.py.
| tuple GaudiTest::maskPointers = RegexpReplacer("0x[0-9a-fA-F]{4,16}","0x########") |
Definition at line 372 of file GaudiTest.py.
| tuple GaudiTest::normalizeDate |
Initial value:
RegexpReplacer("[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9] *(CES?T)?", "00:00:00 1970-01-01")
Definition at line 373 of file GaudiTest.py.
| tuple GaudiTest::normalizeEOL = FilePreprocessor() |
Definition at line 375 of file GaudiTest.py.
Definition at line 383 of file GaudiTest.py.
| tuple GaudiTest::skipEmptyLines = FilePreprocessor() |
Definition at line 378 of file GaudiTest.py.