109467b48Spatrickfrom __future__ import absolute_import 209467b48Spatrickimport inspect 309467b48Spatrickimport os 409467b48Spatrickimport platform 509467b48Spatrickimport sys 609467b48Spatrick 709467b48Spatrickimport lit.Test 809467b48Spatrickimport lit.formats 909467b48Spatrickimport lit.TestingConfig 1009467b48Spatrickimport lit.util 1109467b48Spatrick 1209467b48Spatrick# LitConfig must be a new style class for properties to work 1309467b48Spatrickclass LitConfig(object): 1409467b48Spatrick """LitConfig - Configuration data for a 'lit' test runner instance, shared 1509467b48Spatrick across all tests. 1609467b48Spatrick 1709467b48Spatrick The LitConfig object is also used to communicate with client configuration 1809467b48Spatrick files, it is always passed in as the global variable 'lit' so that 1909467b48Spatrick configuration files can access common functionality and internal components 2009467b48Spatrick easily. 2109467b48Spatrick """ 2209467b48Spatrick 2309467b48Spatrick def __init__(self, progname, path, quiet, 2409467b48Spatrick useValgrind, valgrindLeakCheck, valgrindArgs, 25*d415bd75Srobert noExecute, debug, isWindows, order, 2609467b48Spatrick params, config_prefix = None, 2709467b48Spatrick maxIndividualTestTime = 0, 2809467b48Spatrick parallelism_groups = {}, 2909467b48Spatrick echo_all_commands = False): 3009467b48Spatrick # The name of the test runner. 3109467b48Spatrick self.progname = progname 3209467b48Spatrick # The items to add to the PATH environment variable. 3309467b48Spatrick self.path = [str(p) for p in path] 3409467b48Spatrick self.quiet = bool(quiet) 3509467b48Spatrick self.useValgrind = bool(useValgrind) 3609467b48Spatrick self.valgrindLeakCheck = bool(valgrindLeakCheck) 3709467b48Spatrick self.valgrindUserArgs = list(valgrindArgs) 3809467b48Spatrick self.noExecute = noExecute 3909467b48Spatrick self.debug = debug 4009467b48Spatrick self.isWindows = bool(isWindows) 41*d415bd75Srobert self.order = order 4209467b48Spatrick self.params = dict(params) 4309467b48Spatrick self.bashPath = None 4409467b48Spatrick 4509467b48Spatrick # Configuration files to look for when discovering test suites. 4609467b48Spatrick self.config_prefix = config_prefix or 'lit' 4709467b48Spatrick self.suffixes = ['cfg.py', 'cfg'] 4809467b48Spatrick self.config_names = ['%s.%s' % (self.config_prefix,x) for x in self.suffixes] 4909467b48Spatrick self.site_config_names = ['%s.site.%s' % (self.config_prefix,x) for x in self.suffixes] 5009467b48Spatrick self.local_config_names = ['%s.local.%s' % (self.config_prefix,x) for x in self.suffixes] 5109467b48Spatrick 5209467b48Spatrick self.numErrors = 0 5309467b48Spatrick self.numWarnings = 0 5409467b48Spatrick 5509467b48Spatrick self.valgrindArgs = [] 5609467b48Spatrick if self.useValgrind: 5709467b48Spatrick self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no', 5809467b48Spatrick '--tool=memcheck', '--trace-children=yes', 5909467b48Spatrick '--error-exitcode=123'] 6009467b48Spatrick if self.valgrindLeakCheck: 6109467b48Spatrick self.valgrindArgs.append('--leak-check=full') 6209467b48Spatrick else: 6309467b48Spatrick # The default is 'summary'. 6409467b48Spatrick self.valgrindArgs.append('--leak-check=no') 6509467b48Spatrick self.valgrindArgs.extend(self.valgrindUserArgs) 6609467b48Spatrick 6709467b48Spatrick self.maxIndividualTestTime = maxIndividualTestTime 6809467b48Spatrick self.parallelism_groups = parallelism_groups 6909467b48Spatrick self.echo_all_commands = echo_all_commands 7009467b48Spatrick 7109467b48Spatrick @property 7209467b48Spatrick def maxIndividualTestTime(self): 7309467b48Spatrick """ 7409467b48Spatrick Interface for getting maximum time to spend executing 7509467b48Spatrick a single test 7609467b48Spatrick """ 7709467b48Spatrick return self._maxIndividualTestTime 7809467b48Spatrick 7909467b48Spatrick @property 8009467b48Spatrick def maxIndividualTestTimeIsSupported(self): 8109467b48Spatrick """ 8209467b48Spatrick Returns a tuple (<supported> , <error message>) 8309467b48Spatrick where 8409467b48Spatrick `<supported>` is True if setting maxIndividualTestTime is supported 8509467b48Spatrick on the current host, returns False otherwise. 8609467b48Spatrick `<error message>` is an empty string if `<supported>` is True, 8709467b48Spatrick otherwise is contains a string describing why setting 8809467b48Spatrick maxIndividualTestTime is not supported. 8909467b48Spatrick """ 9009467b48Spatrick return lit.util.killProcessAndChildrenIsSupported() 9109467b48Spatrick 9209467b48Spatrick @maxIndividualTestTime.setter 9309467b48Spatrick def maxIndividualTestTime(self, value): 9409467b48Spatrick """ 9509467b48Spatrick Interface for setting maximum time to spend executing 9609467b48Spatrick a single test 9709467b48Spatrick """ 9809467b48Spatrick if not isinstance(value, int): 9909467b48Spatrick self.fatal('maxIndividualTestTime must set to a value of type int.') 10009467b48Spatrick self._maxIndividualTestTime = value 10109467b48Spatrick if self.maxIndividualTestTime > 0: 10209467b48Spatrick # The current implementation needs psutil on some platforms to set 10309467b48Spatrick # a timeout per test. Check it's available. 10409467b48Spatrick # See lit.util.killProcessAndChildren() 10509467b48Spatrick supported, errormsg = self.maxIndividualTestTimeIsSupported 10609467b48Spatrick if not supported: 10709467b48Spatrick self.fatal('Setting a timeout per test not supported. ' + 10809467b48Spatrick errormsg) 10909467b48Spatrick elif self.maxIndividualTestTime < 0: 11009467b48Spatrick self.fatal('The timeout per test must be >= 0 seconds') 11109467b48Spatrick 11209467b48Spatrick def load_config(self, config, path): 11309467b48Spatrick """load_config(config, path) - Load a config object from an alternate 11409467b48Spatrick path.""" 11509467b48Spatrick if self.debug: 11609467b48Spatrick self.note('load_config from %r' % path) 11709467b48Spatrick config.load_from_path(path, self) 11809467b48Spatrick return config 11909467b48Spatrick 12009467b48Spatrick def getBashPath(self): 12109467b48Spatrick """getBashPath - Get the path to 'bash'""" 12209467b48Spatrick if self.bashPath is not None: 12309467b48Spatrick return self.bashPath 12409467b48Spatrick 12509467b48Spatrick self.bashPath = lit.util.which('bash', os.pathsep.join(self.path)) 12609467b48Spatrick if self.bashPath is None: 12709467b48Spatrick self.bashPath = lit.util.which('bash') 12809467b48Spatrick 12909467b48Spatrick if self.bashPath is None: 13009467b48Spatrick self.bashPath = '' 13109467b48Spatrick 13209467b48Spatrick # Check whether the found version of bash is able to cope with paths in 13309467b48Spatrick # the host path format. If not, don't return it as it can't be used to 13409467b48Spatrick # run scripts. For example, WSL's bash.exe requires '/mnt/c/foo' rather 13509467b48Spatrick # than 'C:\\foo' or 'C:/foo'. 13609467b48Spatrick if self.isWindows and self.bashPath: 13709467b48Spatrick command = [self.bashPath, '-c', 13809467b48Spatrick '[[ -f "%s" ]]' % self.bashPath.replace('\\', '\\\\')] 13909467b48Spatrick _, _, exitCode = lit.util.executeCommand(command) 14009467b48Spatrick if exitCode: 14109467b48Spatrick self.note('bash command failed: %s' % ( 14209467b48Spatrick ' '.join('"%s"' % c for c in command))) 14309467b48Spatrick self.bashPath = '' 14409467b48Spatrick 14509467b48Spatrick if not self.bashPath: 14609467b48Spatrick self.warning('Unable to find a usable version of bash.') 14709467b48Spatrick 14809467b48Spatrick return self.bashPath 14909467b48Spatrick 15009467b48Spatrick def getToolsPath(self, dir, paths, tools): 15109467b48Spatrick if dir is not None and os.path.isabs(dir) and os.path.isdir(dir): 15209467b48Spatrick if not lit.util.checkToolsPath(dir, tools): 15309467b48Spatrick return None 15409467b48Spatrick else: 15509467b48Spatrick dir = lit.util.whichTools(tools, paths) 15609467b48Spatrick 15709467b48Spatrick # bash 15809467b48Spatrick self.bashPath = lit.util.which('bash', dir) 15909467b48Spatrick if self.bashPath is None: 16009467b48Spatrick self.bashPath = '' 16109467b48Spatrick 16209467b48Spatrick return dir 16309467b48Spatrick 16409467b48Spatrick def _write_message(self, kind, message): 16509467b48Spatrick # Get the file/line where this message was generated. 16609467b48Spatrick f = inspect.currentframe() 16709467b48Spatrick # Step out of _write_message, and then out of wrapper. 16809467b48Spatrick f = f.f_back.f_back 16973471bf0Spatrick file = os.path.abspath(inspect.getsourcefile(f)) 17073471bf0Spatrick line = inspect.getlineno(f) 17173471bf0Spatrick sys.stderr.write('%s: %s:%d: %s: %s\n' % (self.progname, file, line, 17209467b48Spatrick kind, message)) 173*d415bd75Srobert if self.isWindows: 174*d415bd75Srobert # In a git bash terminal, the writes to sys.stderr aren't visible 175*d415bd75Srobert # on screen immediately. Flush them here to avoid broken/misoredered 176*d415bd75Srobert # output. 177*d415bd75Srobert sys.stderr.flush() 178*d415bd75Srobert 179*d415bd75Srobert def substitute(self, string): 180*d415bd75Srobert """substitute - Interpolate params into a string""" 181*d415bd75Srobert try: 182*d415bd75Srobert return string % self.params 183*d415bd75Srobert except KeyError as e: 184*d415bd75Srobert key, = e.args 185*d415bd75Srobert self.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % ( 186*d415bd75Srobert key,key)) 18709467b48Spatrick 18809467b48Spatrick def note(self, message): 18909467b48Spatrick if not self.quiet: 19009467b48Spatrick self._write_message('note', message) 19109467b48Spatrick 19209467b48Spatrick def warning(self, message): 19309467b48Spatrick if not self.quiet: 19409467b48Spatrick self._write_message('warning', message) 19509467b48Spatrick self.numWarnings += 1 19609467b48Spatrick 19709467b48Spatrick def error(self, message): 19809467b48Spatrick self._write_message('error', message) 19909467b48Spatrick self.numErrors += 1 20009467b48Spatrick 20109467b48Spatrick def fatal(self, message): 20209467b48Spatrick self._write_message('fatal', message) 20309467b48Spatrick sys.exit(2) 204