1 # Copyright 2017 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import mock 6 import unittest 7 8 import common 9 from autotest_lib.frontend.afe.json_rpc import proxy as rpc_proxy 10 from autotest_lib.server import frontend 11 from autotest_lib.server.hosts import afe_store 12 from autotest_lib.server.hosts import host_info 13 14 class AfeStoreTest(unittest.TestCase): 15 """Test refresh/commit success cases for AfeStore.""" 16 17 def setUp(self): 18 self.hostname = 'some-host' 19 self.mock_afe = mock.create_autospec(frontend.AFE, instance=True) 20 self.store = afe_store.AfeStore(self.hostname, afe=self.mock_afe) 21 22 23 def _create_mock_host(self, labels, attributes): 24 """Create a mock frontend.Host with the given labels and attributes. 25 26 @param labels: The labels to set on the host. 27 @param attributes: The attributes to set on the host. 28 @returns: A mock object for frontend.Host. 29 """ 30 mock_host = mock.create_autospec(frontend.Host, instance=True) 31 mock_host.labels = labels 32 mock_host.attributes = attributes 33 return mock_host 34 35 36 def test_refresh(self): 37 """Test that refresh correctly translates host information.""" 38 self.mock_afe.get_hosts.return_value = [ 39 self._create_mock_host(['label1'], {'attrib1': 'val1'})] 40 info = self.store._refresh_impl() 41 self.assertListEqual(info.labels, ['label1']) 42 self.assertDictEqual(info.attributes, {'attrib1': 'val1'}) 43 44 45 def test_refresh_no_host_raises(self): 46 """Test that refresh complains if no host is found.""" 47 self.mock_afe.get_hosts.return_value = [] 48 with self.assertRaises(host_info.StoreError): 49 self.store._refresh_impl() 50 51 52 def test_refresh_multiple_hosts_picks_first(self): 53 """Test that refresh returns the first host if multiple match.""" 54 self.mock_afe.get_hosts.return_value = [ 55 self._create_mock_host(['label1'], {'attrib1': 'val1'}), 56 self._create_mock_host(['label2'], {'attrib2': 'val2'})] 57 info = self.store._refresh_impl() 58 self.assertListEqual(info.labels, ['label1']) 59 self.assertDictEqual(info.attributes, {'attrib1': 'val1'}) 60 61 62 def test_commit_labels(self): 63 """Tests that labels are updated correctly on commit.""" 64 self.mock_afe.get_hosts.return_value = [ 65 self._create_mock_host(['label1'], {})] 66 info = host_info.HostInfo(['label2'], {}) 67 self.store._commit_impl(info) 68 self.assertEqual(self.mock_afe.run.call_count, 2) 69 expected_run_calls = [ 70 mock.call('host_remove_labels', id='some-host', 71 labels=['label1']), 72 mock.call('host_add_labels', id='some-host', 73 labels=['label2']), 74 ] 75 self.mock_afe.run.assert_has_calls(expected_run_calls, 76 any_order=True) 77 78 79 def test_commit_labels_raises(self): 80 """Test that exception while committing is translated properly.""" 81 self.mock_afe.get_hosts.return_value = [ 82 self._create_mock_host(['label1'], {})] 83 self.mock_afe.run.side_effect = rpc_proxy.JSONRPCException('some error') 84 info = host_info.HostInfo(['label2'], {}) 85 with self.assertRaises(host_info.StoreError): 86 self.store._commit_impl(info) 87 88 89 def test_commit_adds_attributes(self): 90 """Tests that new attributes are added correctly on commit.""" 91 self.mock_afe.get_hosts.return_value = [ 92 self._create_mock_host([], {})] 93 info = host_info.HostInfo([], {'attrib1': 'val1'}) 94 self.store._commit_impl(info) 95 self.assertEqual(self.mock_afe.set_host_attribute.call_count, 1) 96 self.mock_afe.set_host_attribute.assert_called_once_with( 97 'attrib1', 'val1', hostname=self.hostname) 98 99 100 def test_commit_updates_attributes(self): 101 """Tests that existing attributes are updated correctly on commit.""" 102 self.mock_afe.get_hosts.return_value = [ 103 self._create_mock_host([], {'attrib1': 'val1'})] 104 info = host_info.HostInfo([], {'attrib1': 'val1_updated'}) 105 self.store._commit_impl(info) 106 self.assertEqual(self.mock_afe.set_host_attribute.call_count, 1) 107 self.mock_afe.set_host_attribute.assert_called_once_with( 108 'attrib1', 'val1_updated', hostname=self.hostname) 109 110 111 def test_commit_deletes_attributes(self): 112 """Tests that deleted attributes are updated correctly on commit.""" 113 self.mock_afe.get_hosts.return_value = [ 114 self._create_mock_host([], {'attrib1': 'val1'})] 115 info = host_info.HostInfo([], {}) 116 self.store._commit_impl(info) 117 self.assertEqual(self.mock_afe.set_host_attribute.call_count, 1) 118 self.mock_afe.set_host_attribute.assert_called_once_with( 119 'attrib1', None, hostname=self.hostname) 120 121 122 def test_str(self): 123 """Sanity tests the __str__ implementaiton""" 124 self.assertEqual(str(self.store), 'AfeStore[some-host]') 125 126 127 class AfeStoreKeepPoolTest(unittest.TestCase): 128 """Test commit success cases for AfeStoreKeepPool.""" 129 130 def setUp(self): 131 self.hostname = 'some-host' 132 self.mock_afe = mock.create_autospec(frontend.AFE, instance=True) 133 self.store = afe_store.AfeStoreKeepPool( 134 self.hostname, afe=self.mock_afe) 135 136 def _create_mock_host(self, labels, attributes): 137 """Create a mock frontend.Host with the given labels and attributes. 138 139 @param labels: The labels to set on the host. 140 @param attributes: The attributes to set on the host. 141 @returns: A mock object for frontend.Host. 142 """ 143 mock_host = mock.create_autospec(frontend.Host, instance=True) 144 mock_host.labels = labels 145 mock_host.attributes = attributes 146 return mock_host 147 148 def test_no_pool_label(self): 149 """Tests that no pool labels are updated on commit.""" 150 self.mock_afe.get_hosts.return_value = [ 151 self._create_mock_host(['label1'], {})] 152 new_info = host_info.HostInfo(['label2'], {}) 153 old_info = self.store._refresh_impl() 154 labels_to_remove, labels_to_add = self.store._adjust_pool( 155 old_info, new_info) 156 self.assertEqual((labels_to_remove, labels_to_add), 157 (['label1'], ['label2'])) 158 159 def test_update_pool_label(self): 160 """Tests that pool labels are updated correctly on commit.""" 161 self.mock_afe.get_hosts.return_value = [ 162 self._create_mock_host(['pool:XXX'], {})] 163 new_info = host_info.HostInfo(['pool:YYY'], {}) 164 old_info = self.store._refresh_impl() 165 labels_to_remove, labels_to_add = self.store._adjust_pool( 166 old_info, new_info) 167 self.assertEqual((labels_to_remove, labels_to_add), 168 (['pool:XXX'], ['pool:YYY'])) 169 170 def test_add_pool_label(self): 171 """Tests that pool labels are added correctly on commit.""" 172 self.mock_afe.get_hosts.return_value = [ 173 self._create_mock_host(['label1'], {})] 174 new_info = host_info.HostInfo(['pool:YYY'], {}) 175 old_info = self.store._refresh_impl() 176 labels_to_remove, labels_to_add = self.store._adjust_pool( 177 old_info, new_info) 178 self.assertEqual((labels_to_remove, labels_to_add), 179 (['label1'], ['pool:YYY'])) 180 181 def test_remove_pool_label(self): 182 """Tests that pool labels are not removed on commit.""" 183 self.mock_afe.get_hosts.return_value = [ 184 self._create_mock_host(['pool:XXX'], {})] 185 new_info = host_info.HostInfo(['label2'], {}) 186 old_info = self.store._refresh_impl() 187 labels_to_remove, labels_to_add = self.store._adjust_pool( 188 old_info, new_info) 189 self.assertEqual((labels_to_remove, labels_to_add), 190 ([], ['label2'])) 191 192 193 class DictDiffTest(unittest.TestCase): 194 """Tests the afe_store._dict_diff private method.""" 195 196 def _assert_dict_diff(self, got_tuple, expectation_tuple): 197 """Verifies the result from _dict_diff 198 199 @param got_tuple: The tuple returned by afe_store._dict_diff 200 @param expectatin_tuple: tuple (left_only, right_only, differing) 201 containing iterable of keys to verify against got_tuple. 202 """ 203 for got, expect in zip(got_tuple, expectation_tuple): 204 self.assertEqual(got, set(expect)) 205 206 207 def test_both_empty(self): 208 """Tests the case when both dicts are empty.""" 209 self._assert_dict_diff(afe_store._dict_diff({}, {}), 210 ((), (), ())) 211 212 213 def test_right_dict_only(self): 214 """Tests the case when left dict is empty.""" 215 self._assert_dict_diff(afe_store._dict_diff({}, {1: 1}), 216 ((), (1,), ())) 217 218 219 def test_left_dict_only(self): 220 """Tests the case when right dict is empty.""" 221 self._assert_dict_diff(afe_store._dict_diff({1: 1}, {}), 222 ((1,), (), ())) 223 224 225 def test_left_dict_extra(self): 226 """Tests the case when left dict has extra keys.""" 227 self._assert_dict_diff(afe_store._dict_diff({1: 1, 2: 2}, {1: 1}), 228 ((2,), (), ())) 229 230 231 def test_right_dict_extra(self): 232 """Tests the case when right dict has extra keys.""" 233 self._assert_dict_diff(afe_store._dict_diff({1: 1}, {1: 1, 2: 2}), 234 ((), (2,), ())) 235 236 237 def test_identical_keys_with_different_values(self): 238 """Tests the case when the set of keys is same, but values differ.""" 239 self._assert_dict_diff(afe_store._dict_diff({1: 1, 2: 3}, {1: 1, 2: 2}), 240 ((), (), (2,))) 241 242 243 if __name__ == '__main__': 244 unittest.main() 245