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