[lit] Support parsing scripts with inconsistent or invalid encodings.
[oota-llvm.git] / utils / lit / lit / discovery.py
1 """
2 Test discovery functions.
3 """
4
5 import os
6 import sys
7
8 from lit.TestingConfig import TestingConfig
9 from lit import LitConfig, Test
10
11 def dirContainsTestSuite(path, lit_config):
12     cfgpath = os.path.join(path, lit_config.site_config_name)
13     if os.path.exists(cfgpath):
14         return cfgpath
15     cfgpath = os.path.join(path, lit_config.config_name)
16     if os.path.exists(cfgpath):
17         return cfgpath
18
19 def getTestSuite(item, litConfig, cache):
20     """getTestSuite(item, litConfig, cache) -> (suite, relative_path)
21
22     Find the test suite containing @arg item.
23
24     @retval (None, ...) - Indicates no test suite contains @arg item.
25     @retval (suite, relative_path) - The suite that @arg item is in, and its
26     relative path inside that suite.
27     """
28     def search1(path):
29         # Check for a site config or a lit config.
30         cfgpath = dirContainsTestSuite(path, litConfig)
31
32         # If we didn't find a config file, keep looking.
33         if not cfgpath:
34             parent,base = os.path.split(path)
35             if parent == path:
36                 return (None, ())
37
38             ts, relative = search(parent)
39             return (ts, relative + (base,))
40
41         # We found a test suite, create a new config for it and load it.
42         if litConfig.debug:
43             litConfig.note('loading suite config %r' % cfgpath)
44
45         cfg = TestingConfig.fromdefaults(litConfig)
46         cfg.load_from_path(cfgpath, litConfig)
47         source_root = os.path.realpath(cfg.test_source_root or path)
48         exec_root = os.path.realpath(cfg.test_exec_root or path)
49         return Test.TestSuite(cfg.name, source_root, exec_root, cfg), ()
50
51     def search(path):
52         # Check for an already instantiated test suite.
53         res = cache.get(path)
54         if res is None:
55             cache[path] = res = search1(path)
56         return res
57
58     # Canonicalize the path.
59     item = os.path.realpath(item)
60
61     # Skip files and virtual components.
62     components = []
63     while not os.path.isdir(item):
64         parent,base = os.path.split(item)
65         if parent == item:
66             return (None, ())
67         components.append(base)
68         item = parent
69     components.reverse()
70
71     ts, relative = search(item)
72     return ts, tuple(relative + tuple(components))
73
74 def getLocalConfig(ts, path_in_suite, litConfig, cache):
75     def search1(path_in_suite):
76         # Get the parent config.
77         if not path_in_suite:
78             parent = ts.config
79         else:
80             parent = search(path_in_suite[:-1])
81
82         # Check if there is a local configuration file.
83         source_path = ts.getSourcePath(path_in_suite)
84         cfgpath = os.path.join(source_path, litConfig.local_config_name)
85
86         # If not, just reuse the parent config.
87         if not os.path.exists(cfgpath):
88             return parent
89
90         # Otherwise, copy the current config and load the local configuration
91         # file into it.
92         config = parent.clone()
93         if litConfig.debug:
94             litConfig.note('loading local config %r' % cfgpath)
95         config.load_from_path(cfgpath, litConfig)
96         return config
97
98     def search(path_in_suite):
99         key = (ts, path_in_suite)
100         res = cache.get(key)
101         if res is None:
102             cache[key] = res = search1(path_in_suite)
103         return res
104
105     return search(path_in_suite)
106
107 def getTests(path, litConfig, testSuiteCache, localConfigCache):
108     # Find the test suite for this input and its relative path.
109     ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
110     if ts is None:
111         litConfig.warning('unable to find test suite for %r' % path)
112         return (),()
113
114     if litConfig.debug:
115         litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
116                                                         path_in_suite))
117
118     return ts, getTestsInSuite(ts, path_in_suite, litConfig,
119                                testSuiteCache, localConfigCache)
120
121 def getTestsInSuite(ts, path_in_suite, litConfig,
122                     testSuiteCache, localConfigCache):
123     # Check that the source path exists (errors here are reported by the
124     # caller).
125     source_path = ts.getSourcePath(path_in_suite)
126     if not os.path.exists(source_path):
127         return
128
129     # Check if the user named a test directly.
130     if not os.path.isdir(source_path):
131         lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
132         yield Test.Test(ts, path_in_suite, lc)
133         return
134
135     # Otherwise we have a directory to search for tests, start by getting the
136     # local configuration.
137     lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
138
139     # Search for tests.
140     if lc.test_format is not None:
141         for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
142                                                       litConfig, lc):
143             yield res
144
145     # Search subdirectories.
146     for filename in os.listdir(source_path):
147         # FIXME: This doesn't belong here?
148         if filename in ('Output', '.svn', '.git') or filename in lc.excludes:
149             continue
150
151         # Ignore non-directories.
152         file_sourcepath = os.path.join(source_path, filename)
153         if not os.path.isdir(file_sourcepath):
154             continue
155
156         # Check for nested test suites, first in the execpath in case there is a
157         # site configuration and then in the source path.
158         subpath = path_in_suite + (filename,)
159         file_execpath = ts.getExecPath(subpath)
160         if dirContainsTestSuite(file_execpath, litConfig):
161             sub_ts, subpath_in_suite = getTestSuite(file_execpath, litConfig,
162                                                     testSuiteCache)
163         elif dirContainsTestSuite(file_sourcepath, litConfig):
164             sub_ts, subpath_in_suite = getTestSuite(file_sourcepath, litConfig,
165                                                     testSuiteCache)
166         else:
167             sub_ts = None
168
169         # If the this directory recursively maps back to the current test suite,
170         # disregard it (this can happen if the exec root is located inside the
171         # current test suite, for example).
172         if sub_ts is ts:
173             continue
174
175         # Otherwise, load from the nested test suite, if present.
176         if sub_ts is not None:
177             subiter = getTestsInSuite(sub_ts, subpath_in_suite, litConfig,
178                                       testSuiteCache, localConfigCache)
179         else:
180             subiter = getTestsInSuite(ts, subpath, litConfig, testSuiteCache,
181                                       localConfigCache)
182
183         N = 0
184         for res in subiter:
185             N += 1
186             yield res
187         if sub_ts and not N:
188             litConfig.warning('test suite %r contained no tests' % sub_ts.name)
189
190 def find_tests_for_inputs(lit_config, inputs):
191     """
192     find_tests_for_inputs(lit_config, inputs) -> [Test]
193
194     Given a configuration object and a list of input specifiers, find all the
195     tests to execute.
196     """
197
198     # Expand '@...' form in inputs.
199     actual_inputs = []
200     for input in inputs:
201         if os.path.exists(input) or not input.startswith('@'):
202             actual_inputs.append(input)
203         else:
204             f = open(input[1:])
205             try:
206                 for ln in f:
207                     ln = ln.strip()
208                     if ln:
209                         actual_inputs.append(ln)
210             finally:
211                 f.close()
212                     
213     # Load the tests from the inputs.
214     tests = []
215     test_suite_cache = {}
216     local_config_cache = {}
217     for input in actual_inputs:
218         prev = len(tests)
219         tests.extend(getTests(input, lit_config,
220                               test_suite_cache, local_config_cache)[1])
221         if prev == len(tests):
222             lit_config.warning('input %r contained no tests' % input)
223
224     # If there were any errors during test discovery, exit now.
225     if lit_config.numErrors:
226         sys.stderr.write('%d errors, exiting.\n' % lit_config.numErrors)
227         sys.exit(2)
228
229     return tests
230
231 def load_test_suite(inputs):
232     import platform
233     import unittest
234     from lit.LitTestCase import LitTestCase
235
236     # Create the global config object.
237     litConfig = LitConfig.LitConfig(progname = 'lit',
238                                     path = [],
239                                     quiet = False,
240                                     useValgrind = False,
241                                     valgrindLeakCheck = False,
242                                     valgrindArgs = [],
243                                     noExecute = False,
244                                     debug = False,
245                                     isWindows = (platform.system()=='Windows'),
246                                     params = {})
247
248     tests = find_tests_for_inputs(litConfig, inputs)
249
250     # Return a unittest test suite which just runs the tests in order.
251     return unittest.TestSuite([LitTestCase(test, litConfig) for test in tests])