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