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
14 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
15 sys.path.insert(0, ROOT_DIR)
16 sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party'))
18 from depot_tools import auto_stub
22 class RetryLoopMockedTest(auto_stub.TestCase):
23 """Base class for test cases that mock retry loop."""
26 super(RetryLoopMockedTest, self).setUp()
27 self._retry_attemps_cls = net.RetryAttempt
28 self.mock(net, 'sleep_before_retry', self.mocked_sleep_before_retry)
29 self.mock(net, 'current_time', self.mocked_current_time)
30 self.mock(net, 'RetryAttempt', self.mocked_retry_attempt)
34 def mocked_sleep_before_retry(self, attempt, max_wait):
35 self.sleeps.append((attempt, max_wait))
37 def mocked_current_time(self):
38 # One attempt is one virtual second.
39 return float(len(self.attempts))
41 def mocked_retry_attempt(self, *args, **kwargs):
42 attempt = self._retry_attemps_cls(*args, **kwargs)
43 self.attempts.append(attempt)
46 def assertAttempts(self, attempts, max_timeout):
47 """Asserts that retry loop executed given number of |attempts|."""
48 expected = [(i, max_timeout - i) for i in xrange(attempts)]
49 actual = [(x.attempt, x.remaining) for x in self.attempts]
50 self.assertEqual(expected, actual)
52 def assertSleeps(self, sleeps):
53 """Asserts that retry loop slept given number of times."""
54 self.assertEqual(sleeps, len(self.sleeps))
57 class RetryLoopTest(RetryLoopMockedTest):
58 """Test for retry_loop implementation."""
60 def test_sleep_before_retry(self):
61 # Verifies bounds. Because it's using a pseudo-random number generator and
62 # not a read random source, it's basically guaranteed to never return the
63 # same value twice consecutively.
64 a = net.calculate_sleep_before_retry(0, 0)
65 b = net.calculate_sleep_before_retry(0, 0)
66 self.assertTrue(a >= math.pow(1.5, -1), a)
67 self.assertTrue(b >= math.pow(1.5, -1), b)
68 self.assertTrue(a < 1.5 + math.pow(1.5, -1), a)
69 self.assertTrue(b < 1.5 + math.pow(1.5, -1), b)
70 self.assertNotEqual(a, b)
73 class GetHttpServiceTest(unittest.TestCase):
74 """Tests get_http_service implementation."""
76 def test_get_http_service(self):
77 def assert_is_appengine_service(service):
78 """Verifies HttpService is configured for App Engine communications."""
79 self.assertTrue(service.use_count_key)
80 self.assertIsNotNone(service.authenticator)
82 def assert_is_googlestorage_service(service):
83 """Verifies HttpService is configured for GS communications."""
84 self.assertFalse(service.use_count_key)
85 self.assertIsNone(service.authenticator)
87 # Can recognize app engine URLs.
88 assert_is_appengine_service(
89 net.get_http_service('https://appengine-app.appspot.com'))
90 assert_is_appengine_service(
91 net.get_http_service('https://version-dot-appengine-app.appspot.com'))
93 # Localhost is also sort of appengine when running on dev server...
94 assert_is_appengine_service(
95 net.get_http_service('http://localhost:8080'))
98 assert_is_googlestorage_service(
99 net.get_http_service('https://bucket-name.storage.googleapis.com'))
102 class HttpServiceTest(RetryLoopMockedTest):
103 """Tests for HttpService class."""
106 def mocked_http_service(
107 url='http://example.com',
108 perform_request=None,
112 class MockedAuthenticator(net.Authenticator):
113 def authorize(self, request):
114 return authorize(request) if authorize else None
115 def login(self, allow_user_interaction):
116 return login(allow_user_interaction) if login else False
118 class MockedRequestEngine(object):
119 def perform_request(self, request):
120 return perform_request(request) if perform_request else None
122 return net.HttpService(
124 authenticator=MockedAuthenticator(),
125 engine=MockedRequestEngine())
127 def test_request_GET_success(self):
128 service_url = 'http://example.com'
129 request_url = '/some_request'
132 def mock_perform_request(request):
134 request.get_full_url().startswith(service_url + request_url))
135 return request.make_fake_response(response)
137 service = self.mocked_http_service(url=service_url,
138 perform_request=mock_perform_request)
139 self.assertEqual(service.request(request_url).read(), response)
140 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
142 def test_request_POST_success(self):
143 service_url = 'http://example.com'
144 request_url = '/some_request'
147 def mock_perform_request(request):
149 request.get_full_url().startswith(service_url + request_url))
150 self.assertEqual('', request.body)
151 return request.make_fake_response(response)
153 service = self.mocked_http_service(url=service_url,
154 perform_request=mock_perform_request)
155 self.assertEqual(service.request(request_url, data={}).read(), response)
156 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
158 def test_request_PUT_success(self):
159 service_url = 'http://example.com'
160 request_url = '/some_request'
161 request_body = 'data_body'
162 response_body = 'True'
163 content_type = 'application/octet-stream'
165 def mock_perform_request(request):
167 request.get_full_url().startswith(service_url + request_url))
168 self.assertEqual(request_body, request.body)
169 self.assertEqual(request.method, 'PUT')
170 self.assertEqual(request.headers['Content-Type'], content_type)
171 return request.make_fake_response(response_body)
173 service = self.mocked_http_service(url=service_url,
174 perform_request=mock_perform_request)
175 response = service.request(request_url,
176 data=request_body, content_type=content_type, method='PUT')
177 self.assertEqual(response.read(), response_body)
178 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
180 def test_request_success_after_failure(self):
183 def mock_perform_request(request):
184 params = dict(request.params)
185 if params.get(net.COUNT_KEY) != 1:
186 raise net.ConnectionError()
187 return request.make_fake_response(response)
189 service = self.mocked_http_service(perform_request=mock_perform_request)
190 self.assertEqual(service.request('/', data={}).read(), response)
191 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
193 def test_request_failure_max_attempts_default(self):
194 def mock_perform_request(_request):
195 raise net.ConnectionError()
196 service = self.mocked_http_service(perform_request=mock_perform_request)
197 self.assertEqual(service.request('/'), None)
198 self.assertAttempts(net.URL_OPEN_MAX_ATTEMPTS, net.URL_OPEN_TIMEOUT)
200 def test_request_failure_max_attempts(self):
201 def mock_perform_request(_request):
202 raise net.ConnectionError()
203 service = self.mocked_http_service(perform_request=mock_perform_request)
204 self.assertEqual(service.request('/', max_attempts=23), None)
205 self.assertAttempts(23, net.URL_OPEN_TIMEOUT)
207 def test_request_failure_timeout(self):
208 def mock_perform_request(_request):
209 raise net.ConnectionError()
210 service = self.mocked_http_service(perform_request=mock_perform_request)
211 self.assertEqual(service.request('/', max_attempts=10000), None)
212 self.assertAttempts(int(net.URL_OPEN_TIMEOUT) + 1, net.URL_OPEN_TIMEOUT)
214 def test_request_failure_timeout_default(self):
215 def mock_perform_request(_request):
216 raise net.ConnectionError()
217 service = self.mocked_http_service(perform_request=mock_perform_request)
218 self.assertEqual(service.request('/', timeout=10.), None)
219 self.assertAttempts(11, 10.0)
221 def test_request_HTTP_error_no_retry(self):
223 def mock_perform_request(request):
224 count.append(request)
225 raise net.HttpError(400)
227 service = self.mocked_http_service(perform_request=mock_perform_request)
228 self.assertEqual(service.request('/', data={}), None)
229 self.assertEqual(1, len(count))
230 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
232 def test_request_HTTP_error_retry_404(self):
235 def mock_perform_request(request):
236 params = dict(request.params)
237 if params.get(net.COUNT_KEY) == 1:
238 return request.make_fake_response(response)
239 raise net.HttpError(404)
241 service = self.mocked_http_service(perform_request=mock_perform_request)
242 result = service.request('/', data={}, retry_404=True)
243 self.assertEqual(result.read(), response)
244 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
246 def test_request_HTTP_error_with_retry(self):
247 response = 'response'
249 def mock_perform_request(request):
250 params = dict(request.params)
251 if params.get(net.COUNT_KEY) != 1:
252 raise net.HttpError(500)
253 return request.make_fake_response(response)
255 service = self.mocked_http_service(perform_request=mock_perform_request)
256 self.assertTrue(service.request('/', data={}).read(), response)
257 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
259 def test_auth_success(self):
261 response = 'response'
263 def mock_perform_request(request):
264 calls.append('request')
265 if 'login' not in calls:
266 raise net.HttpError(403)
267 return request.make_fake_response(response)
269 def mock_authorize(request):
270 calls.append('authorize')
272 def mock_login(allow_user_interaction):
273 self.assertFalse(allow_user_interaction)
274 calls.append('login')
277 service = self.mocked_http_service(
278 perform_request=mock_perform_request,
279 authorize=mock_authorize,
281 self.assertEqual(service.request('/').read(), response)
283 ['authorize', 'request', 'login', 'authorize', 'request'], calls)
284 self.assertAttempts(2, net.URL_OPEN_TIMEOUT)
287 def test_auth_failure(self):
290 def mock_perform_request(_request):
291 raise net.HttpError(403)
293 def mock_login(allow_user_interaction):
294 self.assertFalse(allow_user_interaction)
298 service = self.mocked_http_service(perform_request=mock_perform_request,
300 self.assertEqual(service.request('/'), None)
301 self.assertEqual(len(count), 1)
302 self.assertAttempts(1, net.URL_OPEN_TIMEOUT)
304 def test_url_read(self):
305 # Successfully reads the data.
306 self.mock(net, 'url_open',
307 lambda url, **_kwargs: net.HttpResponse.get_fake_response('111', url))
308 self.assertEqual(net.url_read('https://fake_url.com/test'), '111')
310 # Respects url_open connection errors.
311 self.mock(net, 'url_open', lambda _url, **_kwargs: None)
312 self.assertIsNone(net.url_read('https://fake_url.com/test'))
314 # Respects read timeout errors.
315 def timeouting_http_response(url):
316 def read_mock(_size=None):
317 raise net.TimeoutError()
318 response = net.HttpResponse.get_fake_response('', url)
319 self.mock(response, 'read', read_mock)
322 self.mock(net, 'url_open',
323 lambda url, **_kwargs: timeouting_http_response(url))
324 self.assertIsNone(net.url_read('https://fake_url.com/test'))
327 if __name__ == '__main__':
329 level=(logging.DEBUG if '-v' in sys.argv else logging.FATAL))
331 unittest.TestCase.maxDiff = None