| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import json | |
| 7 import logging | 6 import logging |
| 8 import math | 7 import math |
| 9 import os | 8 import os |
| 10 import shutil | |
| 11 import StringIO | 9 import StringIO |
| 12 import sys | 10 import sys |
| 13 import tempfile | |
| 14 import time | |
| 15 import unittest | 11 import unittest |
| 16 import urllib2 | 12 import urllib2 |
| 17 | 13 |
| 18 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 14 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 19 sys.path.insert(0, ROOT_DIR) | 15 sys.path.insert(0, ROOT_DIR) |
| 20 | 16 |
| 21 import auto_stub | 17 import auto_stub |
| 22 import run_isolated | 18 from utils import net |
| 23 | 19 |
| 24 # Number of times self._now() is called per loop in HttpService._retry_loop(). | 20 # Number of times self._now() is called per loop in HttpService._retry_loop(). |
| 25 NOW_CALLS_PER_OPEN = 2 | 21 NOW_CALLS_PER_OPEN = 2 |
| 26 | 22 |
| 27 | 23 |
| 28 class RemoteTest(run_isolated.Remote): | |
| 29 def get_file_handler(self, _): # pylint: disable=R0201 | |
| 30 def upload_file(item, _dest): | |
| 31 if type(item) == type(Exception) and issubclass(item, Exception): | |
| 32 raise item() | |
| 33 elif isinstance(item, int): | |
| 34 time.sleep(int(item) / 100) | |
| 35 return upload_file | |
| 36 | |
| 37 | |
| 38 def fake_http_response(content, url): | |
| 39 """Returns run_isolated.HttpResponse with predefined content.""" | |
| 40 stream = StringIO.StringIO(content) | |
| 41 stream.headers = {'content-length': len(content)} | |
| 42 return run_isolated.HttpResponse(stream, url) | |
| 43 | |
| 44 | |
| 45 class RunIsolatedTest(auto_stub.TestCase): | |
| 46 def setUp(self): | |
| 47 super(RunIsolatedTest, self).setUp() | |
| 48 self.tempdir = tempfile.mkdtemp(prefix='run_isolated') | |
| 49 os.chdir(self.tempdir) | |
| 50 | |
| 51 def tearDown(self): | |
| 52 os.chdir(ROOT_DIR) | |
| 53 shutil.rmtree(self.tempdir) | |
| 54 super(RunIsolatedTest, self).tearDown() | |
| 55 | |
| 56 def test_load_isolated_empty(self): | |
| 57 m = run_isolated.load_isolated('{}') | |
| 58 self.assertEqual({}, m) | |
| 59 | |
| 60 def test_load_isolated_good(self): | |
| 61 data = { | |
| 62 u'command': [u'foo', u'bar'], | |
| 63 u'files': { | |
| 64 u'a': { | |
| 65 u'l': u'somewhere', | |
| 66 u'm': 123, | |
| 67 }, | |
| 68 u'b': { | |
| 69 u'm': 123, | |
| 70 u'h': u'0123456789abcdef0123456789abcdef01234567' | |
| 71 } | |
| 72 }, | |
| 73 u'includes': [u'0123456789abcdef0123456789abcdef01234567'], | |
| 74 u'os': run_isolated.get_flavor(), | |
| 75 u'read_only': False, | |
| 76 u'relative_cwd': u'somewhere_else' | |
| 77 } | |
| 78 m = run_isolated.load_isolated(json.dumps(data)) | |
| 79 self.assertEqual(data, m) | |
| 80 | |
| 81 def test_load_isolated_bad(self): | |
| 82 data = { | |
| 83 u'files': { | |
| 84 u'a': { | |
| 85 u'l': u'somewhere', | |
| 86 u'h': u'0123456789abcdef0123456789abcdef01234567' | |
| 87 } | |
| 88 }, | |
| 89 } | |
| 90 try: | |
| 91 run_isolated.load_isolated(json.dumps(data)) | |
| 92 self.fail() | |
| 93 except run_isolated.ConfigError: | |
| 94 pass | |
| 95 | |
| 96 def test_load_isolated_os_only(self): | |
| 97 data = { | |
| 98 u'os': run_isolated.get_flavor(), | |
| 99 } | |
| 100 m = run_isolated.load_isolated(json.dumps(data)) | |
| 101 self.assertEqual(data, m) | |
| 102 | |
| 103 def test_load_isolated_os_bad(self): | |
| 104 data = { | |
| 105 u'os': 'foo', | |
| 106 } | |
| 107 try: | |
| 108 run_isolated.load_isolated(json.dumps(data)) | |
| 109 self.fail() | |
| 110 except run_isolated.ConfigError: | |
| 111 pass | |
| 112 | |
| 113 def test_remote_no_errors(self): | |
| 114 files_to_handle = 50 | |
| 115 remote = RemoteTest('') | |
| 116 | |
| 117 for i in range(files_to_handle): | |
| 118 remote.add_item( | |
| 119 run_isolated.Remote.MED, | |
| 120 'obj%d' % i, | |
| 121 'dest%d' % i, | |
| 122 run_isolated.UNKNOWN_FILE_SIZE) | |
| 123 | |
| 124 items = sorted(remote.join()) | |
| 125 expected = sorted('obj%d' % i for i in range(files_to_handle)) | |
| 126 self.assertEqual(expected, items) | |
| 127 | |
| 128 def test_remote_with_errors(self): | |
| 129 remote = RemoteTest('') | |
| 130 | |
| 131 def RaiseIOError(*_): | |
| 132 raise IOError() | |
| 133 remote._do_item = RaiseIOError | |
| 134 remote.add_item(run_isolated.Remote.MED, 'ignored', '', | |
| 135 run_isolated.UNKNOWN_FILE_SIZE) | |
| 136 self.assertRaises(IOError, remote.join) | |
| 137 self.assertEqual([], remote.join()) | |
| 138 | |
| 139 def test_zip_header_error(self): | |
| 140 self.mock(run_isolated, 'url_open', | |
| 141 lambda url, **_kwargs: fake_http_response('111', url)) | |
| 142 self.mock(run_isolated.time, 'sleep', lambda _x: None) | |
| 143 | |
| 144 remote = run_isolated.Remote('https://fake-CAD.com/') | |
| 145 | |
| 146 # Both files will fail to be unzipped due to incorrect headers, | |
| 147 # ensure that we don't accept the files (even if the size is unknown)}. | |
| 148 remote.add_item(run_isolated.Remote.MED, 'zipped_A', 'A', | |
| 149 run_isolated.UNKNOWN_FILE_SIZE) | |
| 150 remote.add_item(run_isolated.Remote.MED, 'zipped_B', 'B', 5) | |
| 151 self.assertRaises(IOError, remote.get_one_result) | |
| 152 self.assertRaises(IOError, remote.get_one_result) | |
| 153 # Need to use join here, since get_one_result will hang. | |
| 154 self.assertEqual([], remote.join()) | |
| 155 | |
| 156 | |
| 157 | |
| 158 class HttpServiceTest(auto_stub.TestCase): | 24 class HttpServiceTest(auto_stub.TestCase): |
| 159 | 25 |
| 160 # HttpService that doesn't sleep in retries and doesn't write cookie files. | 26 # HttpService that doesn't sleep in retries and doesn't write cookie files. |
| 161 class HttpServiceNoSideEffects(run_isolated.HttpService): | 27 class HttpServiceNoSideEffects(net.HttpService): |
| 162 def __init__(self, *args, **kwargs): | 28 def __init__(self, *args, **kwargs): |
| 163 super(HttpServiceTest.HttpServiceNoSideEffects, self).__init__( | 29 super(HttpServiceTest.HttpServiceNoSideEffects, self).__init__( |
| 164 *args, **kwargs) | 30 *args, **kwargs) |
| 165 self.sleeps = [] | 31 self.sleeps = [] |
| 166 self._count = 0. | 32 self._count = 0. |
| 167 | 33 |
| 168 def _now(self): # pylint: disable=W0221 | 34 def _now(self): # pylint: disable=W0221 |
| 169 x = self._count | 35 x = self._count |
| 170 self._count += 1 | 36 self._count += 1 |
| 171 return x | 37 return x |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 return StringIO.StringIO(response) | 78 return StringIO.StringIO(response) |
| 213 | 79 |
| 214 service = self.mocked_http_service(url=service_url, _url_open=mock_url_open) | 80 service = self.mocked_http_service(url=service_url, _url_open=mock_url_open) |
| 215 self.assertEqual(service.request(request_url, data={}).read(), response) | 81 self.assertEqual(service.request(request_url, data={}).read(), response) |
| 216 self.assertEqual([], service.sleeps) | 82 self.assertEqual([], service.sleeps) |
| 217 | 83 |
| 218 def test_request_success_after_failure(self): | 84 def test_request_success_after_failure(self): |
| 219 response = 'True' | 85 response = 'True' |
| 220 | 86 |
| 221 def mock_url_open(request, **_kwargs): | 87 def mock_url_open(request, **_kwargs): |
| 222 if run_isolated.COUNT_KEY + '=1' not in request.get_data(): | 88 if net.COUNT_KEY + '=1' not in request.get_data(): |
| 223 raise urllib2.URLError('url') | 89 raise urllib2.URLError('url') |
| 224 return StringIO.StringIO(response) | 90 return StringIO.StringIO(response) |
| 225 | 91 |
| 226 service = self.mocked_http_service(_url_open=mock_url_open) | 92 service = self.mocked_http_service(_url_open=mock_url_open) |
| 227 self.assertEqual(service.request('/', data={}).read(), response) | 93 self.assertEqual(service.request('/', data={}).read(), response) |
| 228 self.assertEqual( | 94 self.assertEqual( |
| 229 [(0, run_isolated.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN)], | 95 [(0, net.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN)], |
| 230 service.sleeps) | 96 service.sleeps) |
| 231 | 97 |
| 232 def test_request_failure_max_attempts_default(self): | 98 def test_request_failure_max_attempts_default(self): |
| 233 def mock_url_open(_request, **_kwargs): | 99 def mock_url_open(_request, **_kwargs): |
| 234 raise urllib2.URLError('url') | 100 raise urllib2.URLError('url') |
| 235 service = self.mocked_http_service(_url_open=mock_url_open) | 101 service = self.mocked_http_service(_url_open=mock_url_open) |
| 236 self.assertEqual(service.request('/'), None) | 102 self.assertEqual(service.request('/'), None) |
| 237 retries = [ | 103 retries = [ |
| 238 (i, run_isolated.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN * (i + 1)) | 104 (i, net.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN * (i + 1)) |
| 239 for i in xrange(run_isolated.URL_OPEN_MAX_ATTEMPTS) | 105 for i in xrange(net.URL_OPEN_MAX_ATTEMPTS) |
| 240 ] | 106 ] |
| 241 self.assertEqual(retries, service.sleeps) | 107 self.assertEqual(retries, service.sleeps) |
| 242 | 108 |
| 243 def test_request_failure_max_attempts(self): | 109 def test_request_failure_max_attempts(self): |
| 244 def mock_url_open(_request, **_kwargs): | 110 def mock_url_open(_request, **_kwargs): |
| 245 raise urllib2.URLError('url') | 111 raise urllib2.URLError('url') |
| 246 service = self.mocked_http_service(_url_open=mock_url_open) | 112 service = self.mocked_http_service(_url_open=mock_url_open) |
| 247 self.assertEqual(service.request('/', max_attempts=23), None) | 113 self.assertEqual(service.request('/', max_attempts=23), None) |
| 248 retries = [ | 114 retries = [ |
| 249 (i, run_isolated.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN * (i + 1)) | 115 (i, net.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN * (i + 1)) |
| 250 for i in xrange(23) | 116 for i in xrange(23) |
| 251 ] | 117 ] |
| 252 self.assertEqual(retries, service.sleeps) | 118 self.assertEqual(retries, service.sleeps) |
| 253 | 119 |
| 254 def test_request_failure_timeout(self): | 120 def test_request_failure_timeout(self): |
| 255 def mock_url_open(_request, **_kwargs): | 121 def mock_url_open(_request, **_kwargs): |
| 256 raise urllib2.URLError('url') | 122 raise urllib2.URLError('url') |
| 257 service = self.mocked_http_service(_url_open=mock_url_open) | 123 service = self.mocked_http_service(_url_open=mock_url_open) |
| 258 self.assertEqual(service.request('/', max_attempts=10000), None) | 124 self.assertEqual(service.request('/', max_attempts=10000), None) |
| 259 retries = [ | 125 retries = [ |
| 260 (i, run_isolated.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN * (i + 1)) | 126 (i, net.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN * (i + 1)) |
| 261 # Currently 179 for timeout == 360. | 127 # Currently 179 for timeout == 360. |
| 262 for i in xrange( | 128 for i in xrange( |
| 263 int(run_isolated.URL_OPEN_TIMEOUT) / NOW_CALLS_PER_OPEN - 1) | 129 int(net.URL_OPEN_TIMEOUT) / NOW_CALLS_PER_OPEN - 1) |
| 264 ] | 130 ] |
| 265 self.assertEqual(retries, service.sleeps) | 131 self.assertEqual(retries, service.sleeps) |
| 266 | 132 |
| 267 def test_request_failure_timeout_default(self): | 133 def test_request_failure_timeout_default(self): |
| 268 def mock_url_open(_request, **_kwargs): | 134 def mock_url_open(_request, **_kwargs): |
| 269 raise urllib2.URLError('url') | 135 raise urllib2.URLError('url') |
| 270 service = self.mocked_http_service(_url_open=mock_url_open) | 136 service = self.mocked_http_service(_url_open=mock_url_open) |
| 271 self.assertEqual(service.request('/', timeout=10.), None) | 137 self.assertEqual(service.request('/', timeout=10.), None) |
| 272 retries = [(i, 8. - (NOW_CALLS_PER_OPEN * i)) for i in xrange(4)] | 138 retries = [(i, 8. - (NOW_CALLS_PER_OPEN * i)) for i in xrange(4)] |
| 273 self.assertEqual(retries, service.sleeps) | 139 self.assertEqual(retries, service.sleeps) |
| 274 | 140 |
| 275 def test_request_HTTP_error_no_retry(self): | 141 def test_request_HTTP_error_no_retry(self): |
| 276 count = [] | 142 count = [] |
| 277 def mock_url_open(request, **_kwargs): | 143 def mock_url_open(request, **_kwargs): |
| 278 count.append(request) | 144 count.append(request) |
| 279 raise urllib2.HTTPError( | 145 raise urllib2.HTTPError( |
| 280 'url', 400, 'error message', None, StringIO.StringIO()) | 146 'url', 400, 'error message', None, StringIO.StringIO()) |
| 281 | 147 |
| 282 service = self.mocked_http_service(_url_open=mock_url_open) | 148 service = self.mocked_http_service(_url_open=mock_url_open) |
| 283 self.assertEqual(service.request('/', data={}), None) | 149 self.assertEqual(service.request('/', data={}), None) |
| 284 self.assertEqual(1, len(count)) | 150 self.assertEqual(1, len(count)) |
| 285 self.assertEqual([], service.sleeps) | 151 self.assertEqual([], service.sleeps) |
| 286 | 152 |
| 287 def test_request_HTTP_error_retry_404(self): | 153 def test_request_HTTP_error_retry_404(self): |
| 288 response = 'data' | 154 response = 'data' |
| 289 def mock_url_open(request, **_kwargs): | 155 def mock_url_open(request, **_kwargs): |
| 290 if run_isolated.COUNT_KEY + '=1' in request.get_data(): | 156 if net.COUNT_KEY + '=1' in request.get_data(): |
| 291 return StringIO.StringIO(response) | 157 return StringIO.StringIO(response) |
| 292 raise urllib2.HTTPError( | 158 raise urllib2.HTTPError( |
| 293 'url', 404, 'error message', None, StringIO.StringIO()) | 159 'url', 404, 'error message', None, StringIO.StringIO()) |
| 294 | 160 |
| 295 service = self.mocked_http_service(_url_open=mock_url_open) | 161 service = self.mocked_http_service(_url_open=mock_url_open) |
| 296 result = service.request('/', data={}, retry_404=True) | 162 result = service.request('/', data={}, retry_404=True) |
| 297 self.assertEqual(result.read(), response) | 163 self.assertEqual(result.read(), response) |
| 298 self.assertEqual( | 164 self.assertEqual( |
| 299 [(0, run_isolated.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN)], | 165 [(0, net.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN)], |
| 300 service.sleeps) | 166 service.sleeps) |
| 301 | 167 |
| 302 def test_request_HTTP_error_with_retry(self): | 168 def test_request_HTTP_error_with_retry(self): |
| 303 response = 'response' | 169 response = 'response' |
| 304 | 170 |
| 305 def mock_url_open(request, **_kwargs): | 171 def mock_url_open(request, **_kwargs): |
| 306 if run_isolated.COUNT_KEY + '=1' not in request.get_data(): | 172 if net.COUNT_KEY + '=1' not in request.get_data(): |
| 307 raise urllib2.HTTPError( | 173 raise urllib2.HTTPError( |
| 308 'url', 500, 'error message', None, StringIO.StringIO()) | 174 'url', 500, 'error message', None, StringIO.StringIO()) |
| 309 return StringIO.StringIO(response) | 175 return StringIO.StringIO(response) |
| 310 | 176 |
| 311 service = self.mocked_http_service(_url_open=mock_url_open) | 177 service = self.mocked_http_service(_url_open=mock_url_open) |
| 312 self.assertTrue(service.request('/', data={}).read(), response) | 178 self.assertTrue(service.request('/', data={}).read(), response) |
| 313 self.assertEqual( | 179 self.assertEqual( |
| 314 [(0, run_isolated.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN)], | 180 [(0, net.URL_OPEN_TIMEOUT - NOW_CALLS_PER_OPEN)], |
| 315 service.sleeps) | 181 service.sleeps) |
| 316 | 182 |
| 317 def test_count_key_in_data_failure(self): | 183 def test_count_key_in_data_failure(self): |
| 318 data = {run_isolated.COUNT_KEY: 1} | 184 data = {net.COUNT_KEY: 1} |
| 319 service = self.mocked_http_service() | 185 service = self.mocked_http_service() |
| 320 self.assertEqual(service.request('/', data=data), None) | 186 self.assertEqual(service.request('/', data=data), None) |
| 321 self.assertEqual([], service.sleeps) | 187 self.assertEqual([], service.sleeps) |
| 322 | 188 |
| 323 def test_auth_success(self): | 189 def test_auth_success(self): |
| 324 count = [] | 190 count = [] |
| 325 response = 'response' | 191 response = 'response' |
| 326 def mock_url_open(_request, **_kwargs): | 192 def mock_url_open(_request, **_kwargs): |
| 327 if not count: | 193 if not count: |
| 328 raise urllib2.HTTPError( | 194 raise urllib2.HTTPError( |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 service = self.mocked_http_service(_url_open=mock_url_open, | 230 service = self.mocked_http_service(_url_open=mock_url_open, |
| 365 authenticate=mock_authenticate) | 231 authenticate=mock_authenticate) |
| 366 self.assertEqual(service.request('/'), None) | 232 self.assertEqual(service.request('/'), None) |
| 367 self.assertEqual(calls, ['url_open', 'authenticate']) | 233 self.assertEqual(calls, ['url_open', 'authenticate']) |
| 368 self.assertEqual([], service.sleeps) | 234 self.assertEqual([], service.sleeps) |
| 369 | 235 |
| 370 def test_sleep_before_retry(self): | 236 def test_sleep_before_retry(self): |
| 371 # Verifies bounds. Because it's using a pseudo-random number generator and | 237 # Verifies bounds. Because it's using a pseudo-random number generator and |
| 372 # not a read random source, it's basically guaranteed to never return the | 238 # not a read random source, it's basically guaranteed to never return the |
| 373 # same value twice consecutively. | 239 # same value twice consecutively. |
| 374 a = run_isolated.HttpService.calculate_sleep_before_retry(0, 0) | 240 a = net.HttpService.calculate_sleep_before_retry(0, 0) |
| 375 b = run_isolated.HttpService.calculate_sleep_before_retry(0, 0) | 241 b = net.HttpService.calculate_sleep_before_retry(0, 0) |
| 376 self.assertTrue(a >= math.pow(1.5, -1), a) | 242 self.assertTrue(a >= math.pow(1.5, -1), a) |
| 377 self.assertTrue(b >= math.pow(1.5, -1), b) | 243 self.assertTrue(b >= math.pow(1.5, -1), b) |
| 378 self.assertTrue(a < 1.5 + math.pow(1.5, -1), a) | 244 self.assertTrue(a < 1.5 + math.pow(1.5, -1), a) |
| 379 self.assertTrue(b < 1.5 + math.pow(1.5, -1), b) | 245 self.assertTrue(b < 1.5 + math.pow(1.5, -1), b) |
| 380 self.assertNotEqual(a, b) | 246 self.assertNotEqual(a, b) |
| 381 | 247 |
| 382 def test_url_read(self): | 248 def test_url_read(self): |
| 383 # Successfully reads the data. | 249 # Successfully reads the data. |
| 384 self.mock(run_isolated, 'url_open', | 250 self.mock(net, 'url_open', |
| 385 lambda url, **_kwargs: fake_http_response('111', url)) | 251 lambda url, **_kwargs: net.HttpResponse.get_fake_response('111', url)) |
| 386 self.assertEqual(run_isolated.url_read('https://fake_url.com/test'), '111') | 252 self.assertEqual(net.url_read('https://fake_url.com/test'), '111') |
| 387 | 253 |
| 388 # Respects url_open connection errors. | 254 # Respects url_open connection errors. |
| 389 self.mock(run_isolated, 'url_open', lambda _url, **_kwargs: None) | 255 self.mock(net, 'url_open', lambda _url, **_kwargs: None) |
| 390 self.assertIsNone(run_isolated.url_read('https://fake_url.com/test')) | 256 self.assertIsNone(net.url_read('https://fake_url.com/test')) |
| 391 | 257 |
| 392 # Respects read timeout errors. | 258 # Respects read timeout errors. |
| 393 def timeouting_http_response(url): | 259 def timeouting_http_response(url): |
| 394 def read_mock(_size=None): | 260 def read_mock(_size=None): |
| 395 raise run_isolated.TimeoutError() | 261 raise net.TimeoutError() |
| 396 stream = StringIO.StringIO('') | 262 response = net.HttpResponse(StringIO.StringIO(''), |
| 397 stream.headers = {'content-length': 0} | 263 url, {'content-length': 0}) |
| 398 response = run_isolated.HttpResponse(stream, url) | 264 self.mock(response, 'read', read_mock) |
| 399 response.read = read_mock | |
| 400 return response | 265 return response |
| 401 | 266 |
| 402 self.mock(run_isolated, 'url_open', | 267 self.mock(net, 'url_open', |
| 403 lambda url, **_kwargs: timeouting_http_response(url)) | 268 lambda url, **_kwargs: timeouting_http_response(url)) |
| 404 self.assertIsNone(run_isolated.url_read('https://fake_url.com/test')) | 269 self.assertIsNone(net.url_read('https://fake_url.com/test')) |
| 405 | 270 |
| 406 | 271 |
| 407 if __name__ == '__main__': | 272 if __name__ == '__main__': |
| 408 logging.basicConfig( | 273 logging.basicConfig( |
| 409 level=(logging.DEBUG if '-v' in sys.argv else logging.FATAL)) | 274 level=(logging.DEBUG if '-v' in sys.argv else logging.FATAL)) |
| 410 if '-v' in sys.argv: | 275 if '-v' in sys.argv: |
| 411 unittest.TestCase.maxDiff = None | 276 unittest.TestCase.maxDiff = None |
| 412 unittest.main() | 277 unittest.main() |
| OLD | NEW |