Skip to content

Commit 3828cf5

Browse files
committed
fix after rebase and use more checks_paths
1 parent 66ae283 commit 3828cf5

1 file changed

Lines changed: 70 additions & 42 deletions

File tree

fmf_scheduler.py

Lines changed: 70 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/python3
22
"""
3-
FMF scheduler for nosetests
3+
FMF scheduler via unittest testcases.
4+
Can be used as cli or as an argument to external scheduler like nosetest.
45
Found test via fmf files and creates dynamic testclassed based on that.
56
"""
67

@@ -10,11 +11,12 @@
1011
import imp
1112
import logging
1213
import unittest
14+
import click
1315

1416
from colin.core.target import Target, is_compatible
1517
from colin.core.checks.fmf_check import ExtendedTree, FMFAbstractCheck
16-
from colin.core.constant import PASSED
17-
from colin.core.ruleset.ruleset import get_checks_path
18+
from colin.core.constant import PASSED, COLIN_CHECKS_PATH
19+
from colin.core.ruleset.ruleset import get_checks_paths
1820

1921
from colin.core.checks.dockerfile import DockerfileAbstractCheck, DockerfileLabelAbstractCheck,\
2022
InstructionCountAbstractCheck, InstructionAbstractCheck
@@ -97,41 +99,46 @@ def make_wrapped_fmf_class(node, target):
9799
'backendclass': inernalclass
98100
})
99101

100-
def nosetests_class_fmf_generator(fmfpath, target_name, log_level, ruleset_tree_path=None, filter_names=None, filters=None):
102+
103+
def unittests_class_fmf_generator(fmfpathes, target_name, log_level, ruleset_tree_path=None, filter_names=None, filters=None):
101104
"""
102-
generates dynamic test classes for nosetest or unittest scheduler based on FMF metadata.
105+
generates dynamic test unittest classes based on FMF metadata.
103106
104-
:param fmfpath: path to checks
107+
:param fmfpathes: path to checks
105108
:param target_name: what is the target object
106109
:param log_level:
107110
:param ruleset_tree_path:
108111
:return:
109112
"""
110113
target = Target(target_name, log_level)
111114
test_classes = {}
112-
if not ruleset_tree_path:
113-
ruleset_tree_path = fmfpath
114-
ruleset_metadatatree = ExtendedTree(ruleset_tree_path)
115-
metadatatree = ExtendedTree(fmfpath)
116-
ruleset_metadatatree.references(metadatatree)
117-
for node in ruleset_metadatatree.prune(names=filter_names, filters=filters):
118-
if node.data.get("class") or node.data.get("test"):
119-
logger.debug("node (%s) contains test and class item", node.name)
120-
121-
if node.data.get("class") in CLASS_MAPPING:
122-
logger.debug("Using pure FMF metadata for %s (class %s)", node.name, node.data.get("class"))
123-
test_class = make_base_fmf_class_abstract(node=node, target=target)
124-
else:
125-
logger.debug("searching for %s", node.name)
126-
test_class = make_wrapped_fmf_class(node=node, target=target)
127-
if is_compatible(target_type=target.target_type, check_instance=test_class.backendclass()):
128-
test_classes[test_class.__name__] = test_class
129-
logger.debug("Test added: %s", node.name)
115+
metadata_forest = []
116+
if ruleset_tree_path:
117+
ruleset_mtd = ExtendedTree(ruleset_tree_path)
118+
source_metadata_trees = [ExtendedTree(fmfpath) for fmfpath in fmfpathes]
119+
ruleset_mtd.references(datatrees=source_metadata_trees)
120+
metadata_forest = [ruleset_mtd]
121+
else:
122+
metadata_forest = [ExtendedTree(fmfpath) for fmfpath in fmfpathes]
123+
for metadata_item in metadata_forest:
124+
for node in metadata_item.prune(names=filter_names, filters=filters):
125+
if node.data.get("class") or node.data.get("test"):
126+
logger.debug("node (%s) contains test and class item", node.name)
127+
128+
if node.data.get("class") in CLASS_MAPPING:
129+
logger.debug("Using pure FMF metadata for %s (class %s)", node.name, node.data.get("class"))
130+
test_class = make_base_fmf_class_abstract(node=node, target=target)
131+
else:
132+
logger.debug("searching for %s", node.name)
133+
test_class = make_wrapped_fmf_class(node=node, target=target)
134+
if is_compatible(target_type=target.target_type, check_instance=test_class.backendclass()):
135+
test_classes[test_class.__name__] = test_class
136+
logger.debug("Test added: %s", node.name)
137+
else:
138+
logger.debug("Test (not target): %s", node.name)
130139
else:
131-
logger.debug("Test (not target): %s", node.name)
132-
else:
133-
if "__pycache__" not in node.name:
134-
logger.warning("error in fmf config for node (missing test and class items): %s (data: %s) ", node.name, node.data)
140+
if "__pycache__" not in node.name:
141+
logger.warning("error in fmf config for node (missing test and class items): %s (data: %s) ", node.name, node.data)
135142
return test_classes
136143

137144

@@ -154,7 +161,7 @@ def scheduler_opts(target_name=None, checks=None, ruleset_path=None,
154161
if not target_name:
155162
raise EnvironmentError("TARGET envvar is not set.")
156163
if not checks:
157-
checks = get_checks_path()
164+
checks = get_checks_paths()
158165
if not ruleset_path:
159166
ruleset_path = os.environ.get("RULESETPATH")
160167
if not filters:
@@ -163,28 +170,49 @@ def scheduler_opts(target_name=None, checks=None, ruleset_path=None,
163170
filter_names = os.environ.get("NAMES", "").split(";")
164171
if not log_level:
165172
log_level = get_log_level()
166-
output = nosetests_class_fmf_generator(checks, target_name,
173+
output = unittests_class_fmf_generator(checks, target_name,
167174
ruleset_tree_path=ruleset_path,
168175
filter_names=filter_names,
169176
filters=filters,
170177
log_level=log_level)
171178
return output
172179

180+
@click.command()
181+
@click.argument('target', type=click.STRING)
182+
@click.option('checks_paths', '-c', '--checks-paths',
183+
type=click.Path(exists=True, dir_okay=True, file_okay=False),
184+
multiple=True, envvar=COLIN_CHECKS_PATH, default=get_checks_paths(),
185+
help="Path to directory containing checks (default {}).".format(get_checks_paths()))
186+
@click.option('ruleset_dir', '--ruleset-dir', '-r', type=click.Path(),
187+
help="Path to a directory with rulesets")
188+
@click.option('--name', '-n', multiple=True, type=click.STRING,
189+
help="Select cases by key names")
190+
@click.option('filter_opts','--filter', '-f', multiple=True, type=click.STRING,
191+
help="Filter cases based on FMF filter rules")
192+
@click.option('-v', '--verbose', type=click.INT, default=0,
193+
help="change verbosity of unittest scheduler")
194+
@click.option('--debug', default=False, is_flag=True,
195+
help="Enable debugging mode (debugging logs, full tracebacks).")
196+
def cmdline(target, checks_paths, ruleset_dir, name, filter_opts, verbose, debug):
197+
log_level = logging.INFO
198+
if debug:
199+
log_level = logging.DEBUG
200+
logging.basicConfig(stream=sys.stdout, level=log_level)
201+
test_classes = scheduler_opts(target_name=target, checks=checks_paths, ruleset_path=ruleset_dir,
202+
filter_names=name, filters=filter_opts, log_level=log_level)
203+
loader = unittest.TestLoader()
204+
tests = [loader.loadTestsFromTestCase(test) for test in test_classes.values()]
205+
suite = unittest.TestSuite(tests)
206+
207+
runner = unittest.TextTestRunner(verbosity=verbose)
208+
runner.run(suite)
173209

174-
if __name__ == "__main__":
175-
logging.basicConfig(stream=sys.stdout, level=get_log_level())
176-
classes = scheduler_opts()
177-
for item in classes:
178-
globals()[item] = classes[item]
179210

180-
# try to schedule it via nosetests in case of direct schedule
181-
import nose
182-
logger.info("number of test classes: %s", len(classes))
183-
module_name = sys.modules[__name__].__file__
184-
logging.debug("running nose for package: %s", module_name)
185-
result = nose.run(argv=[sys.argv[0], module_name, '-v'])
186-
logging.info("all tests ok: %s", result)
211+
if __name__ == "__main__":
212+
cmdline()
187213
else:
214+
# when used as an testsuite set all classes globals, you can then invoke it eg:
215+
# TARGET=tests/data/Dockerfile nosetests fmf_scheduler.py
188216
classes = scheduler_opts()
189217
for item in classes:
190218
globals()[item] = classes[item]

0 commit comments

Comments
 (0)