1 # Copyright 2016 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 unittest 6 7 import common 8 from autotest_lib.server.hosts import host_info 9 10 11 class HostInfoTest(unittest.TestCase): 12 """Tests the non-trivial attributes of HostInfo.""" 13 14 def setUp(self): 15 self.info = host_info.HostInfo() 16 17 18 def test_build_needs_prefix(self): 19 """The build prefix is of the form '<type>-version:'""" 20 self.info.labels = ['cros-version', 'ab-version', 'testbed-version', 21 'fwrw-version', 'fwro-version'] 22 self.assertIsNone(self.info.build) 23 24 25 def test_build_prefix_must_be_anchored(self): 26 """Ensure that build ignores prefixes occuring mid-string.""" 27 self.info.labels = ['not-at-start-cros-version:cros1', 28 'not-at-start-ab-version:ab1', 29 'not-at-start-testbed-version:testbed1'] 30 self.assertIsNone(self.info.build) 31 32 33 def test_build_ignores_firmware(self): 34 """build attribute should ignore firmware versions.""" 35 self.info.labels = ['fwrw-version:fwrw1', 'fwro-version:fwro1'] 36 self.assertIsNone(self.info.build) 37 38 39 def test_build_returns_first_match(self): 40 """When multiple labels match, first one should be used as build.""" 41 self.info.labels = ['cros-version:cros1', 'cros-version:cros2'] 42 self.assertEqual(self.info.build, 'cros1') 43 self.info.labels = ['ab-version:ab1', 'ab-version:ab2'] 44 self.assertEqual(self.info.build, 'ab1') 45 self.info.labels = ['testbed-version:tb1', 'testbed-version:tb2'] 46 self.assertEqual(self.info.build, 'tb1') 47 48 49 def test_build_prefer_cros_over_others(self): 50 """When multiple versions are available, prefer cros.""" 51 self.info.labels = ['testbed-version:tb1', 'ab-version:ab1', 52 'cros-version:cros1'] 53 self.assertEqual(self.info.build, 'cros1') 54 self.info.labels = ['cros-version:cros1', 'ab-version:ab1', 55 'testbed-version:tb1'] 56 self.assertEqual(self.info.build, 'cros1') 57 58 59 def test_build_prefer_ab_over_testbed(self): 60 """When multiple versions are available, prefer ab over testbed.""" 61 self.info.labels = ['testbed-version:tb1', 'ab-version:ab1'] 62 self.assertEqual(self.info.build, 'ab1') 63 self.info.labels = ['ab-version:ab1', 'testbed-version:tb1'] 64 self.assertEqual(self.info.build, 'ab1') 65 66 67 def test_os_no_match(self): 68 """Use proper prefix to search for os information.""" 69 self.info.labels = ['something_else', 'cros-version:hana', 70 'os_without_colon'] 71 self.assertEqual(self.info.os, '') 72 73 74 def test_os_returns_first_match(self): 75 """Return the first matching os label.""" 76 self.info.labels = ['os:linux', 'os:windows', 'os_corrupted_label'] 77 self.assertEqual(self.info.os, 'linux') 78 79 80 def test_board_no_match(self): 81 """Use proper prefix to search for board information.""" 82 self.info.labels = ['something_else', 'cros-version:hana', 'os:blah', 83 'board_my_board_no_colon'] 84 self.assertEqual(self.info.board, '') 85 86 87 def test_board_returns_first_match(self): 88 """Return the first matching board label.""" 89 self.info.labels = ['board_corrupted', 'board:walk', 'board:bored'] 90 self.assertEqual(self.info.board, 'walk') 91 92 93 def test_pools_no_match(self): 94 """Use proper prefix to search for pool information.""" 95 self.info.labels = ['something_else', 'cros-version:hana', 'os:blah', 96 'board_my_board_no_colon', 'board:my_board'] 97 self.assertEqual(self.info.pools, set()) 98 99 100 def test_pools_returns_all_matches(self): 101 """Return all matching pool labels.""" 102 self.info.labels = ['board_corrupted', 'board:walk', 'board:bored', 103 'pool:first_pool', 'pool:second_pool'] 104 self.assertEqual(self.info.pools, {'second_pool', 'first_pool'}) 105 106 107 class InMemoryHostInfoStoreTest(unittest.TestCase): 108 """Basic tests for CachingHostInfoStore using InMemoryHostInfoStore.""" 109 110 def setUp(self): 111 self.store = host_info.InMemoryHostInfoStore() 112 113 114 def _verify_host_info_data(self, host_info, labels, attributes): 115 """Verifies the data in the given host_info.""" 116 self.assertListEqual(host_info.labels, labels) 117 self.assertDictEqual(host_info.attributes, attributes) 118 119 120 def test_first_get_refreshes_cache(self): 121 """Test that the first call to get gets the data from store.""" 122 self.store.info = host_info.HostInfo(['label1'], {'attrib1': 'val1'}) 123 got = self.store.get() 124 self._verify_host_info_data(got, ['label1'], {'attrib1': 'val1'}) 125 126 127 def test_repeated_get_returns_from_cache(self): 128 """Tests that repeated calls to get do not refresh cache.""" 129 self.store.info = host_info.HostInfo(['label1'], {'attrib1': 'val1'}) 130 got = self.store.get() 131 self._verify_host_info_data(got, ['label1'], {'attrib1': 'val1'}) 132 133 self.store.info = host_info.HostInfo(['label1', 'label2'], {}) 134 got = self.store.get() 135 self._verify_host_info_data(got, ['label1'], {'attrib1': 'val1'}) 136 137 138 def test_get_uncached_always_refreshes_cache(self): 139 """Tests that calling get_uncached always refreshes the cache.""" 140 self.store.info = host_info.HostInfo(['label1'], {'attrib1': 'val1'}) 141 got = self.store.get(force_refresh=True) 142 self._verify_host_info_data(got, ['label1'], {'attrib1': 'val1'}) 143 144 self.store.info = host_info.HostInfo(['label1', 'label2'], {}) 145 got = self.store.get(force_refresh=True) 146 self._verify_host_info_data(got, ['label1', 'label2'], {}) 147 148 149 def test_commit(self): 150 """Test that commit sends data to store.""" 151 info = host_info.HostInfo(['label1'], {'attrib1': 'val1'}) 152 self._verify_host_info_data(self.store.info, [], {}) 153 self.store.commit(info) 154 self._verify_host_info_data(self.store.info, ['label1'], 155 {'attrib1': 'val1'}) 156 157 158 def test_commit_then_get(self): 159 """Test a commit-get roundtrip.""" 160 got = self.store.get() 161 self._verify_host_info_data(got, [], {}) 162 163 info = host_info.HostInfo(['label1'], {'attrib1': 'val1'}) 164 self.store.commit(info) 165 got = self.store.get() 166 self._verify_host_info_data(got, ['label1'], {'attrib1': 'val1'}) 167 168 169 def test_commit_then_get_uncached(self): 170 """Test a commit-get_uncached roundtrip.""" 171 got = self.store.get() 172 self._verify_host_info_data(got, [], {}) 173 174 info = host_info.HostInfo(['label1'], {'attrib1': 'val1'}) 175 self.store.commit(info) 176 got = self.store.get(force_refresh=True) 177 self._verify_host_info_data(got, ['label1'], {'attrib1': 'val1'}) 178 179 180 def test_commit_deepcopies_data(self): 181 """Once commited, changes to HostInfo don't corrupt the store.""" 182 info = host_info.HostInfo(['label1'], {'attrib1': {'key1': 'data1'}}) 183 self.store.commit(info) 184 info.labels.append('label2') 185 info.attributes['attrib1']['key1'] = 'data2' 186 self._verify_host_info_data(self.store.info, 187 ['label1'], {'attrib1': {'key1': 'data1'}}) 188 189 190 def test_get_returns_deepcopy(self): 191 """The cached object is protected from |get| caller modifications.""" 192 self.store.info = host_info.HostInfo(['label1'], 193 {'attrib1': {'key1': 'data1'}}) 194 got = self.store.get() 195 self._verify_host_info_data(got, 196 ['label1'], {'attrib1': {'key1': 'data1'}}) 197 got.labels.append('label2') 198 got.attributes['attrib1']['key1'] = 'data2' 199 got = self.store.get() 200 self._verify_host_info_data(got, 201 ['label1'], {'attrib1': {'key1': 'data1'}}) 202 203 204 class ExceptionRaisingStore(host_info.CachingHostInfoStore): 205 """A test class that always raises on refresh / commit.""" 206 207 def __init__(self): 208 super(ExceptionRaisingStore, self).__init__() 209 self.refresh_raises = True 210 self.commit_raises = True 211 212 213 def _refresh_impl(self): 214 if self.refresh_raises: 215 raise host_info.StoreError('no can do') 216 return host_info.HostInfo() 217 218 def _commit_impl(self, _): 219 if self.commit_raises: 220 raise host_info.StoreError('wont wont wont') 221 222 223 class CachingHostInfoStoreErrorTest(unittest.TestCase): 224 """Tests error behaviours of CachingHostInfoStore.""" 225 226 def setUp(self): 227 self.store = ExceptionRaisingStore() 228 229 230 def test_failed_refresh_cleans_cache(self): 231 """Sanity checks return values when refresh raises.""" 232 with self.assertRaises(host_info.StoreError): 233 self.store.get() 234 # Since |get| hit an error, a subsequent get should again hit the store. 235 with self.assertRaises(host_info.StoreError): 236 self.store.get() 237 238 239 def test_failed_commit_cleans_cache(self): 240 """Check that a failed commit cleanes cache.""" 241 # Let's initialize the store without errors. 242 self.store.refresh_raises = False 243 self.store.get(force_refresh=True) 244 self.store.refresh_raises = True 245 246 with self.assertRaises(host_info.StoreError): 247 self.store.commit(host_info.HostInfo()) 248 # Since |commit| hit an error, a subsequent get should again hit the 249 # store. 250 with self.assertRaises(host_info.StoreError): 251 self.store.get() 252 253 254 class GetStoreFromMachineTest(unittest.TestCase): 255 """Tests the get_store_from_machine function.""" 256 257 def test_machine_is_dict(self): 258 machine = { 259 'something': 'else', 260 'host_info_store': 5 261 } 262 self.assertEqual(host_info.get_store_from_machine(machine), 5) 263 264 265 def test_machine_is_string(self): 266 machine = 'hostname' 267 self.assertTrue(isinstance(host_info.get_store_from_machine(machine), 268 host_info.InMemoryHostInfoStore)) 269 270 271 if __name__ == '__main__': 272 unittest.main() 273