[lit] Implement support of per test timeout in lit.
[oota-llvm.git] / utils / lit / lit / LitConfig.py
1 from __future__ import absolute_import
2 import inspect
3 import os
4 import sys
5
6 import lit.Test
7 import lit.formats
8 import lit.TestingConfig
9 import lit.util
10
11 # LitConfig must be a new style class for properties to work
12 class 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)