1#!/usr/bin/python
2#
3# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit tests for server/cros/dynamic_suite/host_spec.py."""
8
9import mox
10import unittest
11
12import common
13
14from autotest_lib.server.cros.dynamic_suite import host_spec
15from autotest_lib.server.cros.dynamic_suite.fakes import FakeHost
16
17
18class HostSpecTest(mox.MoxTestBase):
19    """Unit tests for dynamic_suite.host_spec module.
20
21    @var _BOARD: fake board to reimage
22    """
23
24
25    _BOARD = 'board'
26    _SPECS = [host_spec.HostSpec([_BOARD]),
27              host_spec.HostSpec([_BOARD, 'pool:bvt']),
28              host_spec.HostSpec([_BOARD], ['label1'])]
29
30
31    def testOrderSpecsByComplexity(self):
32        """Should return new host spec list with simpler entries later."""
33        reordered = host_spec.order_by_complexity(self._SPECS)
34
35        for spec in self._SPECS[1:]:
36            self.assertTrue(spec in reordered[:-1])
37        self.assertEquals(self._SPECS[0], reordered[-1])
38
39
40    def testSpecSubsets(self):
41        """Validate HostSpec subset checks."""
42        self.assertTrue(self._SPECS[0].is_subset(self._SPECS[1]))
43        self.assertTrue(self._SPECS[0].is_subset(self._SPECS[2]))
44        self.assertFalse(self._SPECS[1].is_subset(self._SPECS[2]))
45        self.assertFalse(self._SPECS[2].is_subset(self._SPECS[1]))
46        self.assertFalse(self._SPECS[1].is_subset(self._SPECS[0]))
47        self.assertFalse(self._SPECS[2].is_subset(self._SPECS[0]))
48
49
50    def testTrivialSpec(self):
51        """Validate that HostSpecs are correctly marked as trivial."""
52        self.assertTrue(self._SPECS[0].is_trivial)
53        self.assertTrue(self._SPECS[1].is_trivial)
54        self.assertFalse(self._SPECS[2].is_trivial)
55
56
57class HostGroupTest(mox.MoxTestBase):
58    """Unit tests for dynamic_suite.host_spec.HostGroup derived classes.
59    """
60
61
62    def testCanConstructExplicit(self):
63        """Should be able to make an ExplicitHostGroup."""
64        host_list = [FakeHost('h1'), FakeHost('h2'), FakeHost('h3')]
65        hosts_per_spec = {host_spec.HostSpec(['l1']): host_list[:1],
66                          host_spec.HostSpec(['l2']): host_list[1:]}
67        group = host_spec.ExplicitHostGroup(hosts_per_spec)
68        for host in host_list:
69            self.assertTrue(host.hostname in group.as_args()['hosts'])
70
71
72    def testExplicitEnforcesHostUniqueness(self):
73        """Should fail to make ExplicitHostGroup with duplicate hosts."""
74        host_list = [FakeHost('h1'), FakeHost('h2'), FakeHost('h3')]
75        hosts_per_spec = {host_spec.HostSpec(['l1']): host_list[:1],
76                          host_spec.HostSpec(['l2']): host_list}
77        self.assertRaises(ValueError,
78                          host_spec.ExplicitHostGroup, hosts_per_spec)
79
80
81    def testCanConstructByMetahostsWithDependencies(self):
82        """Should be able to make a HostGroup from labels."""
83        labels = ['meta_host', 'dep1', 'dep2']
84        num = 3
85        group = host_spec.MetaHostGroup(labels, num)
86        args = group.as_args()
87        self.assertEquals(labels[:1] * num, args['meta_hosts'])
88        self.assertEquals(labels[1:], args['dependencies'])
89
90
91    def testExplicitCanTrackSuccess(self):
92        """Track success/failure in an ExplicitHostGroup."""
93        host_list = [FakeHost('h1'), FakeHost('h2'), FakeHost('h3')]
94        specs = [host_spec.HostSpec(['l1']), host_spec.HostSpec(['l2'])]
95        hosts_per_spec = {specs[0]: host_list[:1], specs[1]: host_list[1:]}
96        group = host_spec.ExplicitHostGroup(hosts_per_spec)
97
98        # Reimage just the one host that satisfies specs[0].
99        group.mark_host_success(host_list[0].hostname)
100        self.assertTrue(group.enough_hosts_succeeded())
101        self.assertTrue(specs[1] in group.doomed_specs)
102
103        # Reimage some host that satisfies specs[1].
104        group.mark_host_success(host_list[2].hostname)
105        self.assertTrue(group.enough_hosts_succeeded())
106        self.assertFalse(group.doomed_specs)
107
108
109    def testExplicitCanTrackSuccessWithSupersets(self):
110        """Track success/failure in an ExplicitHostGroup with supersets."""
111        host_list = [FakeHost('h1'), FakeHost('h2'), FakeHost('h3')]
112        specs = [host_spec.HostSpec(['l1']),
113                 host_spec.HostSpec(['l2']),
114                 host_spec.HostSpec(['l2', 'l1'])]
115        hosts_per_spec = {specs[0]: host_list[:1],
116                          specs[1]: host_list[1:2],
117                          specs[2]: host_list[2:]}
118        group = host_spec.ExplicitHostGroup(hosts_per_spec)
119
120        # Reimage just the one host that satisfies specs[2].
121        # Because satisfying specs[2] statisfies all the specs, we should have
122        # no doomed specs.
123        group.mark_host_success(host_list[2].hostname)
124        self.assertTrue(group.enough_hosts_succeeded())
125        self.assertFalse(group.doomed_specs)
126
127
128    def testExplicitCanTrackUnsatisfiedSpecs(self):
129        """Track unsatisfiable HostSpecs in ExplicitHostGroup."""
130        group = host_spec.ExplicitHostGroup()
131        satisfiable_spec = host_spec.HostSpec(['l2'])
132        unsatisfiable_spec = host_spec.HostSpec(['l1'], ['e1'])
133        group.add_host_for_spec(unsatisfiable_spec, None)
134        group.add_host_for_spec(satisfiable_spec, FakeHost('h1'))
135        self.assertTrue(unsatisfiable_spec in group.unsatisfied_specs)
136        self.assertTrue(satisfiable_spec not in group.unsatisfied_specs)
137
138
139    def testExplicitOneHostEnoughToSatisfySpecs(self):
140        """One host is enough to satisfy a HostSpec in ExplicitHostGroup."""
141        satisfiable_spec = host_spec.HostSpec(['l1'])
142        group = host_spec.ExplicitHostGroup()
143        group.add_host_for_spec(satisfiable_spec, FakeHost('h1'))
144        group.add_host_for_spec(satisfiable_spec, None)
145        self.assertTrue(satisfiable_spec not in group.unsatisfied_specs)
146
147        group = host_spec.ExplicitHostGroup()
148        group.add_host_for_spec(satisfiable_spec, None)
149        group.add_host_for_spec(satisfiable_spec, FakeHost('h1'))
150        self.assertTrue(satisfiable_spec not in group.unsatisfied_specs)
151
152
153    def testExplicitSubsetSpecSatisfiedIfAnyAre(self):
154        """Ensures that any satisfied spec also satisfies a subset HostSpec."""
155        specs = [host_spec.HostSpec(['l1'], ['l3']),
156                 host_spec.HostSpec(['l1'], ['l3', 'l4']),
157                 host_spec.HostSpec(['l1'], ['l5', 'l4']),
158                 host_spec.HostSpec(['l1'], ['l2', 'l3', 'l4'])]
159        group = host_spec.ExplicitHostGroup()
160        group.add_host_for_spec(specs[0], None)
161        group.add_host_for_spec(specs[1], FakeHost('h1'))
162        group.add_host_for_spec(specs[2], FakeHost('h2'))
163        group.add_host_for_spec(specs[3], None)
164
165        self.assertTrue(specs[0] not in group.unsatisfied_specs)
166        self.assertTrue(specs[1] not in group.unsatisfied_specs)
167        self.assertTrue(specs[2] not in group.unsatisfied_specs)
168        self.assertTrue(specs[3] in group.unsatisfied_specs)
169
170
171    def testMetaCanTrackSuccess(self):
172        """Track success/failure in a MetaHostGroup."""
173        labels = ['meta_host', 'dep1', 'dep2']
174        num = 3
175        group = host_spec.MetaHostGroup(labels, num)
176
177        self.assertFalse(group.enough_hosts_succeeded())
178
179        group.mark_host_success('h1')
180        self.assertTrue(group.enough_hosts_succeeded())
181
182
183if __name__ == '__main__':
184    unittest.main()
185