Skip to content

Commit b0151fd

Browse files
committed
runner: Introduce TestInfo to wrap the raw test json
The constructor now performs appropriate verification on the json configuration, rather than open-coding it in get_all_test_info(). Users of the dictionary-like json are altered to use attributes. An all_instances() method is added to return test instances for all suitable environments, optionally with a subset filter. This is used in preference to three open-coded loops, generating instance names. Signed-off-by: Andrew Cooper <[email protected]>
1 parent 7ee7c07 commit b0151fd

File tree

2 files changed

+65
-37
lines changed

2 files changed

+65
-37
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ max-parents=7
327327
max-attributes=250
328328

329329
# Minimum number of public methods for a class (see R0903).
330-
min-public-methods=2
330+
min-public-methods=0
331331

332332
# Maximum number of public methods for a class (see R0904).
333333
max-public-methods=20

xtf-runner

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,57 @@ all_environments = pv_environments + hvm_environments
4141
class RunnerError(Exception):
4242
""" Errors relating to xtf-runner itself """
4343

44+
class TestInfo(object):
45+
""" Object representing a tests info.json, in a more convenient form. """
46+
47+
def __init__(self, test_json):
48+
"""Parse and verify 'test_json'.
49+
50+
May raise KeyError, TypeError or ValueError.
51+
"""
52+
53+
name = test_json["name"]
54+
if not isinstance(name, basestring):
55+
raise TypeError("Expected string for 'name', got '%s'"
56+
% (type(name), ))
57+
self.name = name
58+
59+
cat = test_json["category"]
60+
if not isinstance(cat, basestring):
61+
raise TypeError("Expected string for 'category', got '%s'"
62+
% (type(cat), ))
63+
if not cat in all_categories:
64+
raise ValueError("Unknown category '%s'" % (cat, ))
65+
self.cat = cat
66+
67+
envs = test_json["environments"]
68+
if not isinstance(envs, list):
69+
raise TypeError("Expected list for 'environments', got '%s'"
70+
% (type(envs), ))
71+
if not envs:
72+
raise ValueError("Expected at least one environment")
73+
for env in envs:
74+
if not env in all_environments:
75+
raise ValueError("Unknown environments '%s'" % (env, ))
76+
self.envs = envs
77+
78+
def all_instances(self, env_filter = None):
79+
"""Return a list of test instances, for each supported environment.
80+
Optionally filtered by env_filter. May return an empty list if
81+
the filter doesn't match any supported environment.
82+
"""
83+
84+
if env_filter:
85+
envs = set(env_filter).intersection(self.envs)
86+
else:
87+
envs = self.envs
88+
89+
return [ "test-%s-%s" % (env, self.name) for env in envs ]
90+
91+
def __repr__(self):
92+
return "TestInfo(%s)" % (self.name, )
93+
94+
4495
# Cached test json from disk
4596
_all_test_info = {}
4697

@@ -54,7 +105,6 @@ def get_all_test_info():
54105
for test in os.listdir("tests"):
55106

56107
info_file = None
57-
test_json = {}
58108
try:
59109

60110
# Ignore directories which don't have a info.json inside them
@@ -65,26 +115,15 @@ def get_all_test_info():
65115

66116
# Ignore tests which have bad JSON
67117
try:
68-
test_json = json.load(info_file)
69-
except ValueError:
70-
continue
118+
test_info = TestInfo(json.load(info_file))
71119

72-
# Sanity check JSON fields and types
73-
if (not isinstance(test_json.get("name", None), basestring) or
74-
not isinstance(test_json.get("category", None), basestring) or
75-
not isinstance(test_json.get("environments", None), list)):
76-
continue
120+
if test_info.name != test:
121+
continue
77122

78-
# Sanity check JSON values
79-
if test_json["name"] != test:
80-
continue
81-
if test_json["category"] not in all_categories:
123+
except (ValueError, KeyError, TypeError):
82124
continue
83-
for test_env in test_json["environments"]:
84-
if test_env not in all_environments:
85-
continue
86125

87-
_all_test_info[test] = test_json
126+
_all_test_info[test] = test_info
88127

89128
finally:
90129
if info_file:
@@ -126,11 +165,11 @@ def list_tests(opts):
126165

127166
info = all_test_info[name]
128167

129-
if cat and info["category"] not in cat:
168+
if cat and info.cat not in cat:
130169
continue
131170

132171
if env:
133-
for test_env in info["environments"]:
172+
for test_env in info.envs:
134173
if test_env in env:
135174
break
136175
else:
@@ -201,31 +240,20 @@ def run_tests(opts):
201240

202241
# If arg is a recognised test name, run every environment
203242
if arg in all_test_names:
204-
205-
info = all_test_info[arg]
206-
207-
for env in info["environments"]:
208-
tests.append("test-%s-%s" % (env, arg))
243+
tests.extend(all_test_info[arg].all_instances())
209244
continue
210245

211246
# If arg is a recognised category, run every included test
212247
if arg in all_categories:
213-
214-
for name, info in all_test_info.iteritems():
215-
216-
if info["category"] == arg:
217-
218-
for env in info["environments"]:
219-
tests.append("test-%s-%s" % (env, name))
248+
for info in all_test_info.values():
249+
if info.cat == arg:
250+
tests.extend(info.all_instances())
220251
continue
221252

222253
# If arg is a recognised environment, run every included test
223254
if arg in all_environments:
224-
225-
for name, info in all_test_info.iteritems():
226-
227-
if arg in info["environments"]:
228-
tests.append("test-%s-%s" % (arg, name))
255+
for info in all_test_info.values():
256+
tests.extend(info.all_instances(env_filter = [arg]))
229257
continue
230258

231259
parts = arg.split('-', 2)

0 commit comments

Comments
 (0)