1""" 2Test discovery functions. 3""" 4 5import copy 6import os 7import sys 8 9import lit.run 10from lit.TestingConfig import TestingConfig 11from lit import LitConfig, Test 12 13def 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 21def 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 76def 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.deepcopy(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 109def 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 123def 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 192def 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 233def 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]) 256