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

Side by Side Diff: tests/owners_unittest.py

Issue 11638019: Revert "Try again to land the improved owners algorithm." again :). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 8 years 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 | Annotate | Revision Log
« no previous file with comments | « owners.py ('k') | 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 os 8 import os
9 import sys 9 import sys
10 import unittest 10 import unittest
(...skipping 22 matching lines...) Expand all
33 s += '\n'.join(kwargs.get('lines', [])) + '\n' 33 s += '\n'.join(kwargs.get('lines', [])) + '\n'
34 return s + '\n'.join(email_addresses) + '\n' 34 return s + '\n'.join(email_addresses) + '\n'
35 35
36 36
37 def test_repo(): 37 def test_repo():
38 return filesystem_mock.MockFileSystem(files={ 38 return filesystem_mock.MockFileSystem(files={
39 '/DEPS' : '', 39 '/DEPS' : '',
40 '/OWNERS': owners_file(owners.EVERYONE), 40 '/OWNERS': owners_file(owners.EVERYONE),
41 '/base/vlog.h': '', 41 '/base/vlog.h': '',
42 '/chrome/OWNERS': owners_file(ben, brett), 42 '/chrome/OWNERS': owners_file(ben, brett),
43 '/chrome/browser/OWNERS': owners_file(brett),
44 '/chrome/browser/defaults.h': '',
45 '/chrome/gpu/OWNERS': owners_file(ken), 43 '/chrome/gpu/OWNERS': owners_file(ken),
46 '/chrome/gpu/gpu_channel.h': '', 44 '/chrome/gpu/gpu_channel.h': '',
47 '/chrome/renderer/OWNERS': owners_file(peter), 45 '/chrome/renderer/OWNERS': owners_file(peter),
48 '/chrome/renderer/gpu/gpu_channel_host.h': '', 46 '/chrome/renderer/gpu/gpu_channel_host.h': '',
49 '/chrome/renderer/safe_browsing/scorer.h': '', 47 '/chrome/renderer/safe_browsing/scorer.h': '',
50 '/content/OWNERS': owners_file(john, darin, comment='foo', noparent=True), 48 '/content/OWNERS': owners_file(john, darin, comment='foo', noparent=True),
51 '/content/content.gyp': '', 49 '/content/content.gyp': '',
52 '/content/bar/foo.cc': '', 50 '/content/bar/foo.cc': '',
53 '/content/baz/OWNERS': owners_file(brett), 51 '/content/baz/OWNERS': owners_file(brett),
54 '/content/baz/froboz.h': '', 52 '/content/baz/froboz.h': '',
55 '/content/baz/ugly.cc': '', 53 '/content/baz/ugly.cc': '',
56 '/content/baz/ugly.h': '', 54 '/content/baz/ugly.h': '',
57 '/content/views/OWNERS': owners_file(ben, john, owners.EVERYONE, 55 '/content/views/OWNERS': owners_file(ben, john, owners.EVERYONE,
58 noparent=True), 56 noparent=True),
59 '/content/views/pie.h': '', 57 '/content/views/pie.h': '',
60 }) 58 })
61 59
62 60
63 class _BaseTestCase(unittest.TestCase): 61 class OwnersDatabaseTest(unittest.TestCase):
64 def setUp(self): 62 def setUp(self):
65 self.repo = test_repo() 63 self.repo = test_repo()
66 self.files = self.repo.files 64 self.files = self.repo.files
67 self.root = '/' 65 self.root = '/'
68 self.fopen = self.repo.open_for_reading 66 self.fopen = self.repo.open_for_reading
69 self.glob = self.repo.glob 67 self.glob = self.repo.glob
70 68
71 def db(self, root=None, fopen=None, os_path=None, glob=None): 69 def db(self, root=None, fopen=None, os_path=None, glob=None):
72 root = root or self.root 70 root = root or self.root
73 fopen = fopen or self.fopen 71 fopen = fopen or self.fopen
74 os_path = os_path or self.repo 72 os_path = os_path or self.repo
75 glob = glob or self.glob 73 glob = glob or self.glob
76 return owners.Database(root, fopen, os_path, glob) 74 return owners.Database(root, fopen, os_path, glob)
77 75
78
79 class OwnersDatabaseTest(_BaseTestCase):
80 def test_constructor(self): 76 def test_constructor(self):
81 self.assertNotEquals(self.db(), None) 77 self.assertNotEquals(self.db(), None)
82 78
83 def test_dirs_not_covered_by__valid_inputs(self): 79 def test_dirs_not_covered_by__valid_inputs(self):
84 db = self.db() 80 db = self.db()
85 81
86 # Check that we're passed in a sequence that isn't a string. 82 # Check that we're passed in a sequence that isn't a string.
87 self.assertRaises(AssertionError, db.directories_not_covered_by, 'foo', []) 83 self.assertRaises(AssertionError, db.directories_not_covered_by, 'foo', [])
88 if hasattr(owners.collections, 'Iterable'): 84 if hasattr(owners.collections, 'Iterable'):
89 self.assertRaises(AssertionError, db.directories_not_covered_by, 85 self.assertRaises(AssertionError, db.directories_not_covered_by,
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
202 # This test ensures the mock relpath has the arguments in the right 198 # This test ensures the mock relpath has the arguments in the right
203 # order; this should probably live someplace else. 199 # order; this should probably live someplace else.
204 self.assertEquals(self.repo.relpath('foo/bar.c', 'foo/'), 'bar.c') 200 self.assertEquals(self.repo.relpath('foo/bar.c', 'foo/'), 'bar.c')
205 self.assertEquals(self.repo.relpath('/bar.c', '/'), 'bar.c') 201 self.assertEquals(self.repo.relpath('/bar.c', '/'), 'bar.c')
206 202
207 def test_per_file_glob_across_dirs_not_allowed(self): 203 def test_per_file_glob_across_dirs_not_allowed(self):
208 self.files['/OWNERS'] = 'per-file content/*=john@example.org\n' 204 self.files['/OWNERS'] = 'per-file content/*=john@example.org\n'
209 self.assertRaises(owners.SyntaxErrorInOwnersFile, 205 self.assertRaises(owners.SyntaxErrorInOwnersFile,
210 self.db().directories_not_covered_by, ['DEPS'], [brett]) 206 self.db().directories_not_covered_by, ['DEPS'], [brett])
211 207
212 def assert_syntax_error(self, owners_file_contents): 208 def assert_reviewers_for(self, files, expected_reviewers):
213 db = self.db() 209 db = self.db()
214 self.files['/foo/OWNERS'] = owners_file_contents 210 self.assertEquals(db.reviewers_for(set(files)), set(expected_reviewers))
215 self.files['/foo/DEPS'] = ''
216 try:
217 db.reviewers_for(['foo/DEPS'])
218 self.fail() # pragma: no cover
219 except owners.SyntaxErrorInOwnersFile, e:
220 self.assertTrue(str(e).startswith('/foo/OWNERS:1'))
221
222 def test_syntax_error__unknown_token(self):
223 self.assert_syntax_error('{}\n')
224
225 def test_syntax_error__unknown_set(self):
226 self.assert_syntax_error('set myfatherisbillgates\n')
227
228 def test_syntax_error__bad_email(self):
229 self.assert_syntax_error('ben\n')
230
231
232 class ReviewersForTest(_BaseTestCase):
233 def assert_reviewers_for(self, files, *potential_suggested_reviewers):
234 db = self.db()
235 suggested_reviewers = db.reviewers_for(set(files))
236 self.assertTrue(suggested_reviewers in
237 [set(suggestion) for suggestion in potential_suggested_reviewers])
238 211
239 def test_reviewers_for__basic_functionality(self): 212 def test_reviewers_for__basic_functionality(self):
240 self.assert_reviewers_for(['chrome/gpu/gpu_channel.h'], 213 self.assert_reviewers_for(['chrome/gpu/gpu_channel.h'],
241 [ken]) 214 [brett])
242 215
243 def test_reviewers_for__set_noparent_works(self): 216 def test_reviewers_for__set_noparent_works(self):
244 self.assert_reviewers_for(['content/content.gyp'], 217 self.assert_reviewers_for(['content/content.gyp'], [darin])
245 [john],
246 [darin])
247 218
248 def test_reviewers_for__valid_inputs(self): 219 def test_reviewers_for__valid_inputs(self):
249 db = self.db() 220 db = self.db()
250 221
251 # Check that we're passed in a sequence that isn't a string. 222 # Check that we're passed in a sequence that isn't a string.
252 self.assertRaises(AssertionError, db.reviewers_for, 'foo') 223 self.assertRaises(AssertionError, db.reviewers_for, 'foo')
253 if hasattr(owners.collections, 'Iterable'): 224 if hasattr(owners.collections, 'Iterable'):
254 self.assertRaises(AssertionError, db.reviewers_for, 225 self.assertRaises(AssertionError, db.reviewers_for,
255 (f for f in ['x', 'y'])) 226 (f for f in ['x', 'y']))
256 227
257 # Check that the files are under the root. 228 # Check that the files are under the root.
258 db.root = '/checkout' 229 db.root = '/checkout'
259 self.assertRaises(AssertionError, db.reviewers_for, ['/OWNERS']) 230 self.assertRaises(AssertionError, db.reviewers_for, ['/OWNERS'])
260 231
261 def test_reviewers_for__wildcard_dir(self): 232 def test_reviewers_for__wildcard_dir(self):
262 self.assert_reviewers_for(['DEPS'], [owners.EVERYONE]) 233 self.assert_reviewers_for(['DEPS'], [owners.EVERYONE])
263 234
264 def test_reviewers_for__one_owner(self): 235 def test_reviewers_for__one_owner(self):
265 self.assert_reviewers_for([ 236 self.assert_reviewers_for([
266 'chrome/gpu/gpu_channel.h', 237 'chrome/gpu/gpu_channel.h',
267 'content/baz/froboz.h', 238 'content/baz/froboz.h',
268 'chrome/renderer/gpu/gpu_channel_host.h'], 239 'chrome/renderer/gpu/gpu_channel_host.h'], [brett])
269 [brett])
270 240
271 def test_reviewers_for__two_owners(self): 241 def test_reviewers_for__two_owners(self):
272 self.assert_reviewers_for([ 242 self.assert_reviewers_for([
273 'chrome/gpu/gpu_channel.h', 243 'chrome/gpu/gpu_channel.h',
274 'content/content.gyp', 244 'content/content.gyp',
275 'content/baz/froboz.h', 245 'content/baz/froboz.h',
276 'content/views/pie.h'], 246 'content/views/pie.h'
277 [ken, john]) 247 ], [john, brett])
278 248
279 def test_reviewers_for__all_files(self): 249 def test_reviewers_for__all_files(self):
280 self.assert_reviewers_for([ 250 self.assert_reviewers_for([
281 'chrome/gpu/gpu_channel.h', 251 'chrome/gpu/gpu_channel.h',
282 'chrome/renderer/gpu/gpu_channel_host.h', 252 'chrome/renderer/gpu/gpu_channel_host.h',
283 'chrome/renderer/safe_browsing/scorer.h', 253 'chrome/renderer/safe_browsing/scorer.h',
284 'content/content.gyp', 254 'content/content.gyp',
285 'content/bar/foo.cc', 255 'content/bar/foo.cc',
286 'content/baz/froboz.h', 256 'content/baz/froboz.h',
287 'content/views/pie.h'], 257 'content/views/pie.h'], [john, brett])
288 [peter, ken, john])
289 258
290 def test_reviewers_for__per_file_owners_file(self): 259 def test_reviewers_for__per_file_owners_file(self):
291 self.files['/content/baz/OWNERS'] = owners_file(lines=[ 260 self.files['/content/baz/OWNERS'] = owners_file(lines=[
292 'per-file ugly.*=tom@example.com']) 261 'per-file ugly.*=tom@example.com'])
293 self.assert_reviewers_for(['content/baz/OWNERS'], 262 self.assert_reviewers_for(['content/baz/OWNERS'], [darin])
294 [john],
295 [darin])
296 263
297 def test_reviewers_for__per_file(self): 264 def assert_syntax_error(self, owners_file_contents):
298 self.files['/content/baz/OWNERS'] = owners_file(lines=[ 265 db = self.db()
299 'per-file ugly.*=tom@example.com']) 266 self.files['/foo/OWNERS'] = owners_file_contents
300 self.assert_reviewers_for(['content/baz/ugly.cc'], 267 self.files['/foo/DEPS'] = ''
301 [tom]) 268 try:
269 db.reviewers_for(['foo/DEPS'])
270 self.fail() # pragma: no cover
271 except owners.SyntaxErrorInOwnersFile, e:
272 self.assertTrue(str(e).startswith('/foo/OWNERS:1'))
302 273
303 def test_reviewers_for__two_nested_dirs(self): 274 def test_syntax_error__unknown_token(self):
304 # The same owner is listed in two directories (one above the other) 275 self.assert_syntax_error('{}\n')
305 self.assert_reviewers_for(['chrome/browser/defaults.h'],
306 [brett])
307 276
308 class LowestCostOwnersTest(_BaseTestCase): 277 def test_syntax_error__unknown_set(self):
309 # Keep the data in the test_lowest_cost_owner* methods as consistent with 278 self.assert_syntax_error('set myfatherisbillgates\n')
310 # test_repo() where possible to minimize confusion.
311 279
312 def check(self, possible_owners, dirs, *possible_lowest_cost_owners): 280 def test_syntax_error__bad_email(self):
313 suggested_owner = owners.Database.lowest_cost_owner(possible_owners, dirs) 281 self.assert_syntax_error('ben\n')
314 self.assertTrue(suggested_owner in possible_lowest_cost_owners)
315 282
316 def test_one_dir_with_owner(self):
317 # brett is the only immediate owner for stuff in baz; john is also
318 # an owner, but further removed. We should always get brett.
319 self.check({brett: [('content/baz', 1)],
320 john: [('content/baz', 2)]},
321 ['content/baz'],
322 brett)
323
324 # john and darin are owners for content; the suggestion could be either.
325 def test_one_dir_with_two_owners(self):
326 self.check({john: [('content', 1)],
327 darin: [('content', 1)]},
328 ['content'],
329 john, darin)
330
331 def test_one_dir_with_two_owners_in_parent(self):
332 # As long as the distance is the same, it shouldn't matter (brett isn't
333 # listed in this case).
334 self.check({john: [('content/baz', 2)],
335 darin: [('content/baz', 2)]},
336 ['content/baz'],
337 john, darin)
338
339 def test_two_dirs_two_owners(self):
340 # If they both match both dirs, they should be treated equally.
341 self.check({john: [('content/baz', 2), ('content/bar', 2)],
342 darin: [('content/baz', 2), ('content/bar', 2)]},
343 ['content/baz', 'content/bar'],
344 john, darin)
345
346 # Here brett is better since he's closer for one of the two dirs.
347 self.check({brett: [('content/baz', 1), ('content/views', 1)],
348 darin: [('content/baz', 2), ('content/views', 1)]},
349 ['content/baz', 'content/views'],
350 brett)
351
352 def test_hierarchy(self):
353 # the choices in these tests are more arbitrary value judgements;
354 # also, here we drift away from test_repo() to cover more cases.
355
356 # Here ben isn't picked, even though he can review both; we prefer
357 # closer reviewers.
358 self.check({ben: [('chrome/gpu', 2), ('chrome/renderer', 2)],
359 ken: [('chrome/gpu', 1)],
360 peter: [('chrome/renderer', 1)]},
361 ['chrome/gpu', 'chrome/renderer'],
362 ken, peter)
363
364 # Here we always pick ben since he can review either dir as well as
365 # the others but can review both (giving us fewer total reviewers).
366 self.check({ben: [('chrome/gpu', 1), ('chrome/renderer', 1)],
367 ken: [('chrome/gpu', 1)],
368 peter: [('chrome/renderer', 1)]},
369 ['chrome/gpu', 'chrome/renderer'],
370 ben)
371
372 # However, three reviewers is too many, so ben gets this one.
373 self.check({ben: [('chrome/gpu', 2), ('chrome/renderer', 2),
374 ('chrome/browser', 2)],
375 ken: [('chrome/gpu', 1)],
376 peter: [('chrome/renderer', 1)],
377 brett: [('chrome/browser', 1)]},
378 ['chrome/gpu', 'chrome/renderer',
379 'chrome/browser'],
380 ben)
381 283
382 if __name__ == '__main__': 284 if __name__ == '__main__':
383 unittest.main() 285 unittest.main()
OLDNEW
« no previous file with comments | « owners.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698