| OLD | NEW | 
|---|
| 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 base64 | 6 import base64 | 
| 7 import io | 7 import io | 
| 8 import json | 8 import json | 
| 9 import os | 9 import os | 
| 10 import sys | 10 import sys | 
| 11 import unittest | 11 import unittest | 
|  | 12 import time | 
| 12 | 13 | 
| 13 import test_env | 14 import test_env | 
| 14 | 15 | 
| 15 import mock | 16 import mock | 
| 16 import subprocess42 | 17 import subprocess42 | 
| 17 | 18 | 
| 18 from recipe_engine import fetch | 19 from recipe_engine import fetch | 
| 19 from recipe_engine import requests_ssl | 20 from recipe_engine import requests_ssl | 
| 20 | 21 | 
| 21 | 22 | 
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 151   @mock.patch('os.makedirs') | 152   @mock.patch('os.makedirs') | 
| 152   @mock.patch('tarfile.open') | 153   @mock.patch('tarfile.open') | 
| 153   @mock.patch('requests.get') | 154   @mock.patch('requests.get') | 
| 154   def test_checkout(self, requests_get, tarfile_open, makedirs, rmtree): | 155   def test_checkout(self, requests_get, tarfile_open, makedirs, rmtree): | 
| 155     proto_text = u""" | 156     proto_text = u""" | 
| 156 api_version: 1 | 157 api_version: 1 | 
| 157 project_id: "foo" | 158 project_id: "foo" | 
| 158 recipes_path: "path/to/recipes" | 159 recipes_path: "path/to/recipes" | 
| 159 """.lstrip() | 160 """.lstrip() | 
| 160     requests_get.side_effect = [ | 161     requests_get.side_effect = [ | 
| 161         mock.Mock(text=u')]}\'\n{ "commit": "abc123" }'), | 162         mock.Mock(text=u')]}\'\n{ "commit": "abc123" }', status_code=200), | 
| 162         mock.Mock(text=base64.b64encode(proto_text)), | 163         mock.Mock(text=base64.b64encode(proto_text), status_code=200), | 
| 163         mock.Mock(content=''), | 164         mock.Mock(content='', status_code=200), | 
| 164     ] | 165     ] | 
| 165 | 166 | 
| 166     fetch.GitilesBackend().checkout( | 167     fetch.GitilesBackend().checkout( | 
| 167         'repo', 'revision', 'dir', allow_fetch=True) | 168         'repo', 'revision', 'dir', allow_fetch=True) | 
| 168 | 169 | 
| 169     requests_get.assert_has_calls([ | 170     requests_get.assert_has_calls([ | 
| 170         mock.call('repo/+/revision?format=JSON'), | 171         mock.call('repo/+/revision?format=JSON'), | 
| 171         mock.call('repo/+/abc123/infra/config/recipes.cfg?format=TEXT'), | 172         mock.call('repo/+/abc123/infra/config/recipes.cfg?format=TEXT'), | 
| 172         mock.call('repo/+archive/abc123/path/to/recipes.tar.gz'), | 173         mock.call('repo/+archive/abc123/path/to/recipes.tar.gz'), | 
| 173     ]) | 174     ]) | 
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 210                     }, | 211                     }, | 
| 211                     { | 212                     { | 
| 212                         'old_path': 'path2/foo', | 213                         'old_path': 'path2/foo', | 
| 213                         'new_path': '/dev/null', | 214                         'new_path': '/dev/null', | 
| 214                     }, | 215                     }, | 
| 215                 ], | 216                 ], | 
| 216             }, | 217             }, | 
| 217         ], | 218         ], | 
| 218     } | 219     } | 
| 219     requests_get.side_effect = [ | 220     requests_get.side_effect = [ | 
| 220         mock.Mock(text=u')]}\'\n{ "commit": "sha_a" }'), | 221         mock.Mock(text=u')]}\'\n{ "commit": "sha_a" }', status_code=200), | 
| 221         mock.Mock(text=u')]}\'\n{ "commit": "sha_b" }'), | 222         mock.Mock(text=u')]}\'\n{ "commit": "sha_b" }', status_code=200), | 
| 222         mock.Mock(text=u')]}\'\n%s' % json.dumps(log_json)), | 223         mock.Mock(text=u')]}\'\n%s' % json.dumps(log_json), status_code=200), | 
| 223     ] | 224     ] | 
| 224 | 225 | 
| 225     self.assertEqual( | 226     self.assertEqual( | 
| 226         ['ghi789', 'abc123'], | 227         ['ghi789', 'abc123'], | 
| 227         fetch.GitilesBackend().updates( | 228         fetch.GitilesBackend().updates( | 
| 228             'repo', 'reva', 'dir', True, 'revb', | 229             'repo', 'reva', 'dir', True, 'revb', | 
| 229             ['path1', 'path2'])) | 230             ['path1', 'path2'])) | 
| 230 | 231 | 
| 231     requests_get.assert_has_calls([ | 232     requests_get.assert_has_calls([ | 
| 232         mock.call('repo/+/reva?format=JSON'), | 233         mock.call('repo/+/reva?format=JSON'), | 
| 233         mock.call('repo/+/revb?format=JSON'), | 234         mock.call('repo/+/revb?format=JSON'), | 
| 234         mock.call('repo/+log/sha_a..sha_b?name-status=1&format=JSON'), | 235         mock.call('repo/+log/sha_a..sha_b?name-status=1&format=JSON'), | 
| 235     ]) | 236     ]) | 
| 236 | 237 | 
| 237   @mock.patch('requests.get') | 238   @mock.patch('requests.get') | 
| 238   def test_commit_metadata(self, requests_get): | 239   def test_commit_metadata(self, requests_get): | 
| 239     revision_json = { | 240     revision_json = { | 
| 240       'author': {'email': 'author'}, | 241       'author': {'email': 'author'}, | 
| 241       'message': 'message', | 242       'message': 'message', | 
| 242     } | 243     } | 
| 243     requests_get.side_effect = [ | 244     requests_get.side_effect = [ | 
| 244         mock.Mock(text=u')]}\'\n%s' % json.dumps(revision_json)), | 245         mock.Mock(text=u')]}\'\n%s' % json.dumps(revision_json), | 
|  | 246                   status_code=200), | 
| 245     ] | 247     ] | 
| 246     result = fetch.GitilesBackend().commit_metadata( | 248     result = fetch.GitilesBackend().commit_metadata( | 
| 247         'repo', 'revision', 'dir', allow_fetch=True) | 249         'repo', 'revision', 'dir', allow_fetch=True) | 
| 248     self.assertEqual(result, { | 250     self.assertEqual(result, { | 
| 249       'author': 'author', | 251       'author': 'author', | 
| 250       'message': 'message', | 252       'message': 'message', | 
| 251     }) | 253     }) | 
| 252     requests_get.assert_has_calls([ | 254     requests_get.assert_has_calls([ | 
| 253       mock.call('repo/+/revision?format=JSON'), | 255       mock.call('repo/+/revision?format=JSON'), | 
| 254     ]) | 256     ]) | 
| 255 | 257 | 
|  | 258   @mock.patch('requests.get') | 
|  | 259   def test_non_transient_error(self, requests_get): | 
|  | 260     requests_get.side_effect = [ | 
|  | 261         mock.Mock(text='Not permitted.', status_code=403), | 
|  | 262     ] | 
|  | 263     with self.assertRaises(fetch.GitilesFetchError): | 
|  | 264       fetch.GitilesBackend().commit_metadata( | 
|  | 265           'repo', 'revision', 'dir', allow_fetch=True) | 
|  | 266     requests_get.assert_has_calls([ | 
|  | 267       mock.call('repo/+/revision?format=JSON'), | 
|  | 268     ]) | 
|  | 269 | 
|  | 270   @mock.patch('requests.get') | 
|  | 271   @mock.patch('time.sleep') | 
|  | 272   @mock.patch('logging.exception') | 
|  | 273   def test_transient_retry(self, logging_exception, time_sleep, requests_get): | 
|  | 274     counts = { | 
|  | 275         'sleeps': 0, | 
|  | 276         'fails': 0, | 
|  | 277     } | 
|  | 278 | 
|  | 279     def count_sleep(delay): | 
|  | 280       counts['sleeps'] += 1 | 
|  | 281     time_sleep.side_effect = count_sleep | 
|  | 282 | 
|  | 283     revision_json = { | 
|  | 284       'author': {'email': 'author'}, | 
|  | 285       'message': 'message', | 
|  | 286     } | 
|  | 287 | 
|  | 288     # Fail transiently 4 times, but succeed on the 5th. | 
|  | 289     def transient_side_effect(*args, **kwargs): | 
|  | 290       if counts['fails'] < 4: | 
|  | 291         counts['fails'] += 1 | 
|  | 292         return mock.Mock(text=u'Not permitted (%(fails)d).' % counts, | 
|  | 293                          status_code=500) | 
|  | 294       return mock.Mock(text=u')]}\'\n%s' % json.dumps(revision_json), | 
|  | 295                        status_code=200) | 
|  | 296     requests_get.side_effect = transient_side_effect | 
|  | 297 | 
|  | 298     result = fetch.GitilesBackend().commit_metadata( | 
|  | 299         'repo', 'revision', 'dir', allow_fetch=True) | 
|  | 300     self.assertEqual(result, { | 
|  | 301       'author': 'author', | 
|  | 302       'message': 'message', | 
|  | 303     }) | 
|  | 304     self.assertEqual(counts['sleeps'], 4) | 
|  | 305     requests_get.assert_has_calls([ | 
|  | 306       mock.call('repo/+/revision?format=JSON'), | 
|  | 307     ] * 5) | 
| 256 | 308 | 
| 257 if __name__ == '__main__': | 309 if __name__ == '__main__': | 
| 258   unittest.main() | 310   unittest.main() | 
| OLD | NEW | 
|---|