| OLD | NEW |
| (Empty) |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import base64 | |
| 6 from datetime import datetime | |
| 7 import json | |
| 8 import re | |
| 9 | |
| 10 from testing_utils import testing | |
| 11 | |
| 12 from gae_libs.testcase import TestCase | |
| 13 from lib.gitiles import gitiles_repository | |
| 14 from lib.gitiles.change_log import ChangeLog | |
| 15 from libs.http import retry_http_client | |
| 16 | |
| 17 | |
| 18 COMMIT_MESSAGE = ('Add popover for snapshot canvas log.\n\n' | |
| 19 'Review URL: https://codereview.chromium.org/320423004\n\n' | |
| 20 'Review URL: https://codereview.chromium.org/328113005\n\n' | |
| 21 'Cr-Commit-Position: refs/heads/master@{#175976}') | |
| 22 | |
| 23 COMMIT_LOG = """)]}' | |
| 24 { | |
| 25 "commit": "bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb", | |
| 26 "tree": "481fd0f3bdf6eda5ca29ec6cbc6aa476b3684143", | |
| 27 "parents": [ | |
| 28 "42a94bb5e2ef8525d7dadbd8eae37fe7cb8d77d0" | |
| 29 ], | |
| 30 "author": { | |
| 31 "name": "test1@chromium.org", | |
| 32 "email": "test1@chromium.org@bbb929c8-8fbe-4397-9dbb-9b2b20218538", | |
| 33 "time": "Wed Jun 11 19:35:32 2014 -0400" | |
| 34 }, | |
| 35 "committer": { | |
| 36 "name": "test1@chromium.org", | |
| 37 "email": "test1@chromium.org", | |
| 38 "time": "Wed Jun 11 19:35:32 2014" | |
| 39 }, | |
| 40 "message": %s, | |
| 41 "tree_diff": [ | |
| 42 { | |
| 43 "type": "add", | |
| 44 "old_id": "f71f1167c2204626057d26912b8a2ff096fe4bd2", | |
| 45 "old_mode": 33188, | |
| 46 "old_path": "/dev/null", | |
| 47 "new_id": "165fb11e0658f41d66038199056a53bcfab5dda0", | |
| 48 "new_mode": 33188, | |
| 49 "new_path": "Source/devtools/front_end/layers/added_file.js" | |
| 50 }, | |
| 51 { | |
| 52 "type": "delete", | |
| 53 "old_id": "f71f1167c2204626057d26912b8a2ff096fe4bd3", | |
| 54 "old_mode": 33188, | |
| 55 "old_path": "Source/devtools/front_end/layers/deleted_file.js", | |
| 56 "new_id": "165fb11e0658f41d66038199056a53bcfab5dda1", | |
| 57 "new_mode": 33188, | |
| 58 "new_path": "/dev/null" | |
| 59 }, | |
| 60 { | |
| 61 "type": "modify", | |
| 62 "old_id": "f71f1167c2204626057d26912b8a2ff096fe4bd1", | |
| 63 "old_mode": 33188, | |
| 64 "old_path": "Source/devtools/front_end/layers/modified_file.js", | |
| 65 "new_id": "165fb11e0658f41d66038199056a53bcfab5dda9", | |
| 66 "new_mode": 33188, | |
| 67 "new_path": "Source/devtools/front_end/layers/modified_file.js" | |
| 68 }, | |
| 69 { | |
| 70 "type": "copy", | |
| 71 "old_id": "f71f1167c2204626057d26912b8a2ff096fe4bd4", | |
| 72 "old_mode": 33188, | |
| 73 "old_path": "Source/devtools/front_end/layers/file.js", | |
| 74 "new_id": "165fb11e0658f41d66038199056a53bcfab5dda2", | |
| 75 "new_mode": 33188, | |
| 76 "new_path": "Source/devtools/front_end/layers/copied_file.js" | |
| 77 }, | |
| 78 { | |
| 79 "type": "rename", | |
| 80 "old_id": "f71f1167c2204626057d26912b8a2ff096fe4bd5", | |
| 81 "old_mode": 33188, | |
| 82 "old_path": "Source/devtools/front_end/layers/file.js", | |
| 83 "new_id": "165fb11e0658f41d66038199056a53bcfab5dda3", | |
| 84 "new_mode": 33188, | |
| 85 "new_path": "Source/devtools/front_end/layers/renamed_file.js" | |
| 86 } | |
| 87 ] | |
| 88 }""" % json.JSONEncoder().encode(COMMIT_MESSAGE) | |
| 89 | |
| 90 EXPECTED_CHANGE_LOG_JSON = { | |
| 91 'author_name': 'test1@chromium.org', | |
| 92 'message': COMMIT_MESSAGE, | |
| 93 'committer_email': 'test1@chromium.org', | |
| 94 'commit_position': 175976, | |
| 95 'author_email': 'test1@chromium.org', | |
| 96 'touched_files': [ | |
| 97 { | |
| 98 'change_type': 'add', | |
| 99 'new_path': 'Source/devtools/front_end/layers/added_file.js', | |
| 100 'old_path': '/dev/null' | |
| 101 }, | |
| 102 { | |
| 103 'change_type': 'delete', | |
| 104 'new_path': '/dev/null', | |
| 105 'old_path': 'Source/devtools/front_end/layers/deleted_file.js' | |
| 106 }, | |
| 107 { | |
| 108 'change_type': 'modify', | |
| 109 'new_path': 'Source/devtools/front_end/layers/modified_file.js', | |
| 110 'old_path': 'Source/devtools/front_end/layers/modified_file.js' | |
| 111 }, | |
| 112 { | |
| 113 'change_type': 'copy', | |
| 114 'new_path': 'Source/devtools/front_end/layers/copied_file.js', | |
| 115 'old_path': 'Source/devtools/front_end/layers/file.js' | |
| 116 }, | |
| 117 { | |
| 118 'change_type': 'rename', | |
| 119 'new_path': 'Source/devtools/front_end/layers/renamed_file.js', | |
| 120 'old_path': 'Source/devtools/front_end/layers/file.js' | |
| 121 } | |
| 122 ], | |
| 123 'author_time': datetime(2014, 06, 11, 23, 35, 32), | |
| 124 'committer_time': datetime(2014, 06, 11, 19, 35, 32), | |
| 125 'commit_url': | |
| 126 'https://repo.test/+/bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb', | |
| 127 'code_review_url': 'https://codereview.chromium.org/328113005', | |
| 128 'committer_name': 'test1@chromium.org', | |
| 129 'revision': 'bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb', | |
| 130 'reverted_revision': None | |
| 131 } | |
| 132 | |
| 133 COMMIT_LOG_WITH_UNKNOWN_FILE_CHANGE_TYPE = """)]}' | |
| 134 { | |
| 135 "commit": "bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb", | |
| 136 "tree": "481fd0f3bdf6eda5ca29ec6cbc6aa476b3684143", | |
| 137 "parents": [ | |
| 138 "42a94bb5e2ef8525d7dadbd8eae37fe7cb8d77d0" | |
| 139 ], | |
| 140 "author": { | |
| 141 "name": "test1@chromium.org", | |
| 142 "email": "test1@chromium.org@bbb929c8-8fbe-4397-9dbb-9b2b20218538", | |
| 143 "time": "Wed Jun 11 19:35:32 2014" | |
| 144 }, | |
| 145 "committer": { | |
| 146 "name": "test1@chromium.org", | |
| 147 "email": "test1@chromium.org@bbb929c8-8fbe-4397-9dbb-9b2b20218538", | |
| 148 "time": "Wed Jun 11 19:35:32 2014" | |
| 149 }, | |
| 150 "message": "message", | |
| 151 "tree_diff": [ | |
| 152 { | |
| 153 "type": "unknown_change_type", | |
| 154 "old_id": "f71f1167c2204626057d26912b8a2ff096fe4bd2", | |
| 155 "old_mode": 33188, | |
| 156 "old_path": "/dev/null", | |
| 157 "new_id": "165fb11e0658f41d66038199056a53bcfab5dda0", | |
| 158 "new_mode": 33188, | |
| 159 "new_path": "Source/devtools/front_end/layers/added_file.js" | |
| 160 } | |
| 161 ] | |
| 162 }""" | |
| 163 | |
| 164 GITILES_FILE_BLAME_RESULT = """)]}' | |
| 165 { | |
| 166 "regions": [ | |
| 167 { | |
| 168 "start": 1, | |
| 169 "count": 6, | |
| 170 "path": "chrome/test/chromedriver/element_commands.cc", | |
| 171 "commit": "584ae1f26b070150f65a03dba75fc8af6b6f6ece", | |
| 172 "author": { | |
| 173 "name": "test2@chromium.org", | |
| 174 "email": "test2@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98", | |
| 175 "time": "2013-02-11 20:18:51" | |
| 176 } | |
| 177 }, | |
| 178 { | |
| 179 "start": 7, | |
| 180 "count": 1, | |
| 181 "path": "chrome/test/chromedriver/element_commands.cc", | |
| 182 "commit": "030b5d9bb7d6c9f673cd8f0c86d8f1e921de7076", | |
| 183 "author": { | |
| 184 "name": "test3@chromium.org", | |
| 185 "email": "test3@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98", | |
| 186 "time": "2014-02-06 10:02:10 +0400" | |
| 187 } | |
| 188 }, | |
| 189 { | |
| 190 "start": 8, | |
| 191 "count": 1, | |
| 192 "path": "chrome/test/chromedriver/element_commands.cc", | |
| 193 "commit": "584ae1f26b070150f65a03dba75fc8af6b6f6ece", | |
| 194 "author": { | |
| 195 "name": "test2@chromium.org", | |
| 196 "email": "test2@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98", | |
| 197 "time": "2013-02-11 20:18:51" | |
| 198 } | |
| 199 } | |
| 200 ] | |
| 201 }""" | |
| 202 | |
| 203 EXPECTED_FILE_BLAME_JSON = { | |
| 204 'regions': [ | |
| 205 { | |
| 206 'count': 6, | |
| 207 'author_email': u'test2@chromium.org', | |
| 208 'author_time': datetime(2013, 02, 11, 20, 18, 51), | |
| 209 'author_name': u'test2@chromium.org', | |
| 210 'start': 1, | |
| 211 'revision': u'584ae1f26b070150f65a03dba75fc8af6b6f6ece' | |
| 212 }, | |
| 213 { | |
| 214 'count': 1, | |
| 215 'author_email': u'test3@chromium.org', | |
| 216 'author_time': datetime(2014, 02, 06, 06, 02, 10), | |
| 217 'author_name': u'test3@chromium.org', | |
| 218 'start': 7, | |
| 219 'revision': u'030b5d9bb7d6c9f673cd8f0c86d8f1e921de7076' | |
| 220 }, | |
| 221 { | |
| 222 'count': 1, | |
| 223 'author_email': u'test2@chromium.org', | |
| 224 'author_time': datetime(2013, 02, 11, 20, 18, 51), | |
| 225 'author_name': u'test2@chromium.org', | |
| 226 'start': 8, | |
| 227 'revision': u'584ae1f26b070150f65a03dba75fc8af6b6f6ece' | |
| 228 } | |
| 229 ], | |
| 230 'path': 'a/b/c.cc', | |
| 231 'revision': 'dummy_abcd1234' | |
| 232 } | |
| 233 | |
| 234 DUMMY_CHANGELOG_JSON = { | |
| 235 'author_name': 'test@chromium.org', | |
| 236 'message': 'dummy', | |
| 237 'committer_email': 'test@chromium.org', | |
| 238 'commit_position': 175976, | |
| 239 'author_email': 'test1@chromium.org', | |
| 240 'touched_files': [ | |
| 241 { | |
| 242 'change_type': 'add', | |
| 243 'new_path': 'Source/devtools/added_file.js', | |
| 244 'old_path': '/dev/null' | |
| 245 } | |
| 246 ], | |
| 247 'author_time': datetime(2016, 01, 11, 23, 35, 32), | |
| 248 'committer_time': datetime(2016, 01, 11, 19, 35, 32), | |
| 249 'commit_url': | |
| 250 'https://repo.test/+/bcfd', | |
| 251 'code_review_url': 'https://codereview.chromium.org/328113005', | |
| 252 'committer_name': 'test1@chromium.org', | |
| 253 'revision': 'bcfd', | |
| 254 'reverted_revision': None | |
| 255 } | |
| 256 | |
| 257 | |
| 258 class GitRepositoryTest(TestCase): | |
| 259 | |
| 260 def setUp(self): | |
| 261 super(GitRepositoryTest, self).setUp() | |
| 262 self.http_client_for_git = self.GetMockHttpClient() | |
| 263 self.repo_url = 'https://repo.test' | |
| 264 self.git_repo = gitiles_repository.GitilesRepository( | |
| 265 self.http_client_for_git, self.repo_url) | |
| 266 | |
| 267 def testGitRepositoryPropertySetters(self): | |
| 268 git_repo = gitiles_repository.GitilesRepository(self.http_client_for_git) | |
| 269 git_repo.repo_url = 'https://repo' | |
| 270 self.assertEqual(git_repo.repo_url, 'https://repo') | |
| 271 self.assertEqual(git_repo.http_client, self.http_client_for_git) | |
| 272 self.assertEqual(git_repo.identifier, 'https://repo') | |
| 273 | |
| 274 def testEndingSlashInRepoUrl(self): | |
| 275 git_repo1 = gitiles_repository.GitilesRepository( | |
| 276 self.http_client_for_git, self.repo_url) | |
| 277 self.assertEqual(self.repo_url, git_repo1.repo_url) | |
| 278 | |
| 279 git_repo2 = gitiles_repository.GitilesRepository( | |
| 280 self.http_client_for_git, '%s/' % self.repo_url) | |
| 281 self.assertEqual(self.repo_url, git_repo2.repo_url) | |
| 282 | |
| 283 def testMalformattedJsonReponse(self): | |
| 284 self.http_client_for_git.SetResponseForUrl( | |
| 285 '%s/+/%s?format=json' % (self.repo_url, 'aaa'), 'abcde{"a": 1}') | |
| 286 self.assertRaisesRegexp( | |
| 287 Exception, re.escape('Response does not begin with )]}\'\n'), | |
| 288 self.git_repo.GetChangeLog, 'aaa') | |
| 289 | |
| 290 def testGetChangeLog(self): | |
| 291 self.http_client_for_git.SetResponseForUrl( | |
| 292 '%s/+/%s?format=json' % ( | |
| 293 self.repo_url, 'bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb'), | |
| 294 COMMIT_LOG) | |
| 295 | |
| 296 self.assertIsNone(self.git_repo.GetChangeLog('not_existing_revision')) | |
| 297 | |
| 298 change_log = self.git_repo.GetChangeLog( | |
| 299 'bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb') | |
| 300 self.assertEqual(EXPECTED_CHANGE_LOG_JSON, change_log.ToDict()) | |
| 301 | |
| 302 def testUnknownChangeType(self): | |
| 303 self.http_client_for_git.SetResponseForUrl( | |
| 304 '%s/+/%s?format=json' % ( | |
| 305 self.repo_url, 'bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb'), | |
| 306 COMMIT_LOG_WITH_UNKNOWN_FILE_CHANGE_TYPE) | |
| 307 self.assertRaisesRegexp( | |
| 308 Exception, 'Unknown change type "unknown_change_type"', | |
| 309 self.git_repo.GetChangeLog, 'bcfd5a12eea05588aee98b7cf7e032d8cb5b58bb') | |
| 310 | |
| 311 def testGetChangeDiff(self): | |
| 312 self.assertIsNone(self.git_repo.GetChangeDiff('not_existing_revision')) | |
| 313 | |
| 314 git_revision = 'dummy_abcd1234' | |
| 315 original_diff = 'dummy diff' | |
| 316 self.http_client_for_git.SetResponseForUrl( | |
| 317 '%s/+/%s%%5E%%21/?format=text' % (self.repo_url, git_revision), | |
| 318 base64.b64encode(original_diff)) | |
| 319 diff = self.git_repo.GetChangeDiff(git_revision) | |
| 320 self.assertEqual(original_diff, diff) | |
| 321 | |
| 322 def testGetBlame(self): | |
| 323 self.assertIsNone(self.git_repo.GetBlame('path', 'not_existing_revision')) | |
| 324 | |
| 325 path = 'a/b/c.cc' | |
| 326 git_revision = 'dummy_abcd1234' | |
| 327 self.http_client_for_git.SetResponseForUrl( | |
| 328 '%s/+blame/%s/%s?format=json' % (self.repo_url, git_revision, path), | |
| 329 GITILES_FILE_BLAME_RESULT) | |
| 330 | |
| 331 blame = self.git_repo.GetBlame(path, git_revision) | |
| 332 self.assertEqual(EXPECTED_FILE_BLAME_JSON, blame.ToDict()) | |
| 333 | |
| 334 def testGetSource(self): | |
| 335 self.assertIsNone(self.git_repo.GetSource('path', 'not_existing_revision')) | |
| 336 | |
| 337 path = 'a/b/c.cc' | |
| 338 git_revision = 'dummy_abcd1234' | |
| 339 original_source = 'dummy source' | |
| 340 self.http_client_for_git.SetResponseForUrl( | |
| 341 '%s/+/%s/%s?format=text' % (self.repo_url, git_revision, path), | |
| 342 base64.b64encode(original_source)) | |
| 343 source = self.git_repo.GetSource(path, git_revision) | |
| 344 self.assertEqual(original_source, source) | |
| 345 | |
| 346 def testTimeConversion(self): | |
| 347 datetime_with_timezone = 'Wed Jul 22 19:35:32 2014 +0400' | |
| 348 expected_datetime = datetime(2014, 7, 22, 15, 35, 32) | |
| 349 utc_datetime = self.git_repo._GetDateTimeFromString(datetime_with_timezone) | |
| 350 | |
| 351 self.assertEqual(expected_datetime, utc_datetime) | |
| 352 | |
| 353 def testGetCommitsBetweenRevisions(self): | |
| 354 def _MockSendRequestForJsonResponse(*_): | |
| 355 return { | |
| 356 'log': [ | |
| 357 {'commit': '3'}, | |
| 358 {'commit': '2'}, | |
| 359 {'commit': '1'}] | |
| 360 } | |
| 361 self.mock(gitiles_repository.GitilesRepository, | |
| 362 '_SendRequestForJsonResponse', _MockSendRequestForJsonResponse) | |
| 363 expected_commits = ['3', '2', '1'] | |
| 364 actual_commits = self.git_repo.GetCommitsBetweenRevisions('0', '3') | |
| 365 self.assertEqual(expected_commits, actual_commits) | |
| 366 | |
| 367 def testGetCommitsBetweenRevisionsWithEmptyData(self): | |
| 368 def _MockSendRequestForJsonResponse(*_): | |
| 369 return None | |
| 370 self.mock(gitiles_repository.GitilesRepository, | |
| 371 '_SendRequestForJsonResponse', _MockSendRequestForJsonResponse) | |
| 372 expected_commits = [] | |
| 373 actual_commits = self.git_repo.GetCommitsBetweenRevisions('0', '3') | |
| 374 self.assertEqual(expected_commits, actual_commits) | |
| 375 | |
| 376 def testGetCommitsBetweenRevisionsWithIncompleteData(self): | |
| 377 def _MockSendRequestForJsonResponse(*_): | |
| 378 return { | |
| 379 'log': [ | |
| 380 {'commit': '1'}, | |
| 381 {'something_else': '2'} | |
| 382 ] | |
| 383 } | |
| 384 self.mock(gitiles_repository.GitilesRepository, | |
| 385 '_SendRequestForJsonResponse', _MockSendRequestForJsonResponse) | |
| 386 expected_commits = ['1'] | |
| 387 actual_commits = self.git_repo.GetCommitsBetweenRevisions('0', '3') | |
| 388 self.assertEqual(expected_commits, actual_commits) | |
| 389 | |
| 390 def testGetCommitsBetweenRevisionsWithPaging(self): | |
| 391 def _MockSendRequestForJsonResponse(*args, **_): | |
| 392 url = args[1] | |
| 393 if '0..3' in url: | |
| 394 return { | |
| 395 'log': [ | |
| 396 {'commit': '3'}, | |
| 397 {'commit': '2'} | |
| 398 ], | |
| 399 'next': '1' | |
| 400 } | |
| 401 else: | |
| 402 return { | |
| 403 'log': [ | |
| 404 {'commit': '1'} | |
| 405 ] | |
| 406 } | |
| 407 | |
| 408 self.mock(gitiles_repository.GitilesRepository, | |
| 409 '_SendRequestForJsonResponse', _MockSendRequestForJsonResponse) | |
| 410 expected_commits = ['3', '2', '1'] | |
| 411 actual_commits = self.git_repo.GetCommitsBetweenRevisions('0', '3', n=2) | |
| 412 self.assertEqual(expected_commits, actual_commits) | |
| 413 | |
| 414 def testGetChangeLogs(self): | |
| 415 def _MockSendRequestForJsonResponse(*_, **kargs): | |
| 416 self.assertTrue(bool(kargs)) | |
| 417 return {'log': [json.loads(COMMIT_LOG[5:])]} | |
| 418 | |
| 419 self.mock(gitiles_repository.GitilesRepository, | |
| 420 '_SendRequestForJsonResponse', _MockSendRequestForJsonResponse) | |
| 421 | |
| 422 changelogs = self.git_repo.GetChangeLogs('0', '2') | |
| 423 | |
| 424 self.assertEqual(len(changelogs), 1) | |
| 425 self.assertEqual(changelogs[0].ToDict(), EXPECTED_CHANGE_LOG_JSON) | |
| 426 | |
| 427 def testGetChangeLogsNextPage(self): | |
| 428 log1 = json.loads(COMMIT_LOG[5:]) | |
| 429 log1['commit'] = 'first_commit' | |
| 430 log2 = log1.copy() | |
| 431 log2['commit'] = 'next_page_commit' | |
| 432 | |
| 433 def _MockSendRequestForJsonResponse(_, url, **kargs): | |
| 434 self.assertTrue(bool(kargs)) | |
| 435 if 'next' in url: | |
| 436 return {'log': [log2]} | |
| 437 | |
| 438 return {'log': [log1], 'next': 'next_page_commit'} | |
| 439 | |
| 440 self.mock(gitiles_repository.GitilesRepository, | |
| 441 '_SendRequestForJsonResponse', _MockSendRequestForJsonResponse) | |
| 442 | |
| 443 changelogs = self.git_repo.GetChangeLogs('0', '2') | |
| 444 | |
| 445 self.assertEqual(len(changelogs), 2) | |
| 446 | |
| 447 def testGetWrappedGitRepositoryClass(self): | |
| 448 repo = gitiles_repository.GitilesRepository( | |
| 449 self.http_client_for_git, 'http://repo_url') | |
| 450 self.assertEqual(repo.repo_url, 'http://repo_url') | |
| OLD | NEW |