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

Side by Side Diff: tests/owners_unittest.py

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