# Copyright 2018 Alan Rominger # # This file is part of Ansible # # Ansible 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 3 of the License, or # (at your option) any later version. # # Ansible 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 the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . from __future__ import annotations import unittest from ansible.inventory.group import Group from ansible.inventory.host import Host from ansible.errors import AnsibleError class TestGroup(unittest.TestCase): def test_depth_update(self): A = Group('A') B = Group('B') Z = Group('Z') A.add_child_group(B) A.add_child_group(Z) self.assertEqual(A.depth, 0) self.assertEqual(Z.depth, 1) self.assertEqual(B.depth, 1) def test_depth_update_dual_branches(self): alpha = Group('alpha') A = Group('A') alpha.add_child_group(A) B = Group('B') A.add_child_group(B) Z = Group('Z') alpha.add_child_group(Z) beta = Group('beta') B.add_child_group(beta) Z.add_child_group(beta) self.assertEqual(alpha.depth, 0) # apex self.assertEqual(beta.depth, 3) # alpha -> A -> B -> beta omega = Group('omega') omega.add_child_group(alpha) # verify that both paths are traversed to get the max depth value self.assertEqual(B.depth, 3) # omega -> alpha -> A -> B self.assertEqual(beta.depth, 4) # B -> beta def test_depth_recursion(self): A = Group('A') B = Group('B') A.add_child_group(B) # hypothetical of adding B as child group to A A.parent_groups.append(B) B.child_groups.append(A) # can't update depths of groups, because of loop with self.assertRaises(AnsibleError): B._check_children_depth() def test_loop_detection(self): A = Group('A') B = Group('B') C = Group('C') A.add_child_group(B) B.add_child_group(C) with self.assertRaises(AnsibleError): C.add_child_group(A) def test_direct_host_ordering(self): """Hosts are returned in order they are added """ group = Group('A') # host names not added in alphabetical order host_name_list = ['z', 'b', 'c', 'a', 'p', 'q'] expected_hosts = [] for host_name in host_name_list: h = Host(host_name) group.add_host(h) expected_hosts.append(h) assert group.get_hosts() == expected_hosts def test_sub_group_host_ordering(self): """With multiple nested groups, asserts that hosts are returned in deterministic order """ top_group = Group('A') expected_hosts = [] for name in ['z', 'b', 'c', 'a', 'p', 'q']: child = Group('group_{0}'.format(name)) top_group.add_child_group(child) host = Host('host_{0}'.format(name)) child.add_host(host) expected_hosts.append(host) assert top_group.get_hosts() == expected_hosts def test_populates_descendant_hosts(self): A = Group('A') B = Group('B') C = Group('C') h = Host('h') C.add_host(h) A.add_child_group(B) # B is child of A B.add_child_group(C) # C is descendant of A A.add_child_group(B) self.assertEqual(set(h.groups), set([C, B, A])) h2 = Host('h2') C.add_host(h2) self.assertEqual(set(h2.groups), set([C, B, A])) def test_ancestor_example(self): # see docstring for Group._walk_relationship groups = {} for name in ['A', 'B', 'C', 'D', 'E', 'F']: groups[name] = Group(name) # first row groups['A'].add_child_group(groups['D']) groups['B'].add_child_group(groups['D']) groups['B'].add_child_group(groups['E']) groups['C'].add_child_group(groups['D']) # second row groups['D'].add_child_group(groups['E']) groups['D'].add_child_group(groups['F']) groups['E'].add_child_group(groups['F']) self.assertEqual( set(groups['F'].get_ancestors()), set([ groups['A'], groups['B'], groups['C'], groups['D'], groups['E'] ]) ) def test_ancestors_recursive_loop_safe(self): """ The get_ancestors method may be referenced before circular parenting checks, so the method is expected to be stable even with loops """ A = Group('A') B = Group('B') A.parent_groups.append(B) B.parent_groups.append(A) # finishes in finite time self.assertEqual(A.get_ancestors(), set([A, B]))