Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2016 The LUCI Authors. All rights reserved. | 2 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 import logging | 6 import logging |
| 7 import sys | 7 import sys |
| 8 import threading | 8 import threading |
| 9 import time | 9 import time |
| 10 import unittest | 10 import unittest |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 def TaskUpdate(self, request, **_kwargs): | 29 def TaskUpdate(self, request, **_kwargs): |
| 30 return self._testobj._handle_call('TaskUpdate', request) | 30 return self._testobj._handle_call('TaskUpdate', request) |
| 31 | 31 |
| 32 def Handshake(self, request, **_kwargs): | 32 def Handshake(self, request, **_kwargs): |
| 33 return self._testobj._handle_call('Handshake', request) | 33 return self._testobj._handle_call('Handshake', request) |
| 34 | 34 |
| 35 def Poll(self, request, **_kwargs): | 35 def Poll(self, request, **_kwargs): |
| 36 return self._testobj._handle_call('Poll', request) | 36 return self._testobj._handle_call('Poll', request) |
| 37 | 37 |
| 38 | 38 |
| 39 class FakeGrpcError(remote_client_grpc.grpc.RpcError): | |
| 40 """Duplicates a basic UNAVAILABLE error""" | |
| 41 def __init__(self, code): | |
| 42 self._code = code | |
| 43 super(FakeGrpcError, self).__init__('something terrible happened') | |
| 44 | |
| 45 def code(self): | |
| 46 return self._code | |
| 47 | |
|
M-A Ruel
2016/12/21 16:31:44
add empty line here
aludwin
2016/12/21 16:50:35
Done.
| |
| 39 class TestRemoteClientGrpc(auto_stub.TestCase): | 48 class TestRemoteClientGrpc(auto_stub.TestCase): |
| 40 def setUp(self): | 49 def setUp(self): |
| 41 super(TestRemoteClientGrpc, self).setUp() | 50 super(TestRemoteClientGrpc, self).setUp() |
| 51 self._num_sleeps = 0 | |
| 52 def fake_sleep(_time): | |
| 53 self._num_sleeps += 1 | |
| 54 self.mock(time, 'sleep', fake_sleep) | |
| 42 self._client = remote_client_grpc.RemoteClientGrpc('1.2.3.4:90') | 55 self._client = remote_client_grpc.RemoteClientGrpc('1.2.3.4:90') |
| 43 self._client._stub = FakeBotServiceStub(self) | 56 self._client._stub = FakeBotServiceStub(self) |
| 44 self._expected = [] | 57 self._expected = [] |
| 58 self._error_codes = [] | |
| 45 | 59 |
| 46 def _handle_call(self, method, request): | 60 def _handle_call(self, method, request): |
| 47 """This is called by FakeBotServiceStub to implement fake calls""" | 61 """This is called by FakeBotServiceStub to implement fake calls""" |
| 62 if len(self._error_codes) > 0: | |
| 63 code, self._error_codes = self._error_codes[0], self._error_codes[1:] | |
| 64 raise FakeGrpcError(code) | |
| 65 | |
| 66 if self._error_codes: | |
| 67 text = self._error_codes | |
| 68 self._error_codes = '' | |
| 69 raise FakeGrpcError(text) | |
| 70 | |
| 48 # Pop off the first item on the list | 71 # Pop off the first item on the list |
| 49 self.assertTrue(len(self._expected) > 0) | 72 self.assertTrue(len(self._expected) > 0) |
| 50 expected, self._expected = self._expected[0], self._expected[1:] | 73 expected, self._expected = self._expected[0], self._expected[1:] |
| 51 # Each element of the "expected" array should be a 3-tuple: | 74 # Each element of the "expected" array should be a 3-tuple: |
| 52 # * The name of the method (eg 'TaskUpdate') | 75 # * The name of the method (eg 'TaskUpdate') |
| 53 # * The proto request | 76 # * The proto request |
| 54 # * The proto response | 77 # * The proto response |
| 55 self.assertEqual(method, expected[0]) | 78 self.assertEqual(method, expected[0]) |
| 56 self.assertEqual(request, expected[1]) | 79 self.assertEqual(request, expected[1]) |
| 57 return expected[2] | 80 return expected[2] |
| 58 | 81 |
| 59 def get_bot_attributes_dict(self): | 82 def get_bot_attributes_dict(self): |
| 60 """Gets the attributes of a basic bot""" | 83 """Gets the attributes of a basic bot; must match next function""" |
| 61 return { | 84 return { |
| 62 'version': '123', | 85 'version': '123', |
| 63 'dimensions': { | 86 'dimensions': { |
| 64 'mammal': ['ferrett', 'wombat'], | 87 'mammal': ['ferrett', 'wombat'], |
| 65 'pool': ['dead'], | 88 'pool': ['dead'], |
| 66 }, | 89 }, |
| 67 'state': { | 90 'state': { |
| 68 'audio': ['Rachmaninov', 'Stravinsky'], | 91 'audio': ['Rachmaninov', 'Stravinsky'], |
| 69 'cost_usd_hour': 3.141, | 92 'cost_usd_hour': 3.141, |
| 70 'cpu': 'Intel 8080', | 93 'cpu': 'Intel 8080', |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 118 'server_version': u'101', | 141 'server_version': u'101', |
| 119 'bot_version': u'102', | 142 'bot_version': u'102', |
| 120 'bot_group_cfg_version': u'', | 143 'bot_group_cfg_version': u'', |
| 121 'bot_group_cfg': { | 144 'bot_group_cfg': { |
| 122 'dimensions': { | 145 'dimensions': { |
| 123 u'mammal': [u'kangaroo', u'emu'], | 146 u'mammal': [u'kangaroo', u'emu'], |
| 124 }, | 147 }, |
| 125 }, | 148 }, |
| 126 }) | 149 }) |
| 127 | 150 |
| 151 def test_handshake_grpc_unavailable(self): | |
| 152 """Ensures that the handshake function sleeps after a gRPC error""" | |
| 153 msg_req = remote_client_grpc.swarming_bot_pb2.HandshakeRequest() | |
| 154 msg_req.attributes.CopyFrom(self.get_bot_attributes_proto()) | |
| 155 | |
| 156 # Create proto response | |
| 157 msg_rsp = remote_client_grpc.swarming_bot_pb2.HandshakeResponse() | |
| 158 msg_rsp.server_version = '101' | |
| 159 msg_rsp.bot_version = '102' | |
| 160 d1 = msg_rsp.bot_group_cfg.dimensions.add() | |
| 161 d1.name = 'mammal' | |
| 162 d1.values.extend(['kangaroo', 'emu']) | |
| 163 | |
| 164 # Execute call and verify response | |
| 165 expected_call = ('Handshake', msg_req, msg_rsp) | |
| 166 self._expected.append(expected_call) | |
| 167 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 168 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 169 response = self._client.do_handshake(self.get_bot_attributes_dict()) | |
| 170 self.assertEqual(self._num_sleeps, 2) | |
| 171 self.assertEqual(response, { | |
| 172 'server_version': u'101', | |
| 173 'bot_version': u'102', | |
| 174 'bot_group_cfg_version': u'', | |
| 175 'bot_group_cfg': { | |
| 176 'dimensions': { | |
| 177 u'mammal': [u'kangaroo', u'emu'], | |
| 178 }, | |
| 179 }, | |
| 180 }) | |
| 181 | |
| 182 def test_handshake_grpc_other_error(self): | |
| 183 """Ensures that the handshake function only catches UNAVAILABLE""" | |
| 184 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 185 self._error_codes.append(remote_client_grpc.grpc.StatusCode.INTERNAL) | |
| 186 got_exception = None | |
| 187 try: | |
| 188 self._client.do_handshake(self.get_bot_attributes_dict()) | |
| 189 except remote_client_grpc.grpc.RpcError as g: | |
| 190 got_exception = g | |
| 191 self.assertEqual(got_exception.code(), | |
| 192 remote_client_grpc.grpc.StatusCode.INTERNAL) | |
| 193 self.assertEqual(self._num_sleeps, 1) | |
| 194 | |
| 195 def test_handshake_grpc_too_many_errors(self): | |
| 196 """Ensures that the handshake function only catches UNAVAILABLE""" | |
| 197 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 198 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 199 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 200 self.mock(remote_client_grpc, 'MAX_GRPC_ATTEMPTS', 2) | |
| 201 got_exception = None | |
| 202 try: | |
|
M-A Ruel
2016/12/21 16:31:44
FYI, you can do instead:
with self.asserRaises(re
aludwin
2016/12/21 16:50:35
Done.
| |
| 203 self._client.do_handshake(self.get_bot_attributes_dict()) | |
| 204 except remote_client_grpc.grpc.RpcError as g: | |
| 205 got_exception = g | |
| 206 self.assertEqual(self._num_sleeps, 2) | |
| 207 self.assertEqual(got_exception.code(), | |
| 208 remote_client_grpc.grpc.StatusCode.UNAVAILABLE) | |
| 209 | |
| 128 def test_poll_manifest(self): | 210 def test_poll_manifest(self): |
| 129 """Verifies that we can generate a reasonable manifest from a proto""" | 211 """Verifies that we can generate a reasonable manifest from a proto""" |
| 130 msg_req = remote_client_grpc.swarming_bot_pb2.PollRequest() | 212 msg_req = remote_client_grpc.swarming_bot_pb2.PollRequest() |
| 131 msg_req.attributes.CopyFrom(self.get_bot_attributes_proto()) | 213 msg_req.attributes.CopyFrom(self.get_bot_attributes_proto()) |
| 132 | 214 |
| 133 # Create dict response | 215 # Create dict response |
| 134 dict_rsp = { | 216 dict_rsp = { |
| 135 'bot_id': u'baby_bot', | 217 'bot_id': u'baby_bot', |
| 136 'command': None, | 218 'command': None, |
| 137 'dimensions': { | 219 'dimensions': { |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 245 self._expected.append(expected_call) | 327 self._expected.append(expected_call) |
| 246 response = self._client.post_task_update('abc123', 'baby_bot', params, | 328 response = self._client.post_task_update('abc123', 'baby_bot', params, |
| 247 ['abc', 7], -5) | 329 ['abc', 7], -5) |
| 248 self.assertTrue(response) | 330 self.assertTrue(response) |
| 249 | 331 |
| 250 if __name__ == '__main__': | 332 if __name__ == '__main__': |
| 251 logging.basicConfig( | 333 logging.basicConfig( |
| 252 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) | 334 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) |
| 253 unittest.TestCase.maxDiff = None | 335 unittest.TestCase.maxDiff = None |
| 254 unittest.main() | 336 unittest.main() |
| OLD | NEW |