The Gaudi Framework  v39r0 (5b8b5eda)
GaudiTesting.BaseTest Namespace Reference

Classes

class  BaseTest
 
class  BasicOutputValidator
 
class  BlockSkipper
 
class  FilePreprocessor
 
class  FilePreprocessorSequence
 
class  JSONOutputValidator
 
class  LineSkipper
 
class  LineSorter
 
class  ReferenceFileValidator
 
class  RegexpReplacer
 
class  Result
 
class  SortGroupOfLines
 

Functions

def _new_backslashreplace_errors (exc)
 
def sanitize_for_xml (data)
 
def dumpProcs (name)
 
def kill_tree (ppid, sig)
 
def ROOT6WorkAroundEnabled (id=None)
 
def RationalizePath (p)
 
def which (executable)
 
def findTTreeSummaries (stdout)
 
def cmpTreesDicts (reference, to_check, ignore=None)
 
def getCmpFailingValues (reference, to_check, fail_path)
 
def _parseTTreeSummary (lines, pos)
 
def parseHistosSummary (lines, pos)
 
def findHistosSummaries (stdout)
 
def GetPlatform (self)
 
def isWinPlatform (self)
 

Variables

 SKIP_RETURN_CODE
 
 OUTPUT_LIMIT
 
 maskPointers
 
 normalizeDate
 
 normalizeEOL
 
 __processLine__
 
 skipEmptyLines
 
 normalizeTestSuite
 
 lineSkipper
 
list regexps
 
 normalizeExamples
 
 h_count_re
 

Function Documentation

◆ _new_backslashreplace_errors()

def GaudiTesting.BaseTest._new_backslashreplace_errors (   exc)
private

Definition at line 32 of file BaseTest.py.

33  if isinstance(exc, UnicodeDecodeError):
34  code = hex(ord(exc.object[exc.start]))
35  return ("\\" + code[1:], exc.start + 1)
36  else:
37  return backslashreplace_errors(exc)
38 

◆ _parseTTreeSummary()

def GaudiTesting.BaseTest._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 1342 of file BaseTest.py.

1342 def _parseTTreeSummary(lines, pos):
1343  """
1344  Parse the TTree summary table in lines, starting from pos.
1345  Returns a tuple with the dictionary with the digested informations and the
1346  position of the first line after the summary.
1347  """
1348  result = {}
1349  i = pos + 1 # first line is a sequence of '*'
1350  count = len(lines)
1351 
1352  def splitcols(l):
1353  return [f.strip() for f in l.strip("*\n").split(":", 2)]
1354 
1355  def parseblock(ll):
1356  r = {}
1357  delta_i = 0
1358  cols = splitcols(ll[0])
1359 
1360  if len(ll) == 3:
1361  # default one line name/title
1362  r["Name"], r["Title"] = cols[1:]
1363  elif len(ll) == 4:
1364  # in case title is moved to next line due to too long name
1365  delta_i = 1
1366  r["Name"] = cols[1]
1367  r["Title"] = ll[1].strip("*\n").split("|")[1].strip()
1368  else:
1369  assert False
1370 
1371  cols = splitcols(ll[1 + delta_i])
1372  r["Entries"] = int(cols[1])
1373 
1374  sizes = cols[2].split()
1375  r["Total size"] = int(sizes[2])
1376  if sizes[-1] == "memory":
1377  r["File size"] = 0
1378  else:
1379  r["File size"] = int(sizes[-1])
1380 
1381  cols = splitcols(ll[2 + delta_i])
1382  sizes = cols[2].split()
1383  if cols[0] == "Baskets":
1384  r["Baskets"] = int(cols[1])
1385  r["Basket size"] = int(sizes[2])
1386  r["Compression"] = float(sizes[-1])
1387 
1388  return r
1389 
1390  def nextblock(lines, i):
1391  delta_i = 1
1392  dots = re.compile(r"^\.+$")
1393  stars = re.compile(r"^\*+$")
1394  count = len(lines)
1395  while (
1396  i + delta_i < count
1397  and not dots.match(lines[i + delta_i][1:-1])
1398  and not stars.match(lines[i + delta_i])
1399  ):
1400  delta_i += 1
1401  return i + delta_i
1402 
1403  if i < (count - 3) and lines[i].startswith("*Tree"):
1404  i_nextblock = nextblock(lines, i)
1405  result = parseblock(lines[i:i_nextblock])
1406  result["Branches"] = {}
1407  i = i_nextblock + 1
1408  while i < (count - 3) and lines[i].startswith("*Br"):
1409  if i < (count - 2) and lines[i].startswith("*Branch "):
1410  # skip branch header
1411  i += 3
1412  continue
1413  i_nextblock = nextblock(lines, i)
1414  if i_nextblock >= count:
1415  break
1416  branch = parseblock(lines[i:i_nextblock])
1417  result["Branches"][branch["Name"]] = branch
1418  i = i_nextblock + 1
1419 
1420  return (result, i)
1421 
1422 

◆ cmpTreesDicts()

def GaudiTesting.BaseTest.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 1294 of file BaseTest.py.

1294 def cmpTreesDicts(reference, to_check, ignore=None):
1295  """
1296  Check that all the keys in reference are in to_check too, with the same value.
1297  If the value is a dict, the function is called recursively. to_check can
1298  contain more keys than reference, that will not be tested.
1299  The function returns at the first difference found.
1300  """
1301  fail_keys = []
1302  # filter the keys in the reference dictionary
1303  if ignore:
1304  ignore_re = re.compile(ignore)
1305  keys = [key for key in reference if not ignore_re.match(key)]
1306  else:
1307  keys = reference.keys()
1308  # loop over the keys (not ignored) in the reference dictionary
1309  for k in keys:
1310  if k in to_check: # the key must be in the dictionary to_check
1311  if isinstance(reference[k], dict) and isinstance(to_check[k], dict):
1312  # if both reference and to_check values are dictionaries,
1313  # recurse
1314  failed = fail_keys = cmpTreesDicts(reference[k], to_check[k], ignore)
1315  else:
1316  # compare the two values
1317  failed = to_check[k] != reference[k]
1318  else: # handle missing keys in the dictionary to check (i.e. failure)
1319  to_check[k] = None
1320  failed = True
1321  if failed:
1322  fail_keys.insert(0, k)
1323  break # exit from the loop at the first failure
1324  return fail_keys # return the list of keys bringing to the different values
1325 
1326 

◆ dumpProcs()

def GaudiTesting.BaseTest.dumpProcs (   name)
helper to debug GAUDI-1084, dump the list of processes

Definition at line 67 of file BaseTest.py.

67 def dumpProcs(name):
68  """helper to debug GAUDI-1084, dump the list of processes"""
69  from getpass import getuser
70 
71  if "WORKSPACE" in os.environ:
72  p = Popen(["ps", "-fH", "-U", getuser()], stdout=PIPE)
73  with open(os.path.join(os.environ["WORKSPACE"], name), "wb") as f:
74  f.write(p.communicate()[0])
75 
76 

◆ findHistosSummaries()

def GaudiTesting.BaseTest.findHistosSummaries (   stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 1491 of file BaseTest.py.

1491 def findHistosSummaries(stdout):
1492  """
1493  Scan stdout to find ROOT TTree summaries and digest them.
1494  """
1495  outlines = stdout.splitlines()
1496  nlines = len(outlines) - 1
1497  summaries = {}
1498  global h_count_re
1499 
1500  pos = 0
1501  while pos < nlines:
1502  summ = {}
1503  # find first line of block:
1504  match = h_count_re.search(outlines[pos])
1505  while pos < nlines and not match:
1506  pos += 1
1507  match = h_count_re.search(outlines[pos])
1508  if match:
1509  summ, pos = parseHistosSummary(outlines, pos)
1510  summaries.update(summ)
1511  return summaries
1512 
1513 

◆ findTTreeSummaries()

def GaudiTesting.BaseTest.findTTreeSummaries (   stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 1272 of file BaseTest.py.

1272 def findTTreeSummaries(stdout):
1273  """
1274  Scan stdout to find ROOT TTree summaries and digest them.
1275  """
1276  stars = re.compile(r"^\*+$")
1277  outlines = stdout.splitlines()
1278  nlines = len(outlines)
1279  trees = {}
1280 
1281  i = 0
1282  while i < nlines: # loop over the output
1283  # look for
1284  while i < nlines and not stars.match(outlines[i]):
1285  i += 1
1286  if i < nlines:
1287  tree, i = _parseTTreeSummary(outlines, i)
1288  if tree:
1289  trees[tree["Name"]] = tree
1290 
1291  return trees
1292 
1293 

◆ getCmpFailingValues()

def GaudiTesting.BaseTest.getCmpFailingValues (   reference,
  to_check,
  fail_path 
)

Definition at line 1327 of file BaseTest.py.

1327 def getCmpFailingValues(reference, to_check, fail_path):
1328  c = to_check
1329  r = reference
1330  for k in fail_path:
1331  c = c.get(k, None)
1332  r = r.get(k, None)
1333  if c is None or r is None:
1334  break # one of the dictionaries is not deep enough
1335  return (fail_path, r, c)
1336 
1337 
1338 # signature of the print-out of the histograms

◆ GetPlatform()

def GaudiTesting.BaseTest.GetPlatform (   self)
Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.

Definition at line 1514 of file BaseTest.py.

1514 def GetPlatform(self):
1515  """
1516  Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
1517  """
1518  arch = "None"
1519  # check architecture name
1520  if "BINARY_TAG" in os.environ:
1521  arch = os.environ["BINARY_TAG"]
1522  elif "CMTCONFIG" in os.environ:
1523  arch = os.environ["CMTCONFIG"]
1524  elif "SCRAM_ARCH" in os.environ:
1525  arch = os.environ["SCRAM_ARCH"]
1526  elif os.environ.get("ENV_CMAKE_BUILD_TYPE", "") in (
1527  "Debug",
1528  "FastDebug",
1529  "Developer",
1530  ):
1531  arch = "dummy-dbg"
1532  elif os.environ.get("ENV_CMAKE_BUILD_TYPE", "") in (
1533  "Release",
1534  "MinSizeRel",
1535  "RelWithDebInfo",
1536  "",
1537  ): # RelWithDebInfo == -O2 -g -DNDEBUG
1538  arch = "dummy-opt"
1539  return arch
1540 
1541 

◆ isWinPlatform()

def GaudiTesting.BaseTest.isWinPlatform (   self)
Return True if the current platform is Windows.

This function was needed because of the change in the CMTCONFIG format,
from win32_vc71_dbg to i686-winxp-vc9-dbg.

Definition at line 1542 of file BaseTest.py.

1542 def isWinPlatform(self):
1543  """
1544  Return True if the current platform is Windows.
1545 
1546  This function was needed because of the change in the CMTCONFIG format,
1547  from win32_vc71_dbg to i686-winxp-vc9-dbg.
1548  """
1549  platform = GetPlatform(self)
1550  return "winxp" in platform or platform.startswith("win")
1551 
1552 

◆ kill_tree()

def GaudiTesting.BaseTest.kill_tree (   ppid,
  sig 
)
Send a signal to a process and all its child processes (starting from the
leaves).

Definition at line 77 of file BaseTest.py.

77 def kill_tree(ppid, sig):
78  """
79  Send a signal to a process and all its child processes (starting from the
80  leaves).
81  """
82  log = logging.getLogger("kill_tree")
83  ps_cmd = ["ps", "--no-headers", "-o", "pid", "--ppid", str(ppid)]
84  # Note: start in a clean env to avoid a freeze with libasan.so
85  # See https://sourceware.org/bugzilla/show_bug.cgi?id=27653
86  get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE, env={})
87  children = map(int, get_children.communicate()[0].split())
88  for child in children:
89  kill_tree(child, sig)
90  try:
91  log.debug("killing process %d", ppid)
92  os.kill(ppid, sig)
93  except OSError as err:
94  if err.errno != 3: # No such process
95  raise
96  log.debug("no such process %d", ppid)
97 
98 
99 # -------------------------------------------------------------------------#
100 
101 

◆ parseHistosSummary()

def GaudiTesting.BaseTest.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 1423 of file BaseTest.py.

1423 def parseHistosSummary(lines, pos):
1424  """
1425  Extract the histograms infos from the lines starting at pos.
1426  Returns the position of the first line after the summary block.
1427  """
1428  global h_count_re
1429  h_table_head = re.compile(
1430  r'SUCCESS\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"'
1431  )
1432  h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
1433 
1434  nlines = len(lines)
1435 
1436  # decode header
1437  m = h_count_re.search(lines[pos])
1438  name = m.group(1).strip()
1439  total = int(m.group(2))
1440  header = {}
1441  for k, v in [x.split("=") for x in m.group(3).split()]:
1442  header[k] = int(v)
1443  pos += 1
1444  header["Total"] = total
1445 
1446  summ = {}
1447  while pos < nlines:
1448  m = h_table_head.search(lines[pos])
1449  if m:
1450  t, d = m.groups(1) # type and directory
1451  t = t.replace(" profile", "Prof")
1452  pos += 1
1453  if pos < nlines:
1454  l = lines[pos]
1455  else:
1456  l = ""
1457  cont = {}
1458  if l.startswith(" | ID"):
1459  # table format
1460  titles = [x.strip() for x in l.split("|")][1:]
1461  pos += 1
1462  while pos < nlines and lines[pos].startswith(" |"):
1463  l = lines[pos]
1464  values = [x.strip() for x in l.split("|")][1:]
1465  hcont = {}
1466  for i in range(len(titles)):
1467  hcont[titles[i]] = values[i]
1468  cont[hcont["ID"]] = hcont
1469  pos += 1
1470  elif l.startswith(" ID="):
1471  while pos < nlines and lines[pos].startswith(" ID="):
1472  values = [
1473  x.strip() for x in h_short_summ.search(lines[pos]).groups()
1474  ]
1475  cont[values[0]] = values
1476  pos += 1
1477  else: # not interpreted
1478  raise RuntimeError("Cannot understand line %d: '%s'" % (pos, l))
1479  if d not in summ:
1480  summ[d] = {}
1481  summ[d][t] = cont
1482  summ[d]["header"] = header
1483  else:
1484  break
1485  if not summ:
1486  # If the full table is not present, we use only the header
1487  summ[name] = {"header": header}
1488  return summ, pos
1489 
1490 

◆ RationalizePath()

def GaudiTesting.BaseTest.RationalizePath (   p)
Function used to normalize the used path

Definition at line 781 of file BaseTest.py.

781 def RationalizePath(p):
782  """
783  Function used to normalize the used path
784  """
785  newPath = os.path.normpath(os.path.expandvars(p))
786  if os.path.exists(newPath):
787  p = os.path.realpath(newPath)
788  return p
789 
790 

◆ ROOT6WorkAroundEnabled()

def GaudiTesting.BaseTest.ROOT6WorkAroundEnabled (   id = None)

Definition at line 773 of file BaseTest.py.

773  def ROOT6WorkAroundEnabled(id=None):
774  # dummy implementation
775  return False
776 
777 
778 # --------------------------------- TOOLS ---------------------------------#
779 
780 

◆ sanitize_for_xml()

def GaudiTesting.BaseTest.sanitize_for_xml (   data)
Take a string with invalid ASCII/UTF characters and quote them so that the
string can be used in an XML text.

>>> sanitize_for_xml('this is \x1b')
'this is [NON-XML-CHAR-0x1B]'

Definition at line 50 of file BaseTest.py.

50 def sanitize_for_xml(data):
51  """
52  Take a string with invalid ASCII/UTF characters and quote them so that the
53  string can be used in an XML text.
54 
55  >>> sanitize_for_xml('this is \x1b')
56  'this is [NON-XML-CHAR-0x1B]'
57  """
58  bad_chars = re.compile("[\x00-\x08\x0b\x0c\x0e-\x1f\ud800-\udfff\ufffe\uffff]")
59 
60  def quote(match):
61  "helper function"
62  return "".join("[NON-XML-CHAR-0x%2X]" % ord(c) for c in match.group())
63 
64  return bad_chars.sub(quote, data)
65 
66 

◆ which()

def GaudiTesting.BaseTest.which (   executable)
Locates an executable in the executables path ($PATH) and returns the full
path to it.  An application is looked for with or without the '.exe' suffix.
If the executable cannot be found, None is returned

Definition at line 791 of file BaseTest.py.

791 def which(executable):
792  """
793  Locates an executable in the executables path ($PATH) and returns the full
794  path to it. An application is looked for with or without the '.exe' suffix.
795  If the executable cannot be found, None is returned
796  """
797  if os.path.isabs(executable):
798  if not os.path.isfile(executable):
799  if executable.endswith(".exe"):
800  if os.path.isfile(executable[:-4]):
801  return executable[:-4]
802  else:
803  executable = os.path.split(executable)[1]
804  else:
805  return executable
806  for d in os.environ.get("PATH").split(os.pathsep):
807  fullpath = os.path.join(d, executable)
808  if os.path.isfile(fullpath):
809  return fullpath
810  elif executable.endswith(".exe") and os.path.isfile(fullpath[:-4]):
811  return fullpath[:-4]
812  return None
813 
814 
815 # -------------------------------------------------------------------------#
816 # ----------------------------- Result Classe -----------------------------#
817 # -------------------------------------------------------------------------#
818 
819 

Variable Documentation

◆ __processLine__

GaudiTesting.BaseTest.__processLine__
private

Definition at line 1017 of file BaseTest.py.

◆ h_count_re

GaudiTesting.BaseTest.h_count_re

Definition at line 1339 of file BaseTest.py.

◆ lineSkipper

GaudiTesting.BaseTest.lineSkipper

Definition at line 1112 of file BaseTest.py.

◆ maskPointers

GaudiTesting.BaseTest.maskPointers

Definition at line 1011 of file BaseTest.py.

◆ normalizeDate

GaudiTesting.BaseTest.normalizeDate

Definition at line 1012 of file BaseTest.py.

◆ normalizeEOL

GaudiTesting.BaseTest.normalizeEOL

Definition at line 1016 of file BaseTest.py.

◆ normalizeExamples

GaudiTesting.BaseTest.normalizeExamples

Definition at line 1225 of file BaseTest.py.

◆ normalizeTestSuite

GaudiTesting.BaseTest.normalizeTestSuite

Definition at line 1067 of file BaseTest.py.

◆ OUTPUT_LIMIT

GaudiTesting.BaseTest.OUTPUT_LIMIT

Definition at line 47 of file BaseTest.py.

◆ regexps

list GaudiTesting.BaseTest.regexps

Definition at line 1154 of file BaseTest.py.

◆ SKIP_RETURN_CODE

GaudiTesting.BaseTest.SKIP_RETURN_CODE

Definition at line 44 of file BaseTest.py.

◆ skipEmptyLines

GaudiTesting.BaseTest.skipEmptyLines

Definition at line 1019 of file BaseTest.py.

MSG::hex
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:282
GaudiTesting.BaseTest.dumpProcs
def dumpProcs(name)
Definition: BaseTest.py:67
GaudiTesting.BaseTest._parseTTreeSummary
def _parseTTreeSummary(lines, pos)
Definition: BaseTest.py:1342
GaudiTesting.BaseTest.sanitize_for_xml
def sanitize_for_xml(data)
Definition: BaseTest.py:50
GaudiTesting.BaseTest.getCmpFailingValues
def getCmpFailingValues(reference, to_check, fail_path)
Definition: BaseTest.py:1327
GaudiTesting.BaseTest._new_backslashreplace_errors
def _new_backslashreplace_errors(exc)
Definition: BaseTest.py:32
GaudiTesting.BaseTest.kill_tree
def kill_tree(ppid, sig)
Definition: BaseTest.py:77
Containers::map
struct GAUDI_API map
Parametrisation class for map-like implementation.
Definition: KeyedObjectManager.h:35
GaudiTesting.BaseTest.parseHistosSummary
def parseHistosSummary(lines, pos)
Definition: BaseTest.py:1423
GaudiTesting.BaseTest.isWinPlatform
def isWinPlatform(self)
Definition: BaseTest.py:1542
GaudiTesting.BaseTest.which
def which(executable)
Definition: BaseTest.py:791
GaudiTesting.BaseTest.cmpTreesDicts
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: BaseTest.py:1294
GaudiTesting.BaseTest.RationalizePath
def RationalizePath(p)
Definition: BaseTest.py:781
GaudiTesting.BaseTest.findHistosSummaries
def findHistosSummaries(stdout)
Definition: BaseTest.py:1491
GaudiTesting.BaseTest.findTTreeSummaries
def findTTreeSummaries(stdout)
Definition: BaseTest.py:1272
GaudiTesting.BaseTest.ROOT6WorkAroundEnabled
def ROOT6WorkAroundEnabled(id=None)
Definition: BaseTest.py:773
GaudiTesting.BaseTest.GetPlatform
def GetPlatform(self)
Definition: BaseTest.py:1514
Gaudi::Functional::details::zip::range
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
Definition: details.h:98