Skip to content

Commit

Permalink
Merge pull request #633 from ldoktor/mux-args2tree3
Browse files Browse the repository at this point in the history
avocado.multiplexer: Support for modifying multiplex tree on cmdline [v3]
  • Loading branch information
lmr committed Jun 4, 2015
2 parents b6488cc + e9f5187 commit 4065952
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 48 deletions.
10 changes: 8 additions & 2 deletions avocado/core/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import sys
import argparse

from avocado.core import tree
from avocado.version import VERSION

PROG = 'avocado'
Expand All @@ -33,6 +34,8 @@ class Parser(object):
"""

def __init__(self):
self.args = None
self.subcommands = None
self.application = argparse.ArgumentParser(
prog=PROG,
add_help=False, # see parent parsing
Expand Down Expand Up @@ -71,17 +74,20 @@ def resume(self):
"""
# Inject --help if no arguments is present
default_args = ['--help'] if not sys.argv[1:] else None
self.args, rest = self.application.parse_known_args(args=default_args)
self.args, _ = self.application.parse_known_args(args=default_args)
if not hasattr(self.args, 'dispatch'):
self.application.set_defaults(dispatch=self.application.print_help)
if tree.MULTIPLEX_CAPABLE:
# Allow overriding multiplex variants by plugins args
self.args.default_multiplex_tree = tree.TreeNode()

def finish(self):
"""
Finish the process of parsing arguments.
Side effect: set the final value for attribute `args`.
"""
self.args = self.application.parse_args()
self.args = self.application.parse_args(namespace=self.args)

def take_action(self):
"""
Expand Down
47 changes: 28 additions & 19 deletions avocado/core/plugins/multiplexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# Copyright: Red Hat Inc. 2013-2014
# Author: Lucas Meneghel Rodrigues <lmr@redhat.com>

import os
import sys

from avocado.core.plugins import plugin
Expand Down Expand Up @@ -57,41 +56,51 @@ def configure(self, parser):
self.parser.add_argument('-d', '--debug', action='store_true',
default=False, help="Debug multiplexed "
"files.")
self.parser.add_argument('--env', default=[], nargs='*')
super(Multiplexer, self).configure(self.parser)

def activate(self, args):
# Extend default multiplex tree of --env values
for value in getattr(args, "env", []):
value = value.split(':', 2)
if len(value) < 2:
raise ValueError("key:value pairs required, found only %s"
% (value))
elif len(value) == 2:
args.default_multiplex_tree.value[value[0]] = value[1]
else:
node = args.default_multiplex_tree.get_node(value[0], True)
node.value[value[1]] = value[2]

def run(self, args):
view = output.View(app_args=args)
multiplex_files = args.multiplex_files
if args.tree:
view.notify(event='message', msg='Config file tree structure:')
try:
t = tree.create_from_yaml(multiplex_files)
except IOError, details:
view.notify(event='error', msg=details.strerror)
sys.exit(exit_codes.AVOCADO_JOB_FAIL)
t = tree.apply_filters(t, args.filter_only, args.filter_out)
view.notify(event='minor',
msg=t.get_ascii(attributes=args.attr))
sys.exit(exit_codes.AVOCADO_ALL_OK)

try:
variants = multiplexer.multiplex_yamls(multiplex_files,
args.filter_only,
args.filter_out,
args.debug)
mux_tree = multiplexer.yaml2tree(args.multiplex_files,
args.filter_only, args.filter_out,
args.debug)
except IOError, details:
view.notify(event='error',
msg=details.strerror)
sys.exit(exit_codes.AVOCADO_JOB_FAIL)
mux_tree.merge(args.default_multiplex_tree)
if args.tree:
view.notify(event='message', msg='Config file tree structure:')
view.notify(event='minor',
msg=mux_tree.get_ascii(attributes=args.attr))
sys.exit(exit_codes.AVOCADO_ALL_OK)

variants = multiplexer.MuxTree(mux_tree)
view.notify(event='message', msg='Variants generated:')
for (index, tpl) in enumerate(variants):
if not args.debug:
paths = ', '.join([x.path for x in tpl])
else:
color = output.term_support.LOWLIGHT
cend = output.term_support.ENDC
paths = ', '.join(["%s%s@%s%s" % (_.name, color, _.yaml, cend)
paths = ', '.join(["%s%s@%s%s" % (_.name, color,
getattr(_, 'yaml',
"Unknown"),
cend)
for _ in tpl])
view.notify(event='minor', msg='%sVariant %s: %s' %
(('\n' if args.contents else ''), index + 1, paths))
Expand Down
15 changes: 14 additions & 1 deletion avocado/core/plugins/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,24 @@ def configure(self, parser):
help='Filter out path(s) from multiplexing')
mux.add_argument('--mux-entry', nargs='*', default=None,
help="Multiplex entry point(s)")

mux.add_argument('--env', default=[], nargs='*')
super(TestRunner, self).configure(self.parser)
# Export the test runner parser back to the main parser
parser.runner = self.parser

def activate(self, args):
# Extend default multiplex tree of --env values
for value in getattr(args, "env", []):
value = value.split(':', 2)
if len(value) < 2:
raise ValueError("key:value pairs required, found only %s"
% (value))
elif len(value) == 2:
args.default_multiplex_tree.value[value[0]] = value[1]
else:
node = args.default_multiplex_tree.get_node(value[0], True)
node.value[value[1]] = value[2]

def _validate_job_timeout(self, raw_timeout):
units = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
mult = 1
Expand Down
32 changes: 29 additions & 3 deletions avocado/core/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __init__(self, name='', value=None, parent=None, children=None):
self._environment = None
self.environment_origin = {}
self.ctrl = []
self.multiplex = False
self.multiplex = None
for child in children:
self.add_child(child)

Expand Down Expand Up @@ -159,7 +159,10 @@ def merge(self, other):
remove.append(key)
for key in remove:
self.value.pop(key, None)
self.multiplex = other.multiplex
if other.multiplex is True:
self.multiplex = True
elif other.multiplex is False:
self.multiplex = False
self.value.update(other.value)
for child in other.children:
self.add_child(child)
Expand Down Expand Up @@ -245,6 +248,29 @@ def set_environment_dirty(self):
child.set_environment_dirty()
self._environment = None

def get_node(self, path, create=False):
"""
:param path: Path of the desired node (relative to this node)
:param create: Create the node (and intermediary ones) when not present
:return: the node associated with this path
:raise ValueError: When path doesn't exist and create not set
"""
node = self
for name in path.split('/'):
if not name:
continue
try:
node = node.children[node.children.index(name)]
except ValueError:
if create:
child = node.__class__(name)
node.add_child(child)
node = child
else:
raise ValueError("Path %s does not exists in this tree\n%s"
% (path, self.get_ascii()))
return node

def iter_children_preorder(self):
""" Iterate through children """
queue = collections.deque()
Expand Down Expand Up @@ -670,7 +696,7 @@ def merge(self, other):
Override origin with the one from other tree. Updated/Newly set values
are going to use this location as origin.
"""
if hasattr(other, 'yaml'):
if hasattr(other, 'yaml') and other.yaml:
srcyaml = os.path.relpath(other.yaml)
# when we use TreeNodeDebug, value is always ValueDict
self.value.yaml_per_key.update(other.value.yaml_per_key) # pylint: disable=E1101
Expand Down
14 changes: 8 additions & 6 deletions avocado/multiplexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,16 @@ def __iter__(self):
yield ret


def multiplex_yamls(input_yamls, filter_only=None, filter_out=None,
debug=False):
def yaml2tree(input_yamls, filter_only=None, filter_out=None,
debug=False):
if filter_only is None:
filter_only = []
if filter_out is None:
filter_out = []
input_tree = tree.create_from_yaml(input_yamls, debug)
# TODO: Process filters and multiplex simultaneously
final_tree = tree.apply_filters(input_tree, filter_only, filter_out)
result = MuxTree(final_tree)
return result
return final_tree


# TODO: Create multiplexer plugin and split these functions into multiple files
Expand Down Expand Up @@ -396,9 +395,12 @@ def __init__(self, args):
filter_only = getattr(args, 'filter_only', None)
filter_out = getattr(args, 'filter_out', None)
if mux_files:
self.variants = multiplex_yamls(mux_files, filter_only, filter_out)
mux_tree = yaml2tree(mux_files, filter_only, filter_out)
else: # no variants
self.variants = None
mux_tree = tree.TreeNode()
if getattr(args, 'default_multiplex_tree', None):
mux_tree.merge(args.default_multiplex_tree)
self.variants = MuxTree(mux_tree)
self._mux_entry = getattr(args, 'mux_entry', None)
if self._mux_entry is None:
self._mux_entry = ['/run/*']
Expand Down
31 changes: 19 additions & 12 deletions selftests/all/unit/avocado/multiplexer_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,30 @@ def test_full(self):
self.assertEqual(len(self.mux_full), 12)

def test_create_variants(self):
from_file = multiplexer.multiplex_yamls(['/:' + PATH_PREFIX + 'examples/mux-selftest.yaml'])
from_file = multiplexer.yaml2tree(
["/:" + PATH_PREFIX + 'examples/mux-selftest.yaml'])
from_file = multiplexer.MuxTree(from_file)
self.assertEqual(self.mux_full, tuple(from_file))

# Filters are tested in tree_unittests, only verify `multiplex_yamls` calls
def test_filter_only(self):
exp = (['intel', 'scsi'], ['intel', 'virtio'])
act = tuple(multiplexer.multiplex_yamls(['/:' + PATH_PREFIX + 'examples/mux-selftest.yaml'],
('/hw/cpu/intel',
'/distro/fedora',
'/hw')))
act = multiplexer.yaml2tree(["/:" + PATH_PREFIX +
'examples/mux-selftest.yaml'],
('/hw/cpu/intel',
'/distro/fedora',
'/hw'))
act = tuple(multiplexer.MuxTree(act))
self.assertEqual(act, exp)

def test_filter_out(self):
act = tuple(multiplexer.multiplex_yamls(['/:' + PATH_PREFIX + 'examples/mux-selftest.yaml'],
None,
('/hw/cpu/intel',
'/distro/fedora',
'/distro')))
act = multiplexer.yaml2tree(["/:" + PATH_PREFIX +
'examples/mux-selftest.yaml'],
None,
('/hw/cpu/intel',
'/distro/fedora',
'/distro'))
act = tuple(multiplexer.MuxTree(act))
self.assertEqual(len(act), 4)
self.assertEqual(len(act[0]), 3)
str_act = str(act)
Expand All @@ -71,8 +77,9 @@ def test_filter_out(self):


class TestAvocadoParams(unittest.TestCase):
yamls = iter(multiplexer.multiplex_yamls(['/:' + PATH_PREFIX + 'examples/mux-selftest-params.'
'yaml']))
yamls = multiplexer.yaml2tree(["/:" + PATH_PREFIX +
'examples/mux-selftest-params.yaml'])
yamls = iter(multiplexer.MuxTree(yamls))
params1 = multiplexer.AvocadoParams(yamls.next(), 'Unittest1', 1,
['/ch0/*', '/ch1/*'], {})
yamls.next() # Skip 2nd
Expand Down
10 changes: 5 additions & 5 deletions selftests/all/unit/avocado/tree_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,20 @@ def test_advanced_yaml(self):
self.assertEqual({'new_value': 'something'},
oldroot.children[3].children[0].children[0].value)
# multiplex root (always True)
self.assertEqual(tree2.multiplex, False)
self.assertEqual(tree2.multiplex, None)
# multiplex /virt/
self.assertEqual(tree2.children[0].multiplex, False)
self.assertEqual(tree2.children[0].multiplex, None)
# multiplex /virt/hw
self.assertEqual(tree2.children[0].children[0].multiplex, False)
self.assertEqual(tree2.children[0].children[0].multiplex, None)
# multiplex /virt/distro
self.assertEqual(tree2.children[0].children[1].multiplex, True)
# multiplex /virt/env
self.assertEqual(tree2.children[0].children[2].multiplex, True)
# multiplex /virt/absolutly
self.assertEqual(tree2.children[0].children[3].multiplex, False)
self.assertEqual(tree2.children[0].children[3].multiplex, None)
# multiplex /virt/distro/fedora
self.assertEqual(tree2.children[0].children[1].children[0].multiplex,
False)
None)


class TestPathParent(unittest.TestCase):
Expand Down

0 comments on commit 4065952

Please sign in to comment.