3 from __future__ import absolute_import, print_function
10 import arvnodeman.clientactor as clientactor
11 from . import testutil
13 class RemotePollLoopActorTestCase(testutil.RemotePollLoopActorTestMixin,
15 class MockClientError(Exception):
18 class TestActor(clientactor.RemotePollLoopActor):
19 LOGGER_NAME = 'arvnodeman.testpoll'
21 def _send_request(self):
23 TestActor.CLIENT_ERRORS = (MockClientError,)
24 TEST_CLASS = TestActor
27 def build_monitor(self, side_effect, *args, **kwargs):
28 super(RemotePollLoopActorTestCase, self).build_monitor(*args, **kwargs)
29 self.client.side_effect = side_effect
31 def test_poll_loop_starts_after_subscription(self):
32 self.build_monitor(['test1'])
33 self.monitor.subscribe(self.subscriber).get(self.TIMEOUT)
34 self.stop_proxy(self.monitor)
35 self.subscriber.assert_called_with('test1')
36 self.assertTrue(self.timer.schedule.called)
38 def test_poll_loop_continues_after_failure(self):
39 self.build_monitor(self.MockClientError)
40 self.monitor.subscribe(self.subscriber).get(self.TIMEOUT)
41 self.assertTrue(self.stop_proxy(self.monitor),
42 "poll loop died after error")
43 self.assertTrue(self.timer.schedule.called,
44 "poll loop did not reschedule after error")
45 self.assertFalse(self.subscriber.called,
46 "poll loop notified subscribers after error")
48 def test_late_subscribers_get_responses(self):
49 self.build_monitor(['pre_late_test', 'late_test'])
50 mock_subscriber = mock.Mock(name='mock_subscriber')
51 self.monitor.subscribe(mock_subscriber).get(self.TIMEOUT)
52 self.monitor.subscribe(self.subscriber)
53 self.monitor.poll().get(self.TIMEOUT)
54 self.stop_proxy(self.monitor)
55 self.subscriber.assert_called_with('late_test')
57 def test_survive_dead_subscriptions(self):
58 self.build_monitor(['survive1', 'survive2'])
59 dead_subscriber = mock.Mock(name='dead_subscriber')
60 dead_subscriber.side_effect = pykka.ActorDeadError
61 self.monitor.subscribe(dead_subscriber)
62 self.monitor.subscribe(self.subscriber)
63 self.monitor.poll().get(self.TIMEOUT)
64 self.assertTrue(self.stop_proxy(self.monitor),
65 "poll loop died from dead subscriber")
66 self.subscriber.assert_called_with('survive2')
68 def check_poll_timers(self, *test_times):
69 schedule_mock = self.timer.schedule
71 with mock.patch('time.time') as time_mock:
72 for fake_time, expect_next in test_times:
73 time_mock.return_value = fake_time
74 self.monitor.poll(last_expect).get(self.TIMEOUT)
75 self.assertTrue(schedule_mock.called)
76 self.assertEqual(expect_next, schedule_mock.call_args[0][0])
77 schedule_mock.reset_mock()
78 last_expect = expect_next
80 def test_poll_timing_on_consecutive_successes_with_drift(self):
81 self.build_monitor(['1', '2'], poll_wait=3, max_poll_wait=14)
82 self.check_poll_timers((0, 3), (4, 6))
84 def test_poll_backoff_on_failures(self):
85 self.build_monitor(self.MockClientError, poll_wait=3, max_poll_wait=14)
86 self.check_poll_timers((0, 6), (6, 18), (18, 32))
88 def test_poll_timing_after_error_recovery(self):
89 self.build_monitor(['a', self.MockClientError(), 'b'],
90 poll_wait=3, max_poll_wait=14)
91 self.check_poll_timers((0, 3), (4, 10), (10, 13))
93 def test_no_subscriptions_by_key_without_support(self):
94 self.build_monitor([])
95 with self.assertRaises(AttributeError):
96 self.monitor.subscribe_to('key')
99 class RemotePollLoopActorWithKeysTestCase(testutil.RemotePollLoopActorTestMixin,
101 class TestActor(RemotePollLoopActorTestCase.TestActor):
102 def _item_key(self, item):
104 TEST_CLASS = TestActor
107 def build_monitor(self, side_effect, *args, **kwargs):
108 super(RemotePollLoopActorWithKeysTestCase, self).build_monitor(
110 self.client.side_effect = side_effect
112 def test_key_subscription(self):
113 self.build_monitor([[{'key': 1}, {'key': 2}]])
114 self.monitor.subscribe_to(2, self.subscriber).get(self.TIMEOUT)
115 self.stop_proxy(self.monitor)
116 self.subscriber.assert_called_with({'key': 2})
118 def test_survive_dead_key_subscriptions(self):
120 self.build_monitor([[item], [item]])
121 dead_subscriber = mock.Mock(name='dead_subscriber')
122 dead_subscriber.side_effect = pykka.ActorDeadError
123 self.monitor.subscribe_to(3, dead_subscriber)
124 self.monitor.subscribe_to(3, self.subscriber)
125 self.monitor.poll().get(self.TIMEOUT)
126 self.assertTrue(self.stop_proxy(self.monitor),
127 "poll loop died from dead key subscriber")
128 self.subscriber.assert_called_with(item)
130 def test_mixed_subscriptions(self):
132 self.build_monitor([[item], [item]])
133 key_subscriber = mock.Mock(name='key_subscriber')
134 self.monitor.subscribe(self.subscriber)
135 self.monitor.subscribe_to(4, key_subscriber)
136 self.monitor.poll().get(self.TIMEOUT)
137 self.stop_proxy(self.monitor)
138 self.subscriber.assert_called_with([item])
139 key_subscriber.assert_called_with(item)
141 def test_subscription_to_missing_key(self):
142 self.build_monitor([[]])
143 self.monitor.subscribe_to('nonesuch', self.subscriber).get(self.TIMEOUT)
144 self.stop_proxy(self.monitor)
145 self.subscriber.assert_called_with(None)
148 if __name__ == '__main__':