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

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

Issue 2987333002: Refactor all gRPC proxy code into a single class. (Closed)
Patch Set: Fix pylint errors Created 3 years, 4 months 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
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
11 11
12 import test_env_bot_code 12 import test_env_bot_code
13 test_env_bot_code.setup_test_env() 13 test_env_bot_code.setup_test_env()
14 14
15 from depot_tools import auto_stub 15 from depot_tools import auto_stub
16 16 import remote_client_grpc
17 try:
18 import remote_client_grpc
19 except ImportError as e:
20 print('Could not import gRPC remote client, likely due to missing grpc '
21 'library. Skipping tests.')
22 sys.exit(0)
23 17
24 18
25 class FakeBotServiceStub(object): 19 class FakeGrpcProxy(object):
26 def __init__(self, testobj): 20 def __init__(self, testobj):
27 self._testobj = testobj 21 self._testobj = testobj
28 22
29 def TaskUpdate(self, request, **_kwargs): 23 def call_unary(self, name, request):
30 return self._testobj._handle_call('TaskUpdate', request) 24 return self._testobj._handle_call(name, request)
31
32 def Handshake(self, request, **_kwargs):
33 return self._testobj._handle_call('Handshake', request)
34
35 def Poll(self, request, **_kwargs):
36 return self._testobj._handle_call('Poll', request)
37
38
39 # If gRPC isn't successfully imported, this will be seen as a nonstandard
40 # exception because it won't appear to be derived from Exception. This
41 # only affects PyLint because the test will never be run if gRPC import
42 # fails.
43 # pylint: disable=W0710
44 class FakeGrpcError(remote_client_grpc.grpc.RpcError):
45 """Duplicates a basic UNAVAILABLE error"""
46 def __init__(self, code):
47 self._code = code
48 super(FakeGrpcError, self).__init__('something terrible happened')
49
50 def code(self):
51 return self._code
52 25
53 26
54 class TestRemoteClientGrpc(auto_stub.TestCase): 27 class TestRemoteClientGrpc(auto_stub.TestCase):
55 def setUp(self): 28 def setUp(self):
56 super(TestRemoteClientGrpc, self).setUp() 29 super(TestRemoteClientGrpc, self).setUp()
57 self._num_sleeps = 0 30 self._num_sleeps = 0
58 def fake_sleep(_time): 31 def fake_sleep(_time):
59 self._num_sleeps += 1 32 self._num_sleeps += 1
60 self.mock(time, 'sleep', fake_sleep) 33 self.mock(time, 'sleep', fake_sleep)
61 self._client = remote_client_grpc.RemoteClientGrpc('1.2.3.4:90') 34 self._client = remote_client_grpc.RemoteClientGrpc('1.2.3.4:90',
62 self._client._stub = FakeBotServiceStub(self) 35 FakeGrpcProxy(self))
63 self._expected = [] 36 self._expected = []
64 self._error_codes = [] 37 self._error_codes = []
65 38
66 def _handle_call(self, method, request): 39 def _handle_call(self, method, request):
67 """This is called by FakeBotServiceStub to implement fake calls""" 40 """This is called by FakeGrpcProxy to implement fake calls"""
68 if len(self._error_codes) > 0:
69 code, self._error_codes = self._error_codes[0], self._error_codes[1:]
70 raise FakeGrpcError(code)
71
72 if self._error_codes:
73 text = self._error_codes
74 self._error_codes = ''
75 raise FakeGrpcError(text)
76
77 # Pop off the first item on the list 41 # Pop off the first item on the list
78 self.assertTrue(len(self._expected) > 0) 42 self.assertTrue(len(self._expected) > 0)
79 expected, self._expected = self._expected[0], self._expected[1:] 43 expected, self._expected = self._expected[0], self._expected[1:]
80 # Each element of the "expected" array should be a 3-tuple: 44 # Each element of the "expected" array should be a 3-tuple:
81 # * The name of the method (eg 'TaskUpdate') 45 # * The name of the method (eg 'TaskUpdate')
82 # * The proto request 46 # * The proto request
83 # * The proto response 47 # * The proto response
84 self.assertEqual(method, expected[0]) 48 self.assertEqual(method, expected[0])
85 self.assertEqual(request, expected[1]) 49 self.assertEqual(request, expected[1])
86 return expected[2] 50 return expected[2]
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 'server_version': u'101', 111 'server_version': u'101',
148 'bot_version': u'102', 112 'bot_version': u'102',
149 'bot_group_cfg_version': u'', 113 'bot_group_cfg_version': u'',
150 'bot_group_cfg': { 114 'bot_group_cfg': {
151 'dimensions': { 115 'dimensions': {
152 u'mammal': [u'kangaroo', u'emu'], 116 u'mammal': [u'kangaroo', u'emu'],
153 }, 117 },
154 }, 118 },
155 }) 119 })
156 120
157 def test_handshake_grpc_unavailable(self):
158 """Ensures that the handshake function sleeps after a gRPC error"""
159 msg_req = remote_client_grpc.swarming_bot_pb2.HandshakeRequest()
160 msg_req.attributes.CopyFrom(self.get_bot_attributes_proto())
161
162 # Create proto response
163 msg_rsp = remote_client_grpc.swarming_bot_pb2.HandshakeResponse()
164 msg_rsp.server_version = '101'
165 msg_rsp.bot_version = '102'
166 d1 = msg_rsp.bot_group_cfg.dimensions.add()
167 d1.name = 'mammal'
168 d1.values.extend(['kangaroo', 'emu'])
169
170 # Execute call and verify response
171 expected_call = ('Handshake', msg_req, msg_rsp)
172 self._expected.append(expected_call)
173 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
174 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
175 response = self._client.do_handshake(self.get_bot_attributes_dict())
176 self.assertEqual(self._num_sleeps, 2)
177 self.assertEqual(response, {
178 'server_version': u'101',
179 'bot_version': u'102',
180 'bot_group_cfg_version': u'',
181 'bot_group_cfg': {
182 'dimensions': {
183 u'mammal': [u'kangaroo', u'emu'],
184 },
185 },
186 })
187
188 def test_handshake_grpc_other_error(self):
189 """Ensures that the handshake function only catches UNAVAILABLE"""
190 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
191 self._error_codes.append(remote_client_grpc.grpc.StatusCode.INTERNAL)
192 got_exception = None
193 try:
194 self._client.do_handshake(self.get_bot_attributes_dict())
195 except remote_client_grpc.grpc.RpcError as g:
196 got_exception = g
197 self.assertEqual(got_exception.code(),
198 remote_client_grpc.grpc.StatusCode.INTERNAL)
199 self.assertEqual(self._num_sleeps, 1)
200
201 def test_handshake_grpc_too_many_errors(self):
202 """Ensures that the handshake function only catches UNAVAILABLE"""
203 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
204 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
205 self._error_codes.append(remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
206 self.mock(remote_client_grpc, 'MAX_GRPC_ATTEMPTS', 2)
207 with self.assertRaises(remote_client_grpc.grpc.RpcError) as g:
208 self._client.do_handshake(self.get_bot_attributes_dict())
209 self.assertEqual(self._num_sleeps, 2)
210 self.assertEqual(g.exception.code(),
211 remote_client_grpc.grpc.StatusCode.UNAVAILABLE)
212
213 def test_poll_manifest(self): 121 def test_poll_manifest(self):
214 """Verifies that we can generate a reasonable manifest from a proto""" 122 """Verifies that we can generate a reasonable manifest from a proto"""
215 msg_req = remote_client_grpc.swarming_bot_pb2.PollRequest() 123 msg_req = remote_client_grpc.swarming_bot_pb2.PollRequest()
216 msg_req.attributes.CopyFrom(self.get_bot_attributes_proto()) 124 msg_req.attributes.CopyFrom(self.get_bot_attributes_proto())
217 125
218 # Create dict response 126 # Create dict response
219 dict_rsp = { 127 dict_rsp = {
220 'bot_id': u'baby_bot', 128 'bot_id': u'baby_bot',
221 'command': None, 129 'command': None,
222 'dimensions': { 130 'dimensions': {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 self._expected.append(expected_call) 238 self._expected.append(expected_call)
331 response = self._client.post_task_update('abc123', 'baby_bot', params, 239 response = self._client.post_task_update('abc123', 'baby_bot', params,
332 ['abc', 7], -5) 240 ['abc', 7], -5)
333 self.assertTrue(response) 241 self.assertTrue(response)
334 242
335 if __name__ == '__main__': 243 if __name__ == '__main__':
336 logging.basicConfig( 244 logging.basicConfig(
337 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) 245 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL)
338 unittest.TestCase.maxDiff = None 246 unittest.TestCase.maxDiff = None
339 unittest.main() 247 unittest.main()
OLDNEW
« no previous file with comments | « appengine/swarming/swarming_bot/bot_code/remote_client_grpc.py ('k') | client/isolate_storage.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698