Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(606)

Side by Side Diff: appengine/swarming/swarming_bot/bot_code/remote_client_grpc_test.py

Issue 2592683002: Retry non-streaming gRPC calls (Closed)
Patch Set: Call calculate_sleep_before_retry and add unit tests Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/swarming/swarming_bot/bot_code/remote_client_grpc.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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()
OLDNEW
« no previous file with comments | « appengine/swarming/swarming_bot/bot_code/remote_client_grpc.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698