The Gaudi Framework  master (37c0b60a)
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 1349 of file BaseTest.py.

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

◆ 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 1301 of file BaseTest.py.

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

◆ 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 1498 of file BaseTest.py.

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

◆ findTTreeSummaries()

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

Definition at line 1279 of file BaseTest.py.

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

◆ getCmpFailingValues()

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

Definition at line 1334 of file BaseTest.py.

1334 def getCmpFailingValues(reference, to_check, fail_path):
1335  c = to_check
1336  r = reference
1337  for k in fail_path:
1338  c = c.get(k, None)
1339  r = r.get(k, None)
1340  if c is None or r is None:
1341  break # one of the dictionaries is not deep enough
1342  return (fail_path, r, c)
1343 
1344 
1345 # 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 1521 of file BaseTest.py.

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

◆ 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 1549 of file BaseTest.py.

1549 def isWinPlatform(self):
1550  """
1551  Return True if the current platform is Windows.
1552 
1553  This function was needed because of the change in the CMTCONFIG format,
1554  from win32_vc71_dbg to i686-winxp-vc9-dbg.
1555  """
1556  platform = GetPlatform(self)
1557  return "winxp" in platform or platform.startswith("win")
1558 
1559 

◆ 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 1430 of file BaseTest.py.

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

◆ 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  p = os.path.expandvars(p)
786 
787  # handle the special case "path/to/file:some_suffix"
788  suffix = ""
789  if ":" in p:
790  p, suffix = p.rsplit(":", 1)
791  suffix = f":{suffix}"
792 
793  if os.path.exists(p):
794  p = os.path.realpath(p)
795  return p + suffix
796 
797 

◆ 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 798 of file BaseTest.py.

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

Variable Documentation

◆ __processLine__

GaudiTesting.BaseTest.__processLine__
private

Definition at line 1024 of file BaseTest.py.

◆ h_count_re

GaudiTesting.BaseTest.h_count_re

Definition at line 1346 of file BaseTest.py.

◆ lineSkipper

GaudiTesting.BaseTest.lineSkipper

Definition at line 1119 of file BaseTest.py.

◆ maskPointers

GaudiTesting.BaseTest.maskPointers

Definition at line 1018 of file BaseTest.py.

◆ normalizeDate

GaudiTesting.BaseTest.normalizeDate

Definition at line 1019 of file BaseTest.py.

◆ normalizeEOL

GaudiTesting.BaseTest.normalizeEOL

Definition at line 1023 of file BaseTest.py.

◆ normalizeExamples

GaudiTesting.BaseTest.normalizeExamples

Definition at line 1232 of file BaseTest.py.

◆ normalizeTestSuite

GaudiTesting.BaseTest.normalizeTestSuite

Definition at line 1074 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 1161 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 1026 of file BaseTest.py.

MSG::hex
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:281
GaudiTesting.BaseTest.dumpProcs
def dumpProcs(name)
Definition: BaseTest.py:67
GaudiTesting.BaseTest._parseTTreeSummary
def _parseTTreeSummary(lines, pos)
Definition: BaseTest.py:1349
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:1334
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:1430
GaudiTesting.BaseTest.isWinPlatform
def isWinPlatform(self)
Definition: BaseTest.py:1549
GaudiTesting.BaseTest.which
def which(executable)
Definition: BaseTest.py:798
GaudiTesting.BaseTest.cmpTreesDicts
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: BaseTest.py:1301
GaudiTesting.BaseTest.RationalizePath
def RationalizePath(p)
Definition: BaseTest.py:781
GaudiTesting.BaseTest.findHistosSummaries
def findHistosSummaries(stdout)
Definition: BaseTest.py:1498
GaudiTesting.BaseTest.findTTreeSummaries
def findTTreeSummaries(stdout)
Definition: BaseTest.py:1279
GaudiTesting.BaseTest.ROOT6WorkAroundEnabled
def ROOT6WorkAroundEnabled(id=None)
Definition: BaseTest.py:773
GaudiTesting.BaseTest.GetPlatform
def GetPlatform(self)
Definition: BaseTest.py:1521
Gaudi::Functional::details::zip::range
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
Definition: details.h:97