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