• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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