| 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 """Unit tests for owners.py.""" | 6 """Unit tests for owners.py.""" |
| 7 | 7 |
| 8 import itertools |
| 8 import os | 9 import os |
| 9 import sys | 10 import sys |
| 10 import unittest | 11 import unittest |
| 11 | 12 |
| 12 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | 13 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 13 | 14 |
| 14 from testing_support import filesystem_mock | 15 from testing_support import filesystem_mock |
| 15 | 16 |
| 16 import owners | 17 import owners |
| 17 | 18 |
| 18 ben = 'ben@example.com' | 19 ben = 'ben@example.com' |
| 19 brett = 'brett@example.com' | 20 brett = 'brett@example.com' |
| 21 dana = 'dana@example.com' |
| 20 darin = 'darin@example.com' | 22 darin = 'darin@example.com' |
| 23 elena = 'elena@example.com' |
| 21 john = 'john@example.com' | 24 john = 'john@example.com' |
| 22 ken = 'ken@example.com' | 25 ken = 'ken@example.com' |
| 23 peter = 'peter@example.com' | 26 peter = 'peter@example.com' |
| 24 tom = 'tom@example.com' | 27 tom = 'tom@example.com' |
| 25 | 28 |
| 26 | |
| 27 def owners_file(*email_addresses, **kwargs): | 29 def owners_file(*email_addresses, **kwargs): |
| 28 s = '' | 30 s = '' |
| 29 if kwargs.get('comment'): | 31 if kwargs.get('comment'): |
| 30 s += '# %s\n' % kwargs.get('comment') | 32 s += '# %s\n' % kwargs.get('comment') |
| 31 if kwargs.get('noparent'): | 33 if kwargs.get('noparent'): |
| 32 s += 'set noparent\n' | 34 s += 'set noparent\n' |
| 33 if kwargs.get('file'): | 35 if kwargs.get('file'): |
| 34 s += 'file:%s\n' % kwargs.get('file') | 36 s += 'file:%s\n' % kwargs.get('file') |
| 35 s += '\n'.join(kwargs.get('lines', [])) + '\n' | 37 s += '\n'.join(kwargs.get('lines', [])) + '\n' |
| 36 return s + '\n'.join(email_addresses) + '\n' | 38 return s + '\n'.join(email_addresses) + '\n' |
| 37 | 39 |
| 38 | 40 |
| 39 def test_repo(): | 41 def test_repo(): |
| 40 return filesystem_mock.MockFileSystem(files={ | 42 return filesystem_mock.MockFileSystem(files={ |
| 41 '/DEPS' : '', | 43 '/DEPS' : '', |
| 42 '/OWNERS': owners_file(owners.EVERYONE), | 44 '/OWNERS': owners_file(dana), |
| 43 '/base/vlog.h': '', | 45 '/base/vlog.h': '', |
| 44 '/chrome/OWNERS': owners_file(ben, brett), | 46 '/chrome/OWNERS': owners_file(ben, brett), |
| 47 '/chrome/chrome.mojom': '', |
| 48 '/chrome/locked.mojom': '', |
| 45 '/chrome/browser/OWNERS': owners_file(brett), | 49 '/chrome/browser/OWNERS': owners_file(brett), |
| 46 '/chrome/browser/defaults.h': '', | 50 '/chrome/browser/defaults.h': '', |
| 47 '/chrome/gpu/OWNERS': owners_file(ken), | 51 '/chrome/gpu/OWNERS': owners_file(ken), |
| 48 '/chrome/gpu/gpu_channel.h': '', | 52 '/chrome/gpu/gpu_channel.h': '', |
| 49 '/chrome/renderer/OWNERS': owners_file(peter), | 53 '/chrome/renderer/OWNERS': owners_file(peter), |
| 50 '/chrome/renderer/gpu/gpu_channel_host.h': '', | 54 '/chrome/renderer/gpu/gpu_channel_host.h': '', |
| 51 '/chrome/renderer/safe_browsing/scorer.h': '', | 55 '/chrome/renderer/safe_browsing/scorer.h': '', |
| 56 '/chrome/renderer/safe_browsing/scorer.mojom': '', |
| 52 '/content/OWNERS': owners_file(john, darin, comment='foo', noparent=True), | 57 '/content/OWNERS': owners_file(john, darin, comment='foo', noparent=True), |
| 53 '/content/content.gyp': '', | 58 '/content/content.gyp': '', |
| 54 '/content/bar/foo.cc': '', | 59 '/content/bar/foo.cc': '', |
| 60 '/content/bar/armchair.mojom': '', |
| 55 '/content/baz/OWNERS': owners_file(brett), | 61 '/content/baz/OWNERS': owners_file(brett), |
| 56 '/content/baz/froboz.h': '', | 62 '/content/baz/froboz.h': '', |
| 57 '/content/baz/ugly.cc': '', | 63 '/content/baz/ugly.cc': '', |
| 58 '/content/baz/ugly.h': '', | 64 '/content/baz/ugly.h': '', |
| 59 '/content/garply/OWNERS': owners_file(file='test/OWNERS'), | 65 '/content/garply/OWNERS': owners_file(file='test/OWNERS'), |
| 60 '/content/garply/foo.cc': '', | 66 '/content/garply/foo.cc': '', |
| 61 '/content/garply/test/OWNERS': owners_file(peter), | 67 '/content/garply/test/OWNERS': owners_file(peter), |
| 62 '/content/qux/OWNERS': owners_file(peter, file='//content/baz/OWNERS'), | 68 '/content/qux/OWNERS': owners_file(peter, file='//content/baz/OWNERS'), |
| 63 '/content/qux/foo.cc': '', | 69 '/content/qux/foo.cc': '', |
| 64 '/content/views/OWNERS': owners_file(ben, john, owners.EVERYONE, | 70 '/content/views/OWNERS': owners_file(ben, john, owners.EVERYONE, |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 self.assertRaises(AssertionError, db.files_not_covered_by, | 105 self.assertRaises(AssertionError, db.files_not_covered_by, |
| 100 ['/OWNERS'], []) | 106 ['/OWNERS'], []) |
| 101 db.root = '/' | 107 db.root = '/' |
| 102 | 108 |
| 103 # Check invalid email address. | 109 # Check invalid email address. |
| 104 self.assertRaises(AssertionError, db.files_not_covered_by, | 110 self.assertRaises(AssertionError, db.files_not_covered_by, |
| 105 ['OWNERS'], ['foo']) | 111 ['OWNERS'], ['foo']) |
| 106 | 112 |
| 107 def assert_files_not_covered_by(self, files, reviewers, unreviewed_files): | 113 def assert_files_not_covered_by(self, files, reviewers, unreviewed_files): |
| 108 db = self.db() | 114 db = self.db() |
| 109 self.assertEquals(db.files_not_covered_by(set(files), set(reviewers)), | 115 result = db.files_not_covered_by(set(files), set(reviewers)) |
| 110 set(unreviewed_files)) | 116 expected = set(unreviewed_files) |
| 117 if result != expected: |
| 118 raise AssertionError('files_not_covered_by(%s, %s)\n returned: %s\n expe
cted: %s' % (list(set(files)), list(set(reviewers)), list(result), list(expected
))) |
| 111 | 119 |
| 112 def test_files_not_covered_by__owners_propagates_down(self): | 120 def test_files_not_covered_by__owners_propagates_down(self): |
| 113 self.assert_files_not_covered_by( | 121 self.assert_files_not_covered_by( |
| 114 ['chrome/gpu/gpu_channel.h', 'chrome/renderer/gpu/gpu_channel_host.h'], | 122 ['chrome/gpu/gpu_channel.h', 'chrome/renderer/gpu/gpu_channel_host.h'], |
| 115 [ben], []) | 123 [ben], []) |
| 116 | 124 |
| 117 def test_files_not_covered_by__partial_covering(self): | 125 def test_files_not_covered_by__partial_covering(self): |
| 118 self.assert_files_not_covered_by( | 126 self.assert_files_not_covered_by( |
| 119 ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h'], | 127 ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h'], |
| 120 [peter], ['content/content.gyp']) | 128 [peter], ['content/content.gyp']) |
| 121 | 129 |
| 122 def test_files_not_covered_by__set_noparent_works(self): | 130 def test_files_not_covered_by__set_noparent_works(self): |
| 123 self.assert_files_not_covered_by(['content/content.gyp'], [ben], | 131 self.assert_files_not_covered_by(['content/content.gyp'], [ben], |
| 124 ['content/content.gyp']) | 132 ['content/content.gyp']) |
| 125 | 133 |
| 126 def test_files_not_covered_by__no_reviewer(self): | 134 def test_files_not_covered_by__no_reviewer(self): |
| 127 self.assert_files_not_covered_by( | 135 self.assert_files_not_covered_by( |
| 128 ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h'], | 136 ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h'], |
| 129 [], ['content/content.gyp']) | 137 [], ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h']) |
| 138 self.assert_files_not_covered_by( |
| 139 ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h', 'content
/views/pie.h'], |
| 140 [], ['content/content.gyp', 'chrome/renderer/gpu/gpu_channel_host.h']) |
| 130 | 141 |
| 131 def test_files_not_covered_by__combines_directories(self): | 142 def test_files_not_covered_by__combines_directories(self): |
| 132 self.assert_files_not_covered_by(['content/content.gyp', | 143 self.assert_files_not_covered_by(['content/content.gyp', |
| 133 'content/bar/foo.cc', | 144 'content/bar/foo.cc', |
| 134 'chrome/renderer/gpu/gpu_channel_host.h'], | 145 'chrome/renderer/gpu/gpu_channel_host.h'], |
| 135 [peter], | 146 [peter], |
| 136 ['content/content.gyp', | 147 ['content/content.gyp', |
| 137 'content/bar/foo.cc']) | 148 'content/bar/foo.cc']) |
| 138 | 149 |
| 139 def test_files_not_covered_by__multiple_directories(self): | 150 def test_files_not_covered_by__multiple_directories(self): |
| 151 self.files['/OWNERS'] = owners_file(owners.EVERYONE) |
| 140 self.assert_files_not_covered_by( | 152 self.assert_files_not_covered_by( |
| 141 ['content/content.gyp', # Not covered | 153 ['content/content.gyp', # Not covered |
| 142 'content/bar/foo.cc', # Not covered (combines in) | 154 'content/bar/foo.cc', # Not covered (combines in) |
| 143 'content/baz/froboz.h', # Not covered | 155 'content/baz/froboz.h', # Not covered |
| 144 'chrome/gpu/gpu_channel.h', # Owned by ken | 156 'chrome/gpu/gpu_channel.h', # Owned by ken |
| 145 'chrome/renderer/gpu/gpu_channel_host.h' # Owned by * via parent | 157 'chrome/renderer/gpu/gpu_channel_host.h' # Owned by * via parent |
| 146 ], | 158 ], |
| 147 [ken], | 159 [ken], |
| 148 ['content/content.gyp', 'content/bar/foo.cc', 'content/baz/froboz.h']) | 160 ['content/content.gyp', 'content/bar/foo.cc', 'content/baz/froboz.h']) |
| 149 | 161 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 # This test ensures the mock relpath has the arguments in the right | 248 # This test ensures the mock relpath has the arguments in the right |
| 237 # order; this should probably live someplace else. | 249 # order; this should probably live someplace else. |
| 238 self.assertEquals(self.repo.relpath('foo/bar.c', 'foo/'), 'bar.c') | 250 self.assertEquals(self.repo.relpath('foo/bar.c', 'foo/'), 'bar.c') |
| 239 self.assertEquals(self.repo.relpath('/bar.c', '/'), 'bar.c') | 251 self.assertEquals(self.repo.relpath('/bar.c', '/'), 'bar.c') |
| 240 | 252 |
| 241 def test_per_file_glob_across_dirs_not_allowed(self): | 253 def test_per_file_glob_across_dirs_not_allowed(self): |
| 242 self.files['/OWNERS'] = 'per-file content/*=john@example.org\n' | 254 self.files['/OWNERS'] = 'per-file content/*=john@example.org\n' |
| 243 self.assertRaises(owners.SyntaxErrorInOwnersFile, | 255 self.assertRaises(owners.SyntaxErrorInOwnersFile, |
| 244 self.db().files_not_covered_by, ['DEPS'], [brett]) | 256 self.db().files_not_covered_by, ['DEPS'], [brett]) |
| 245 | 257 |
| 258 def test_per_file_glob_transitive(self): |
| 259 # per-file *.ext rules apply to matching files |
| 260 # in subdirectories, but not across 'set noparent'. |
| 261 self.files['/chrome/OWNERS'] = owners_file(ben, brett, |
| 262 lines=['per-file *.mojom=tom@exam
ple.com', |
| 263 'per-file locked.mojom=set
noparent']) |
| 264 self.files['/OWNERS'] = owners_file(dana, |
| 265 lines=['per-file *.mojom=elena@example.c
om']) |
| 266 # For each file, who we expect to be able to approve it. |
| 267 permissions = { |
| 268 'chrome/chrome.mojom': [dana, tom, elena], |
| 269 'chrome/locked.mojom': [tom], |
| 270 'chrome/renderer/safe_browsing/scorer.mojom': [dana, tom, elena], |
| 271 'chrome/renderer/safe_browsing/scorer.h': [dana, ben], |
| 272 'content/bar/armchair.mojom': [darin], |
| 273 } |
| 274 |
| 275 reviewers = set(sum(permissions.values(), [])) |
| 276 files = permissions.keys() |
| 277 for reviewer in reviewers: |
| 278 owned_files = set( |
| 279 f for (f, acl) in permissions.iteritems() if reviewer in acl) |
| 280 for num_files in range(1, len(files)+1): |
| 281 for touched_files in itertools.combinations(files, num_files): |
| 282 expected_missing_coverage = set(touched_files) - owned_files |
| 283 self.assert_files_not_covered_by(touched_files, [reviewer], |
| 284 expected_missing_coverage) |
| 285 |
| 246 def test_file_include_absolute_path(self): | 286 def test_file_include_absolute_path(self): |
| 247 self.assert_files_not_covered_by(['content/qux/foo.cc'], [brett], []) | 287 self.assert_files_not_covered_by(['content/qux/foo.cc'], [brett], []) |
| 248 self.assert_files_not_covered_by(['content/qux/bar.cc'], [peter], []) | 288 self.assert_files_not_covered_by(['content/qux/bar.cc'], [peter], []) |
| 249 self.assert_files_not_covered_by(['content/qux/baz.cc'], | 289 self.assert_files_not_covered_by(['content/qux/baz.cc'], |
| 250 [tom], ['content/qux/baz.cc']) | 290 [tom], ['content/qux/baz.cc']) |
| 251 | 291 |
| 252 def test_file_include_relative_path(self): | 292 def test_file_include_relative_path(self): |
| 253 self.assert_files_not_covered_by(['content/garply/foo.cc'], [peter], []) | 293 self.assert_files_not_covered_by(['content/garply/foo.cc'], [peter], []) |
| 254 self.assert_files_not_covered_by(['content/garply/bar.cc'], [darin], []) | 294 self.assert_files_not_covered_by(['content/garply/bar.cc'], [darin], []) |
| 255 self.assert_files_not_covered_by(['content/garply/baz.cc'], | 295 self.assert_files_not_covered_by(['content/garply/baz.cc'], |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 self.assertRaises(AssertionError, db.reviewers_for, 'foo', None) | 386 self.assertRaises(AssertionError, db.reviewers_for, 'foo', None) |
| 347 if hasattr(owners.collections, 'Iterable'): | 387 if hasattr(owners.collections, 'Iterable'): |
| 348 self.assertRaises(AssertionError, db.reviewers_for, | 388 self.assertRaises(AssertionError, db.reviewers_for, |
| 349 (f for f in ['x', 'y']), None) | 389 (f for f in ['x', 'y']), None) |
| 350 | 390 |
| 351 # Check that the files are under the root. | 391 # Check that the files are under the root. |
| 352 db.root = '/checkout' | 392 db.root = '/checkout' |
| 353 self.assertRaises(AssertionError, db.reviewers_for, ['/OWNERS'], None) | 393 self.assertRaises(AssertionError, db.reviewers_for, ['/OWNERS'], None) |
| 354 | 394 |
| 355 def test_reviewers_for__wildcard_dir(self): | 395 def test_reviewers_for__wildcard_dir(self): |
| 396 self.files['/OWNERS'] = owners_file(owners.EVERYONE) |
| 356 self.assert_reviewers_for(['DEPS'], [['<anyone>']]) | 397 self.assert_reviewers_for(['DEPS'], [['<anyone>']]) |
| 357 self.assert_reviewers_for(['DEPS', 'chrome/gpu/gpu_channel.h'], [[ken]]) | 398 self.assert_reviewers_for(['DEPS', 'chrome/gpu/gpu_channel.h'], [[ken]]) |
| 358 | 399 |
| 359 def test_reviewers_for__one_owner(self): | 400 def test_reviewers_for__one_owner(self): |
| 360 self.assert_reviewers_for([ | 401 self.assert_reviewers_for([ |
| 361 'chrome/gpu/gpu_channel.h', | 402 'chrome/gpu/gpu_channel.h', |
| 362 'content/baz/froboz.h', | 403 'content/baz/froboz.h', |
| 363 'chrome/renderer/gpu/gpu_channel_host.h'], | 404 'chrome/renderer/gpu/gpu_channel_host.h'], |
| 364 [[brett]]) | 405 [[brett]]) |
| 365 | 406 |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 512 ('chrome/browser', 2)], | 553 ('chrome/browser', 2)], |
| 513 ken: [('chrome/gpu', 1)], | 554 ken: [('chrome/gpu', 1)], |
| 514 peter: [('chrome/renderer', 1)], | 555 peter: [('chrome/renderer', 1)], |
| 515 brett: [('chrome/browser', 1)]}, | 556 brett: [('chrome/browser', 1)]}, |
| 516 ['chrome/gpu', 'chrome/renderer', | 557 ['chrome/gpu', 'chrome/renderer', |
| 517 'chrome/browser'], | 558 'chrome/browser'], |
| 518 ben) | 559 ben) |
| 519 | 560 |
| 520 if __name__ == '__main__': | 561 if __name__ == '__main__': |
| 521 unittest.main() | 562 unittest.main() |
| OLD | NEW |