diff --git a/optional_plugins/varianter_yaml_to_mux/avocado_varianter_yaml_to_mux/__init__.py b/optional_plugins/varianter_yaml_to_mux/avocado_varianter_yaml_to_mux/__init__.py index bf09344d237b5528808375f0d2831778734eca64..4bdddf1c16de4b9fa46ab0334a4355c7d1de880b 100644 --- a/optional_plugins/varianter_yaml_to_mux/avocado_varianter_yaml_to_mux/__init__.py +++ b/optional_plugins/varianter_yaml_to_mux/avocado_varianter_yaml_to_mux/__init__.py @@ -73,58 +73,113 @@ class ListOfNodeObjects(list): # Few methods pylint: disable=R0903 """ +def _normalize_path(path): + """ + End the path with single '/' + + :param path: original path + :type path: str + :returns: path with trailing '/', or None when empty path + :rtype: str or None + """ + if not path: + return + if path[-1] != '/': + path += '/' + return path + + +def _handle_control_tag(path, cls_node, node, value): + """ + Handling of most YAML control tags (all but "!using") + + :param path: path on the YAML + :type path: str + :param cls_node: the class of the node + :type cls_node: :class:`avocado.core.tree.TreeNode` or similar + :param node: the node in which to handle control tags + :type node: instance of :class:`avocado.core.tree.TreeNode` or similar + :param value: the value of the node + """ + if value[0].code == YAML_INCLUDE: + # Include file + ypath = value[1] + if not os.path.isabs(ypath): + ypath = os.path.join(os.path.dirname(path), ypath) + if not os.path.exists(ypath): + raise ValueError("File '%s' included from '%s' does not " + "exist." % (ypath, path)) + node.merge(_create_from_yaml('/:' + ypath, cls_node)) + elif value[0].code in (YAML_REMOVE_NODE, YAML_REMOVE_VALUE): + value[0].value = value[1] # set the name + node.ctrl.append(value[0]) # add "blue pill" of death + elif value[0].code == YAML_MUX: + node.multiplex = True + elif value[0].code == YAML_FILTER_ONLY: + new_value = _normalize_path(value[1]) + if new_value: + node.filters[0].append(new_value) + elif value[0].code == YAML_FILTER_OUT: + new_value = _normalize_path(value[1]) + if new_value: + node.filters[1].append(new_value) + + +def _handle_control_tag_using(path, name, using, value): + """ + Handling of the "!using" YAML control tag + + :param path: path on the YAML + :type path: str + :param name: name to be applied in the "!using" tag + :type name: str + :param using: wether using is already being used + :type using: bool + :param value: the value of the node + """ + if using: + raise ValueError("!using can be used only once per " + "node! (%s:%s)" % (path, name)) + using = value + if using[0] == '/': + using = using[1:] + if using[-1] == '/': + using = using[:-1] + return using + + +def _apply_using(name, cls_node, using, node): + """ + Create the structure defined by "!using" and return the new root + + :param name: the tag name to have the "!using" applied to + :type name: str + :param cls_node: the class of the node + :type cls_node: :class:`avocado.core.tree.TreeNode` or similar + :param using: the new location to put the tag into + :type using: bool + :param node: the node in which to handle control tags + :type node: instance of :class:`avocado.core.tree.TreeNode` or similar + """ + if name is not '': + for name in using.split('/')[::-1]: + node = cls_node(name, children=[node]) + else: + using = using.split('/')[::-1] + node.name = using.pop() + while True: + if not using: + break + name = using.pop() # 'using' is list pylint: disable=E1101 + node = cls_node(name, children=[node]) + node = cls_node('', children=[node]) + return node + + def _create_from_yaml(path, cls_node=mux.MuxTreeNode): """Create tree structure from yaml stream""" def tree_node_from_values(name, values): """Create `name` node and add values""" - def handle_control_tag(node, value): - """Handling of YAML tags (except of !using)""" - def normalize_path(path): - """End the path with single '/', None when empty path""" - if not path: - return - if path[-1] != '/': - path += '/' - return path - - if value[0].code == YAML_INCLUDE: - # Include file - ypath = value[1] - if not os.path.isabs(ypath): - ypath = os.path.join(os.path.dirname(path), ypath) - if not os.path.exists(ypath): - raise ValueError("File '%s' included from '%s' does not " - "exist." % (ypath, path)) - node.merge(_create_from_yaml('/:' + ypath, cls_node)) - elif value[0].code == YAML_REMOVE_NODE: - value[0].value = value[1] # set the name - node.ctrl.append(value[0]) # add "blue pill" of death - elif value[0].code == YAML_REMOVE_VALUE: - value[0].value = value[1] # set the name - node.ctrl.append(value[0]) - elif value[0].code == YAML_MUX: - node.multiplex = True - elif value[0].code == YAML_FILTER_ONLY: - new_value = normalize_path(value[1]) - if new_value: - node.filters[0].append(new_value) - elif value[0].code == YAML_FILTER_OUT: - new_value = normalize_path(value[1]) - if new_value: - node.filters[1].append(new_value) - - def handle_control_tag_using(name, using, value): - """Handling of the !using tag""" - if using: - raise ValueError("!using can be used only once per " - "node! (%s:%s)" % (path, name)) - using = value - if using[0] == '/': - using = using[1:] - if using[-1] == '/': - using = using[:-1] - return using - def node_content_from_node(node, values, using): """Processes node values into the current node content""" for value in values: @@ -132,9 +187,9 @@ def _create_from_yaml(path, cls_node=mux.MuxTreeNode): node.add_child(value) elif isinstance(value[0], mux.Control): if value[0].code == YAML_USING: - using = handle_control_tag_using(name, using, value[1]) + using = _handle_control_tag_using(path, name, using, value[1]) else: - handle_control_tag(node, value) + _handle_control_tag(path, cls_node, node, value) elif isinstance(value[1], collections.OrderedDict): node.add_child(tree_node_from_values(str(value[0]), value[1])) @@ -147,9 +202,9 @@ def _create_from_yaml(path, cls_node=mux.MuxTreeNode): for key, value in iteritems(values): if isinstance(key, mux.Control): if key.code == YAML_USING: - using = handle_control_tag_using(name, using, value) + using = _handle_control_tag_using(path, name, using, value) else: - handle_control_tag(node, [key, value]) + _handle_control_tag(path, cls_node, node, [key, value]) elif (isinstance(value, collections.OrderedDict) or value is None): node.add_child(tree_node_from_values(key, value)) @@ -157,22 +212,6 @@ def _create_from_yaml(path, cls_node=mux.MuxTreeNode): node.value[key] = value return using - def apply_using(name, using, node): - '''Create the structure defined by using and return the new root''' - if name is not '': - for name in using.split('/')[::-1]: - node = cls_node(name, children=[node]) - else: - using = using.split('/')[::-1] - node.name = using.pop() - while True: - if not using: - break - name = using.pop() # 'using' is list pylint: disable=E1101 - node = cls_node(name, children=[node]) - node = cls_node('', children=[node]) - return node - # Initialize the node node = cls_node(str(name)) if not values: @@ -187,7 +226,7 @@ def _create_from_yaml(path, cls_node=mux.MuxTreeNode): # Prefix nodes if tag "!using" was used if using: - node = apply_using(name, using, node) + node = _apply_using(name, cls_node, using, node) return node def mapping_to_tree_loader(loader, node, looks_like_node=False): diff --git a/optional_plugins/varianter_yaml_to_mux/tests/.data/mux-selftest-using.yaml b/optional_plugins/varianter_yaml_to_mux/tests/.data/mux-selftest-using.yaml new file mode 100644 index 0000000000000000000000000000000000000000..329b00a94c1bc79ce3d8d5b34e0b494ca7a38286 --- /dev/null +++ b/optional_plugins/varianter_yaml_to_mux/tests/.data/mux-selftest-using.yaml @@ -0,0 +1,3 @@ +!using : /foo +bar: + !using : baz diff --git a/optional_plugins/varianter_yaml_to_mux/tests/test_multiplex.py b/optional_plugins/varianter_yaml_to_mux/tests/test_multiplex.py index 0ada816c5f1da7f9b582f2523e8e93a9f31cf213..0fbd085e6224c00cbe649df8a36c9bc8c9b3841c 100644 --- a/optional_plugins/varianter_yaml_to_mux/tests/test_multiplex.py +++ b/optional_plugins/varianter_yaml_to_mux/tests/test_multiplex.py @@ -55,6 +55,13 @@ class MultiplexTests(unittest.TestCase): result = self.run_and_check(cmd_line, expected_rc) self.assertIn('No such file or directory', result.stderr_text) + def test_mplex_plugin_using(self): + cmd_line = ('%s variants -m /:optional_plugins/varianter_yaml_to_mux/' + 'tests/.data/mux-selftest-using.yaml' % AVOCADO) + expected_rc = exit_codes.AVOCADO_ALL_OK + result = self.run_and_check(cmd_line, expected_rc) + self.assertIn(b' /foo/baz/bar', result.stdout) + @unittest.skipIf(sys.version_info[0] == 3, "Test currently broken on Python 3") def test_mplex_debug(self): diff --git a/optional_plugins/varianter_yaml_to_mux/tests/test_mux.py b/optional_plugins/varianter_yaml_to_mux/tests/test_mux.py index 7f93b5f65eeb8b2f52e3cc804c70c294e0efa3ba..3102ba145cd1531df5753acfe848e33275d8d9f5 100644 --- a/optional_plugins/varianter_yaml_to_mux/tests/test_mux.py +++ b/optional_plugins/varianter_yaml_to_mux/tests/test_mux.py @@ -535,6 +535,49 @@ class TestPathParent(unittest.TestCase): self.assertNotEqual(mux.path_parent('/os/linux'), '/') +class TestCreateFromYaml(unittest.TestCase): + + def test_normalize_path(self): + self.assertEqual(yaml_to_mux._normalize_path(''), None) + self.assertEqual(yaml_to_mux._normalize_path('path'), 'path/') + + def test_handle_control_path_include_file_does_not_exist(self): + with self.assertRaises(ValueError): + yaml_to_mux._handle_control_tag( + 'original_fake_file.yaml', + mux.MuxTreeNode, mux.MuxTreeNode(), + (mux.Control(yaml_to_mux.YAML_INCLUDE), + 'unexisting_include.yaml')) + + def test_handle_control_path_remove(self): + klass = mux.MuxTreeNode + node = klass() + control = mux.Control(yaml_to_mux.YAML_REMOVE_NODE) + to_be_removed = 'node_to_be_removed' + yaml_to_mux._handle_control_tag('fake_path', + klass, node, + (control, to_be_removed)) + self.assertEqual(control.value, to_be_removed) + self.assertIn(control, node.ctrl) + + def test_handle_control_tag_using_multiple(self): + with self.assertRaises(ValueError): + yaml_to_mux._handle_control_tag_using('original_fake_file.yaml', + 'name', True, 'using') + + def test_handle_control_tag_using(self): + using = yaml_to_mux._handle_control_tag_using('fake_path', + 'name', + False, + '/using/path/') + self.assertEqual(using, 'using/path') + + def test_apply_using(self): + node = yaml_to_mux._apply_using('bar', mux.MuxTreeNode, + 'foo', mux.MuxTreeNode()) + self.assertEqual(node.path, '/foo') + + class TestFingerprint(unittest.TestCase): def test_fingerprint(self):