1from __future__ import absolute_import 2import inspect 3import os 4import sys 5 6import lit.Test 7import lit.formats 8import lit.TestingConfig 9import lit.util 10 11# LitConfig must be a new style class for properties to work 12class LitConfig(object): 13 """LitConfig - Configuration data for a 'lit' test runner instance, shared 14 across all tests. 15 16 The LitConfig object is also used to communicate with client configuration 17 files, it is always passed in as the global variable 'lit' so that 18 configuration files can access common functionality and internal components 19 easily. 20 """ 21 22 def __init__(self, progname, path, quiet, 23 useValgrind, valgrindLeakCheck, valgrindArgs, 24 noExecute, debug, isWindows, 25 params, config_prefix = None, 26 maxIndividualTestTime = 0): 27 # The name of the test runner. 28 self.progname = progname 29 # The items to add to the PATH environment variable. 30 self.path = [str(p) for p in path] 31 self.quiet = bool(quiet) 32 self.useValgrind = bool(useValgrind) 33 self.valgrindLeakCheck = bool(valgrindLeakCheck) 34 self.valgrindUserArgs = list(valgrindArgs) 35 self.noExecute = noExecute 36 self.debug = debug 37 self.isWindows = bool(isWindows) 38 self.params = dict(params) 39 self.bashPath = None 40 41 # Configuration files to look for when discovering test suites. 42 self.config_prefix = config_prefix or 'lit' 43 self.config_name = '%s.cfg' % (self.config_prefix,) 44 self.site_config_name = '%s.site.cfg' % (self.config_prefix,) 45 self.local_config_name = '%s.local.cfg' % (self.config_prefix,) 46 47 self.numErrors = 0 48 self.numWarnings = 0 49 50 self.valgrindArgs = [] 51 if self.useValgrind: 52 self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no', 53 '--tool=memcheck', '--trace-children=yes', 54 '--error-exitcode=123'] 55 if self.valgrindLeakCheck: 56 self.valgrindArgs.append('--leak-check=full') 57 else: 58 # The default is 'summary'. 59 self.valgrindArgs.append('--leak-check=no') 60 self.valgrindArgs.extend(self.valgrindUserArgs) 61 62 self.maxIndividualTestTime = maxIndividualTestTime 63 64 @property 65 def maxIndividualTestTime(self): 66 """ 67 Interface for getting maximum time to spend executing 68 a single test 69 """ 70 return self._maxIndividualTestTime 71 72 @maxIndividualTestTime.setter 73 def maxIndividualTestTime(self, value): 74 """ 75 Interface for setting maximum time to spend executing 76 a single test 77 """ 78 self._maxIndividualTestTime = value 79 if self.maxIndividualTestTime > 0: 80 # The current implementation needs psutil to set 81 # a timeout per test. Check it's available. 82 # See lit.util.killProcessAndChildren() 83 try: 84 import psutil 85 except ImportError: 86 self.fatal("Setting a timeout per test requires the" 87 " Python psutil module but it could not be" 88 " found. Try installing it via pip or via" 89 " your operating system's package manager.") 90 elif self.maxIndividualTestTime < 0: 91 self.fatal('The timeout per test must be >= 0 seconds') 92 93 def load_config(self, config, path): 94 """load_config(config, path) - Load a config object from an alternate 95 path.""" 96 if self.debug: 97 self.note('load_config from %r' % path) 98 config.load_from_path(path, self) 99 return config 100 101 def getBashPath(self): 102 """getBashPath - Get the path to 'bash'""" 103 if self.bashPath is not None: 104 return self.bashPath 105 106 self.bashPath = lit.util.which('bash', os.pathsep.join(self.path)) 107 if self.bashPath is None: 108 self.bashPath = lit.util.which('bash') 109 110 if self.bashPath is None: 111 self.bashPath = '' 112 113 return self.bashPath 114 115 def getToolsPath(self, dir, paths, tools): 116 if dir is not None and os.path.isabs(dir) and os.path.isdir(dir): 117 if not lit.util.checkToolsPath(dir, tools): 118 return None 119 else: 120 dir = lit.util.whichTools(tools, paths) 121 122 # bash 123 self.bashPath = lit.util.which('bash', dir) 124 if self.bashPath is None: 125 self.bashPath = '' 126 127 return dir 128 129 def _write_message(self, kind, message): 130 # Get the file/line where this message was generated. 131 f = inspect.currentframe() 132 # Step out of _write_message, and then out of wrapper. 133 f = f.f_back.f_back 134 file,line,_,_,_ = inspect.getframeinfo(f) 135 location = '%s:%d' % (os.path.basename(file), line) 136 137 sys.stderr.write('%s: %s: %s: %s\n' % (self.progname, location, 138 kind, message)) 139 140 def note(self, message): 141 self._write_message('note', message) 142 143 def warning(self, message): 144 self._write_message('warning', message) 145 self.numWarnings += 1 146 147 def error(self, message): 148 self._write_message('error', message) 149 self.numErrors += 1 150 151 def fatal(self, message): 152 self._write_message('fatal', message) 153 sys.exit(2) 154