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