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