提交 0216c1cb 编写于 作者: R Rudá Moura 提交者: Lucas Meneghel Rodrigues

avocado.core: Introduce tree library

Introduce a new data structure that will be the base of
our new multiplexing mechanism.
Signed-off-by: NRuda Moura <rmoura@redhat.com>
上级 631319b4
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2014
#
# Authors: Ruda Moura <rmoura@redhat.com>
# Lucas Meneghel Rodrigues <lmr@redhat.com>
"""
Tree data strucure with nodes.
"""
import collections
import yaml
class TreeNode(object):
def __init__(self, name='/root', value=None, parent=None, children=None):
if value is None:
value = {}
if children is None:
children = []
self.name = name
self.value = value
self.parent = parent
self.children = children
def __repr__(self):
return 'TreeNode(name=%r, value=%r, parent=%r, children=%r)' % (self.name, self.value, self.parent, self.children)
def __str__(self):
return '%s=%r' % (self.name, self.environment)
def __len__(self):
return len(self.get_leaves())
def __iter__(self):
return self.iter_leaves()
def add_child(self, node):
if isinstance(node, self.__class__):
node.parent = self
self.children.append(node)
else:
raise ValueError('Bad node type.')
return node
@property
def is_leaf(self):
return len(self.children) == 0
@property
def root(self):
return self.get_root()
def get_root(self):
root = self
while root.parent is not None:
root = root.parent
return root
def iter_parents(self):
node = self
while node.parent is not None:
yield node.parent
node = node.parent
@property
def parents(self):
return self.get_parents()
def get_parents(self):
return list(self.iter_parents())
@property
def path(self):
return self.get_path()
def get_path(self, sep='/'):
path = [self.name]
for node in self.iter_parents():
path.append(node.name)
return sep.join(reversed(path))
@property
def environment(self):
return self.get_environment()
def get_environment(self):
def update_or_extend(target, source):
for k, v in source.items():
if target.has_key(k) and isinstance(target[k], list):
target[k].extend(source[k])
else:
if isinstance(source[k], list):
target[k] = source[k][:]
else:
target[k] = source[k]
env = {}
rev_parents = reversed(self.get_parents())
for parent in rev_parents:
update_or_extend(env, parent.value)
update_or_extend(env, self.value)
return env
def iter_children_preorder(self, node=None):
q = collections.deque()
node = self
while node is not None:
yield node
q.extendleft(reversed(node.children))
try:
node = q.popleft()
except:
node = None
def iter_leaves(self):
for node in self.iter_children_preorder():
if node.is_leaf:
yield node
def get_leaves(self):
return list(self.iter_leaves())
def get_ascii(self, show_internal=True, compact=False, attributes=None):
(lines, mid) = self._ascii_art(show_internal=show_internal,
compact=compact, attributes=attributes)
return '\n' + '\n'.join(lines)
def _ascii_art(self, char1='-', show_internal=True, compact=False, attributes=None):
if attributes is None:
attributes = ["name"]
node_name = ', '.join(map(str, [getattr(self, v) for v in attributes if hasattr(self, v)]))
LEN = max(3, len(node_name) if not self.children or show_internal else 3)
PAD = ' ' * LEN
PA = ' ' * (LEN - 1)
if not self.is_leaf:
mids = []
result = []
for c in self.children:
if len(self.children) == 1:
char2 = '/'
elif c is self.children[0]:
char2 = '/'
elif c is self.children[-1]:
char2 = '\\'
else:
char2 = '-'
(clines, mid) = c._ascii_art(char2, show_internal, compact, attributes)
mids.append(mid + len(result))
result.extend(clines)
if not compact:
result.append('')
if not compact:
result.pop()
(lo, hi, end) = (mids[0], mids[-1], len(result))
prefixes = [PAD] * (lo + 1) + [PA + '|'] * (hi - lo - 1) + [PAD] * (end - hi)
mid = (lo + hi) / 2
prefixes[mid] = char1 + '-' * (LEN - 2) + prefixes[mid][-1]
result = [p + l for (p, l) in zip(prefixes, result)]
if show_internal:
stem = result[mid]
result[mid] = stem[0] + node_name + stem[len(node_name) + 1:]
return result, mid
else:
return [char1 + '-' + node_name], 0
def ordered_load(stream, Loader=yaml.Loader,
object_pairs_hook=collections.OrderedDict):
class OrderedLoader(Loader):
pass
OrderedLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
lambda loader, node:
object_pairs_hook(loader.construct_pairs(node)))
return yaml.load(stream, OrderedLoader)
def read_ordered_yaml(fileobj):
try:
data = ordered_load(fileobj.read())
except (yaml.scanner.ScannerError, yaml.parser.ParserError) as err:
raise SyntaxError(err)
return data
def create_from_ordered_data(data, tree=None, root=None, name='/root'):
if tree is None:
tree = TreeNode(name)
if root is None:
root = tree
if isinstance(data, dict):
for key, value in data.items():
if isinstance(value, dict):
leaf = True
for k, v in value.items():
if isinstance(v, dict):
leaf = False
node = TreeNode(key)
tree.add_child(node)
create_from_ordered_data(value, node, root)
elif value is None:
# Leaf without variable
node = TreeNode(key)
tree.add_child(node)
else:
# Node/leaf with variable
tree.value.update({key: value})
return root
def create_from_yaml(input_yaml):
data = read_ordered_yaml(input_yaml)
return create_from_ordered_data(data)
#!/usr/bin/env python
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2014
#
# Author: Ruda Moura <rmoura@redhat.com>
import unittest
import StringIO
from avocado.core.tree import *
source_yaml = """
hw:
cpu:
arch:
- noarch
intel:
arch:
- i386
- x86-64
arm:
arch:
- arm
- arm64
os:
linux:
dev-tools: gcc
pm: tar
fedora:
pm: rpm
mint:
pm: deb
centos:
pm: rpm
win:
dev-tools: 'cygwin'
win7:
metro: false
win8:
metro: true
"""
class TestReadYAML(unittest.TestCase):
def setUp(self):
self.yaml = StringIO.StringIO(source_yaml)
def test_read_yaml_values(self):
data = read_ordered_yaml(self.yaml)
self.assertIsInstance(data, dict)
self.assertIn('hw', data)
self.assertIn('os', data)
self.assertIn('cpu', data['hw'])
self.assertIn('intel', data['hw']['cpu'])
self.assertIsInstance(data['hw']['cpu']['intel']['arch'], list)
self.assertIn('arm', data['hw']['cpu'])
self.assertIsInstance(data['hw']['cpu']['arm']['arch'], list)
self.assertIn('os', data)
self.assertIn('linux', data['os'])
self.assertIn('dev-tools', data['os']['linux'])
self.assertIn('fedora', data['os']['linux'])
self.assertIn('mint', data['os']['linux'])
self.assertIn('centos', data['os']['linux'])
self.assertIn('win', data['os'])
self.assertIn('dev-tools', data['os']['win'])
self.assertIn('win7', data['os']['win'])
self.assertIn('win8', data['os']['win'])
class TestTreeNode(unittest.TestCase):
def setUp(self):
self.data = read_ordered_yaml(StringIO.StringIO(source_yaml))
self.treenode = create_from_ordered_data(self.data)
def test_create_treenode(self):
self.assertIsInstance(self.treenode, TreeNode)
def test_node_order(self):
self.assertEqual(self.treenode.children[0].name, 'hw')
self.assertEqual(self.treenode.children[0].children[0].name, 'cpu')
self.assertEqual(self.treenode.children[0].children[0].children[0].name, 'intel')
self.assertEqual(self.treenode.children[0].children[0].children[1].name, 'arm')
self.assertEqual(self.treenode.children[1].name, 'os')
self.assertEqual(self.treenode.children[1].children[0].name, 'linux')
self.assertEqual(self.treenode.children[1].children[0].children[0].name, 'fedora')
self.assertEqual(self.treenode.children[1].children[0].children[1].name, 'mint')
self.assertEqual(self.treenode.children[1].children[0].children[2].name, 'centos')
self.assertEqual(self.treenode.children[1].children[1].name, 'win')
self.assertEqual(self.treenode.children[1].children[1].children[0].name, 'win7')
self.assertEqual(self.treenode.children[1].children[1].children[1].name, 'win8')
def test_values(self):
self.assertDictEqual(self.treenode.children[0].children[0].children[0].value, {'arch': ['i386', 'x86-64']})
self.assertDictEqual(self.treenode.children[0].children[0].children[1].value, {'arch': ['arm', 'arm64']})
self.assertDictEqual(self.treenode.children[1].children[0].value, {'dev-tools': 'gcc', 'pm': 'tar'})
self.assertDictEqual(self.treenode.children[1].children[1].value, {'dev-tools': 'cygwin'})
def test_parent(self):
self.assertIsNone(self.treenode.parent)
self.assertIsNone(self.treenode.children[0].parent.parent)
self.assertEqual(self.treenode.children[0].parent, self.treenode)
self.assertEqual(self.treenode.children[0].children[0].parent, self.treenode.children[0])
self.assertEqual(self.treenode.children[0].children[0].parent.parent, self.treenode)
def test_environment(self):
self.assertDictEqual(self.treenode.children[0].children[0].environment, {'arch': ['noarch']})
self.assertDictEqual(self.treenode.children[0].children[0].children[0].environment, {'arch': ['noarch', 'i386', 'x86-64']})
self.assertDictEqual(self.treenode.children[0].children[0].children[1].environment, {'arch': ['noarch', 'arm', 'arm64']})
self.assertDictEqual(self.treenode.children[1].children[0].environment, {'dev-tools': 'gcc', 'pm': 'tar'})
self.assertDictEqual(self.treenode.children[1].children[0].children[0].environment, {'dev-tools': 'gcc', 'pm': 'rpm'})
self.assertDictEqual(self.treenode.children[1].children[0].children[1].environment, {'dev-tools': 'gcc', 'pm': 'deb'})
self.assertDictEqual(self.treenode.children[1].children[0].children[2].environment, {'dev-tools': 'gcc', 'pm': 'rpm'})
self.assertDictEqual(self.treenode.children[1].children[1].environment, {'dev-tools': 'cygwin'})
self.assertDictEqual(self.treenode.children[1].children[1].children[0].environment, {'dev-tools': 'cygwin', 'metro': False})
self.assertDictEqual(self.treenode.children[1].children[1].children[1].environment, {'dev-tools': 'cygwin', 'metro': True})
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册