Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(144)

Side by Side Diff: tests/owners_unittest.py

Issue 2295723007: owners_unittest.py: Add tests of interaction between per-file owners,
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698