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, singleProcess, 25 params, config_prefix = None, 26 maxIndividualTestTime = 0, 27 maxFailures = None, 28 parallelism_groups = {}, 29 echo_all_commands = False): 30 # The name of the test runner. 31 self.progname = progname 32 # The items to add to the PATH environment variable. 33 self.path = [str(p) for p in path] 34 self.quiet = bool(quiet) 35 self.useValgrind = bool(useValgrind) 36 self.valgrindLeakCheck = bool(valgrindLeakCheck) 37 self.valgrindUserArgs = list(valgrindArgs) 38 self.noExecute = noExecute 39 self.debug = debug 40 self.singleProcess = singleProcess 41 self.isWindows = bool(isWindows) 42 self.params = dict(params) 43 self.bashPath = None 44 45 # Configuration files to look for when discovering test suites. 46 self.config_prefix = config_prefix or 'lit' 47 self.suffixes = ['cfg.py', 'cfg'] 48 self.config_names = ['%s.%s' % (self.config_prefix,x) for x in self.suffixes] 49 self.site_config_names = ['%s.site.%s' % (self.config_prefix,x) for x in self.suffixes] 50 self.local_config_names = ['%s.local.%s' % (self.config_prefix,x) for x in self.suffixes] 51 52 self.numErrors = 0 53 self.numWarnings = 0 54 55 self.valgrindArgs = [] 56 if self.useValgrind: 57 self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no', 58 '--tool=memcheck', '--trace-children=yes', 59 '--error-exitcode=123'] 60 if self.valgrindLeakCheck: 61 self.valgrindArgs.append('--leak-check=full') 62 else: 63 # The default is 'summary'. 64 self.valgrindArgs.append('--leak-check=no') 65 self.valgrindArgs.extend(self.valgrindUserArgs) 66 67 self.maxIndividualTestTime = maxIndividualTestTime 68 self.maxFailures = maxFailures 69 self.parallelism_groups = parallelism_groups 70 self.echo_all_commands = echo_all_commands 71 72 @property 73 def maxIndividualTestTime(self): 74 """ 75 Interface for getting maximum time to spend executing 76 a single test 77 """ 78 return self._maxIndividualTestTime 79 80 @maxIndividualTestTime.setter 81 def maxIndividualTestTime(self, value): 82 """ 83 Interface for setting maximum time to spend executing 84 a single test 85 """ 86 if not isinstance(value, int): 87 self.fatal('maxIndividualTestTime must set to a value of type int.') 88 self._maxIndividualTestTime = value 89 if self.maxIndividualTestTime > 0: 90 # The current implementation needs psutil to set 91 # a timeout per test. Check it's available. 92 # See lit.util.killProcessAndChildren() 93 try: 94 import psutil # noqa: F401 95 except ImportError: 96 self.fatal("Setting a timeout per test requires the" 97 " Python psutil module but it could not be" 98 " found. Try installing it via pip or via" 99 " your operating system's package manager.") 100 elif self.maxIndividualTestTime < 0: 101 self.fatal('The timeout per test must be >= 0 seconds') 102 103 def load_config(self, config, path): 104 """load_config(config, path) - Load a config object from an alternate 105 path.""" 106 if self.debug: 107 self.note('load_config from %r' % path) 108 config.load_from_path(path, self) 109 return config 110 111 def getBashPath(self): 112 """getBashPath - Get the path to 'bash'""" 113 if self.bashPath is not None: 114 return self.bashPath 115 116 self.bashPath = lit.util.which('bash', os.pathsep.join(self.path)) 117 if self.bashPath is None: 118 self.bashPath = lit.util.which('bash') 119 120 if self.bashPath is None: 121 self.bashPath = '' 122 123 return self.bashPath 124 125 def getToolsPath(self, dir, paths, tools): 126 if dir is not None and os.path.isabs(dir) and os.path.isdir(dir): 127 if not lit.util.checkToolsPath(dir, tools): 128 return None 129 else: 130 dir = lit.util.whichTools(tools, paths) 131 132 # bash 133 self.bashPath = lit.util.which('bash', dir) 134 if self.bashPath is None: 135 self.bashPath = '' 136 137 return dir 138 139 def _write_message(self, kind, message): 140 # Get the file/line where this message was generated. 141 f = inspect.currentframe() 142 # Step out of _write_message, and then out of wrapper. 143 f = f.f_back.f_back 144 file,line,_,_,_ = inspect.getframeinfo(f) 145 location = '%s:%d' % (file, line) 146 147 sys.stderr.write('%s: %s: %s: %s\n' % (self.progname, location, 148 kind, message)) 149 150 def note(self, message): 151 self._write_message('note', message) 152 153 def warning(self, message): 154 self._write_message('warning', message) 155 self.numWarnings += 1 156 157 def error(self, message): 158 self._write_message('error', message) 159 self.numErrors += 1 160 161 def fatal(self, message): 162 self._write_message('fatal', message) 163 sys.exit(2) 164