Home | History | Annotate | Download | only in hosts
      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 from __future__ import print_function
      6 from __future__ import absolute_import
      7 
      8 import mock
      9 import unittest
     10 
     11 import common
     12 from autotest_lib.server.hosts import host_info
     13 from autotest_lib.server.hosts import shadowing_store
     14 
     15 
     16 class ShadowingStoreTestCase(unittest.TestCase):
     17     """Tests shadowing capabilities of ShadowingStore"""
     18 
     19     def test_init_commits_to_shadow(self):
     20         """Initialization updates the shadow store"""
     21         info = host_info.HostInfo(labels='blah', attributes='boo')
     22         primary = _FakeRaisingStore(info)
     23         shadow = _FakeRaisingStore()
     24         store = shadowing_store.ShadowingStore(primary, shadow)
     25         self.assertEqual(shadow.get(), info)
     26 
     27     def test_commit_commits_to_both_stores(self):
     28         """Successful commit involves committing to both stores"""
     29         primary = _FakeRaisingStore()
     30         shadow = _FakeRaisingStore()
     31         store = shadowing_store.ShadowingStore(primary, shadow)
     32         info = host_info.HostInfo(labels='blah', attributes='boo')
     33         store.commit(info)
     34         self.assertEqual(primary.get(), info)
     35         self.assertEqual(shadow.get(), info)
     36 
     37     def test_commit_ignores_failure_to_commit_to_shadow(self):
     38         """Failure to commit to shadow store does not affect the result"""
     39         init_info = host_info.HostInfo(labels='init')
     40         primary = _FakeRaisingStore(init_info)
     41         shadow = _FakeRaisingStore(init_info, raise_on_commit=True)
     42         store = shadowing_store.ShadowingStore(primary, shadow)
     43         info = host_info.HostInfo(labels='blah', attributes='boo')
     44         store.commit(info)
     45         self.assertEqual(primary.get(), info)
     46         self.assertEqual(shadow.get(), init_info)
     47 
     48     def test_refresh_validates_matching_stores(self):
     49         """Successful validation on refresh returns the correct info"""
     50         init_info = host_info.HostInfo(labels='init')
     51         primary = _FakeRaisingStore(init_info)
     52         shadow = _FakeRaisingStore(init_info)
     53         store = shadowing_store.ShadowingStore(primary, shadow)
     54         got = store.get(force_refresh=True)
     55         self.assertEqual(got, init_info)
     56 
     57     def test_refresh_ignores_failed_refresh_from_shadow_store(self):
     58         """Failure to refresh from shadow store does not affect the result"""
     59         init_info = host_info.HostInfo(labels='init')
     60         primary = _FakeRaisingStore(init_info)
     61         shadow = _FakeRaisingStore(init_info, raise_on_refresh=True)
     62         store = shadowing_store.ShadowingStore(primary, shadow)
     63         got = store.get(force_refresh=True)
     64         self.assertEqual(got, init_info)
     65 
     66     def test_refresh_complains_on_mismatching_stores(self):
     67         """Store complains on mismatched responses from the primary / shadow"""
     68         callback = mock.MagicMock()
     69         p_info = host_info.HostInfo('primary')
     70         primary = _FakeRaisingStore(p_info)
     71         shadow = _FakeRaisingStore()
     72         store = shadowing_store.ShadowingStore(primary, shadow,
     73                                                mismatch_callback=callback)
     74         # ShadowingStore will update shadow on initialization, so we modify it
     75         # after creating store.
     76         s_info = host_info.HostInfo('shadow')
     77         shadow.commit(s_info)
     78 
     79         got = store.get(force_refresh=True)
     80         self.assertEqual(got, p_info)
     81         callback.assert_called_once_with(p_info, s_info)
     82 
     83     def test_refresh_fixes_mismatch_in_stores(self):
     84         """On finding a mismatch, the difference is fixed by the store"""
     85         callback = mock.MagicMock()
     86         p_info = host_info.HostInfo('primary')
     87         primary = _FakeRaisingStore(p_info)
     88         shadow = _FakeRaisingStore()
     89         store = shadowing_store.ShadowingStore(primary, shadow,
     90                                                mismatch_callback=callback)
     91         # ShadowingStore will update shadow on initialization, so we modify it
     92         # after creating store.
     93         s_info = host_info.HostInfo('shadow')
     94         shadow.commit(s_info)
     95 
     96         got = store.get(force_refresh=True)
     97         self.assertEqual(got, p_info)
     98         callback.assert_called_once_with(p_info, s_info)
     99         self.assertEqual(got, shadow.get())
    100 
    101         got = store.get(force_refresh=True)
    102         self.assertEqual(got, p_info)
    103         # No extra calls, just the one we already saw above.
    104         callback.assert_called_once_with(p_info, s_info)
    105 
    106 
    107 class _FakeRaisingStore(host_info.InMemoryHostInfoStore):
    108     """A fake store that raises an error on refresh / commit if requested"""
    109 
    110     def __init__(self, info=None, raise_on_refresh=False,
    111                  raise_on_commit=False):
    112         """
    113         @param info: A HostInfo to initialize the store with.
    114         @param raise_on_refresh: If True, _refresh_impl raises a StoreError.
    115         @param raise_on_commit: If True, _commit_impl raises a StoreError.
    116         """
    117         super(_FakeRaisingStore, self).__init__(info)
    118         self._raise_on_refresh = raise_on_refresh
    119         self._raise_on_commit = raise_on_commit
    120 
    121     def _refresh_impl(self):
    122         print('refresh_impl')
    123         if self._raise_on_refresh:
    124             raise host_info.StoreError('Test refresh error')
    125         return super(_FakeRaisingStore, self)._refresh_impl()
    126 
    127     def _commit_impl(self, info):
    128         print('commit_impl: %s' % info)
    129         if self._raise_on_commit:
    130             raise host_info.StoreError('Test commit error')
    131         super(_FakeRaisingStore, self)._commit_impl(info)
    132 
    133 
    134 if __name__ == '__main__':
    135     unittest.main()
    136