2 # Copyright 2013 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 that
4 # can be found in the LICENSE file.
6 # pylint: disable=R0201,W0613
17 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18 sys.path.insert(0, ROOT_DIR)
19 sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party'))
21 from depot_tools import auto_stub
25 class RetryLoopMockedTest(auto_stub.TestCase):
26 """Base class for test cases that mock retry loop."""
29 super(RetryLoopMockedTest, self).setUp()
30 self._retry_attemps_cls = net.RetryAttempt
31 self.mock(net, 'sleep_before_retry', self.mocked_sleep_before_retry)
32 self.mock(net, 'current_time', self.mocked_current_time)
33 self.mock(net, 'RetryAttempt', self.mocked_retry_attempt)
37 def mocked_sleep_before_retry(self, attempt, max_wait):
38 self.sleeps.append((attempt, max_wait))
40 def mocked_current_time(self):
41 # One attempt is one virtual second.
42 return float(len(self.attempts))
44 def mocked_retry_attempt(self, *args, **kwargs):
45 attempt = self._retry_attemps_cls(*args, **kwargs)
46 self.attempts.append(attempt)
49 def assertAttempts(self, attempts, max_timeout):
50 """Asserts that retry loop executed given number of |attempts|."""
51 expected = [(i, max_timeout - i) for i in xrange(attempts)]
52 actual = [(x.attempt, x.remaining) for x in self.attempts]
53 self.assertEqual(expected, actual)
55 def assertSleeps(self, sleeps):
56 """Asserts that retry loop slept given number of times."""
57 self.assertEqual(sleeps, len(self.sleeps))
60 class RetryLoopTest(RetryLoopMockedTest):
61 """Test for retry_loop implementation."""
63 def test_sleep_before_retry(self):
64 # Verifies bounds. Because it's using a pseudo-random number generator and
65 # not a read random source, it's basically guaranteed to never return the
66 # same value twice consecutively.
67 a = net.calculate_sleep_before_retry(0, 0)
68 b = net.calculate_sleep_before_retry(0, 0)
69 self.assertTrue(a >= math.pow(1.5, -1), a)
70 self.assertTrue(b >= math.pow(1.5, -1), b)
71 self.assertTrue(a < 1.5 + math.pow(1.5, -1), a)
72 self.assertTrue(b < 1.5 + math.pow(1.5, -1), b)
73 self.assertNotEqual(a, b)
76 class GetHttpServiceTest(unittest.TestCase):
77 """Tests get_http_service implementation."""
79 def test_get_http_service(self):
80 def assert_is_appengine_service(service):
81 """Verifies HttpService is configured for App Engine communications."""
82 self.assertIsNotNone(service.authenticator)
84 def assert_is_googlestorage_service(service):
85 """Verifies HttpService is configured for GS communications."""
86 self.assertIsNone(service.authenticator)
88 # Can recognize app engine URLs.
89 assert_is_appengine_service(
90 net.get_http_service('https://appengine-app.appspot.com'))
91 assert_is_appengine_service(
92 net.get_http_service('https://version-dot-appengine-app.appspot.com'))
94 # Localhost is also sort of appengine when running on dev server...
95 assert_is_appengine_service(
96 net.get_http_service('http://localhost:8080'))
99 assert_is_googlestorage_service(
100 net.get_http_service('https://bucket-name.storage.googleapis.com'))
103 class HttpServiceTest(RetryLoopMockedTest):
104 """Tests for HttpService class."""
107 def mocked_http_service(
108 url='http://example.com',
109 perform_request=None,
113 class MockedAuthenticator(net.Authenticator):
114 def authorize(self, request):
115 return authorize(request) if authorize else None
116 def login(self, allow_user_interaction):
117 return login(allow_user_interaction) if login else False
119 class MockedRequestEngine(object):
120 def perform_request(self, request):
121 return perform_request(request) if perform_request else None
123 return net.HttpService(
125 authenticator=MockedAuthenticator(),
126 engine=MockedRequestEngine())
128 def test_request_GET_success(self):
129 service_url = 'http://example.com'
130 request_url = '/some_request'
133 def mock_perform_request(request):
135 request.get_full_url().startswith(service_url + request_url))
136 return request.make_fake_response(response)
138 service = self.mocked_http_service(url=service_url,
139 perform_request=mock_perform_request)
140 self.assertEqual(service.request(request_url).read(), response)
141 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
143 def test_request_POST_success(self):
144 service_url = 'http://example.com'
145 request_url = '/some_request'
148 def mock_perform_request(request):
150 request.get_full_url().startswith(service_url + request_url))
151 self.assertEqual('', request.body)
152 return request.make_fake_response(response)
154 service = self.mocked_http_service(url=service_url,
155 perform_request=mock_perform_request)
156 self.assertEqual(service.request(request_url, data={}).read(), response)
157 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
159 def test_request_PUT_success(self):
160 service_url = 'http://example.com'
161 request_url = '/some_request'
162 request_body = 'data_body'
163 response_body = 'True'
164 content_type = 'application/octet-stream'
166 def mock_perform_request(request):
168 request.get_full_url().startswith(service_url + request_url))
169 self.assertEqual(request_body, request.body)
170 self.assertEqual(request.method, 'PUT')
171 self.assertEqual(request.headers['Content-Type'], content_type)
172 return request.make_fake_response(response_body)
174 service = self.mocked_http_service(url=service_url,
175 perform_request=mock_perform_request)
176 response = service.request(request_url,
177 data=request_body, content_type=content_type, method='PUT')
178 self.assertEqual(response.read(), response_body)
179 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
181 def test_request_success_after_failure(self):
185 def mock_perform_request(request):
186 attempts.append(request)
187 if len(attempts) == 1:
188 raise net.ConnectionError()
189 return request.make_fake_response(response)
191 service = self.mocked_http_service(perform_request=mock_perform_request)
192 self.assertEqual(service.request('/', data={}).read(), response)
193 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
195 def test_request_failure_max_attempts_default(self):
196 def mock_perform_request(_request):
197 raise net.ConnectionError()
198 service = self.mocked_http_service(perform_request=mock_perform_request)
199 self.assertEqual(service.request('/'), None)
200 self.assertAttempts(net.URL_OPEN_MAX_ATTEMPTS, net.URL_OPEN_TIMEOUT)
202 def test_request_failure_max_attempts(self):
203 def mock_perform_request(_request):
204 raise net.ConnectionError()
205 service = self.mocked_http_service(perform_request=mock_perform_request)
206 self.assertEqual(service.request('/', max_attempts=23), None)
207 self.assertAttempts(23, net.URL_OPEN_TIMEOUT)
209 def test_request_failure_timeout(self):
210 def mock_perform_request(_request):
211 raise net.ConnectionError()
212 service = self.mocked_http_service(perform_request=mock_perform_request)
213 self.assertEqual(service.request('/', max_attempts=10000), None)
214 self.assertAttempts(int(net.URL_OPEN_TIMEOUT) + 1, net.URL_OPEN_TIMEOUT)
216 def test_request_failure_timeout_default(self):
217 def mock_perform_request(_request):
218 raise net.ConnectionError()
219 service = self.mocked_http_service(perform_request=mock_perform_request)
220 self.assertEqual(service.request('/', timeout=10.), None)
221 self.assertAttempts(11, 10.0)
223 def test_request_HTTP_error_no_retry(self):
225 def mock_perform_request(request):
226 count.append(request)
227 raise net.HttpError(400)
229 service = self.mocked_http_service(perform_request=mock_perform_request)
230 self.assertEqual(service.request('/', data={}), None)
231 self.assertEqual(1, len(count))
232 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
234 def test_request_HTTP_error_retry_404(self):
238 def mock_perform_request(request):
239 attempts.append(request)
240 if len(attempts) == 1:
241 raise net.HttpError(404)
242 return request.make_fake_response(response)
244 service = self.mocked_http_service(perform_request=mock_perform_request)
245 result = service.request('/', data={}, retry_404=True)
246 self.assertEqual(result.read(), response)
247 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
249 def test_request_HTTP_error_with_retry(self):
250 response = 'response'
253 def mock_perform_request(request):
254 attempts.append(request)
255 if len(attempts) == 1:
256 raise net.HttpError(500)
257 return request.make_fake_response(response)
259 service = self.mocked_http_service(perform_request=mock_perform_request)
260 self.assertTrue(service.request('/', data={}).read(), response)
261 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
263 def test_auth_success(self):
265 response = 'response'
267 def mock_perform_request(request):
268 calls.append('request')
269 if 'login' not in calls:
270 raise net.HttpError(403)
271 return request.make_fake_response(response)
273 def mock_authorize(request):
274 calls.append('authorize')
276 def mock_login(allow_user_interaction):
277 self.assertFalse(allow_user_interaction)
278 calls.append('login')
281 service = self.mocked_http_service(
282 perform_request=mock_perform_request,
283 authorize=mock_authorize,
285 self.assertEqual(service.request('/').read(), response)
287 ['authorize', 'request', 'login', 'authorize', 'request'], calls)
288 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
291 def test_auth_failure(self):
294 def mock_perform_request(_request):
295 raise net.HttpError(403)
297 def mock_login(allow_user_interaction):
298 self.assertFalse(allow_user_interaction)
302 service = self.mocked_http_service(perform_request=mock_perform_request,
304 self.assertEqual(service.request('/'), None)
305 self.assertEqual(len(count), 1)
306 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
308 def test_url_read(self):
309 # Successfully reads the data.
310 self.mock(net, 'url_open',
311 lambda url, **_kwargs: net.HttpResponse.get_fake_response('111', url))
312 self.assertEqual(net.url_read('https://fake_url.com/test'), '111')
314 # Respects url_open connection errors.
315 self.mock(net, 'url_open', lambda _url, **_kwargs: None)
316 self.assertIsNone(net.url_read('https://fake_url.com/test'))
318 # Respects read timeout errors.
319 def timeouting_http_response(url):
320 def read_mock(_size=None):
321 raise net.TimeoutError()
322 response = net.HttpResponse.get_fake_response('', url)
323 self.mock(response, 'read', read_mock)
326 self.mock(net, 'url_open',
327 lambda url, **_kwargs: timeouting_http_response(url))
328 self.assertIsNone(net.url_read('https://fake_url.com/test'))
330 def test_url_retrieve(self):
331 # Successfully reads the data.
332 @contextlib.contextmanager
333 def fake_open(_filepath, _mode):
334 yield StringIO.StringIO()
336 self.mock(__builtin__, 'open', fake_open)
337 self.mock(net, 'url_open',
338 lambda url, **_kwargs: net.HttpResponse.get_fake_response('111', url))
340 True, net.url_retrieve('filepath', 'https://localhost/test'))
342 # Respects url_open connection errors.
343 self.mock(net, 'url_open', lambda _url, **_kwargs: None)
345 False, net.url_retrieve('filepath', 'https://localhost/test'))
347 # Respects read timeout errors.
348 def timeouting_http_response(url):
349 def read_mock(_size=None):
350 raise net.TimeoutError()
351 response = net.HttpResponse.get_fake_response('', url)
352 self.mock(response, 'read', read_mock)
356 self.mock(os, 'remove', removed.append)
357 self.mock(net, 'url_open',
358 lambda url, **_kwargs: timeouting_http_response(url))
360 False, net.url_retrieve('filepath', 'https://localhost/test'))
361 self.assertEqual(['filepath'], removed)
364 if __name__ == '__main__':
366 level=(logging.DEBUG if '-v' in sys.argv else logging.FATAL))
368 unittest.TestCase.maxDiff = None