OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """Unit tests for presubmit.py and presubmit_canned_checks.py.""" |
| 7 |
| 8 import os |
| 9 import StringIO |
| 10 import unittest |
| 11 |
| 12 # Local imports |
| 13 import gcl |
| 14 import presubmit |
| 15 import presubmit_canned_checks |
| 16 |
| 17 |
| 18 class PresubmitTestsBase(unittest.TestCase): |
| 19 """Setups and tear downs the mocks but doesn't test anything as-is.""" |
| 20 def setUp(self): |
| 21 self.original_IsFile = os.path.isfile |
| 22 def MockIsFile(f): |
| 23 dir = os.path.dirname(f) |
| 24 return dir.endswith('haspresubmit') or dir == '' |
| 25 os.path.isfile = MockIsFile |
| 26 |
| 27 self.original_GetSVNFileInfo = gcl.GetSVNFileInfo |
| 28 def MockGetSVNFileInfo(path): |
| 29 if path.count('notfound'): |
| 30 return {} |
| 31 results = { |
| 32 'Path': path[len('svn:/foo/'):], |
| 33 'URL': 'svn:/foo/%s' % path.replace('\\', '/'), |
| 34 } |
| 35 if path.endswith('isdir'): |
| 36 results['Node Kind'] = 'directory' |
| 37 else: |
| 38 results['Node Kind'] = 'file' |
| 39 return results |
| 40 gcl.GetSVNFileInfo = MockGetSVNFileInfo |
| 41 |
| 42 self.original_GetSVNFileProperty = gcl.GetSVNFileProperty |
| 43 def MockGetSVNFileProperty(path, property_name): |
| 44 if property_name == 'svn:secret-property': |
| 45 return 'secret-property-value' |
| 46 elif path.count('binary'): |
| 47 return 'application/octet-stream' |
| 48 else: |
| 49 if len(path) % 2: |
| 50 return 'text/plain' |
| 51 else: |
| 52 return '' |
| 53 gcl.GetSVNFileProperty = MockGetSVNFileProperty |
| 54 |
| 55 self.original_ReadFile = gcl.ReadFile |
| 56 def MockReadFile(path): |
| 57 if path.count('nosuchfile'): |
| 58 return None |
| 59 elif path.endswith('isdir'): |
| 60 self.fail('Should not attempt to read file that is directory.') |
| 61 elif path.endswith('PRESUBMIT.py'): |
| 62 # used in testDoPresubmitChecks |
| 63 return """ |
| 64 def CheckChangeOnUpload(input_api, output_api): |
| 65 if not input_api.change.NOSUCHKEY: |
| 66 return [output_api.PresubmitError("!!")] |
| 67 elif not input_api.change.REALLYNOSUCHKEY: |
| 68 return [output_api.PresubmitPromptWarning("??")] |
| 69 elif not input_api.change.REALLYABSOLUTELYNOSUCHKEY: |
| 70 return [output_api.PresubmitPromptWarning("??"), |
| 71 output_api.PresubmitError("XX!!XX")] |
| 72 else: |
| 73 return () |
| 74 """ |
| 75 else: |
| 76 return 'one:%s\r\ntwo:%s' % (path, path) |
| 77 gcl.ReadFile = MockReadFile |
| 78 |
| 79 self.original_GetRepositoryRoot = gcl.GetRepositoryRoot |
| 80 def MockGetRepositoryRoot(): |
| 81 return '' |
| 82 gcl.GetRepositoryRoot = MockGetRepositoryRoot |
| 83 |
| 84 def tearDown(self): |
| 85 os.path.isfile = self.original_IsFile |
| 86 gcl.GetSVNFileInfo = self.original_GetSVNFileInfo |
| 87 gcl.GetSVNFileProperty = self.original_GetSVNFileProperty |
| 88 gcl.ReadFile = self.original_ReadFile |
| 89 gcl.GetRepositoryRoot = self.original_GetRepositoryRoot |
| 90 |
| 91 @staticmethod |
| 92 def MakeBasicChange(name, description): |
| 93 ci = gcl.ChangeInfo(name=name, |
| 94 description=description, |
| 95 files=[]) |
| 96 change = presubmit.GclChange(ci) |
| 97 return change |
| 98 |
| 99 def compareMembers(self, object, members): |
| 100 """If you add a member, be sure to add the relevant test!""" |
| 101 # Skip over members starting with '_' since they are usually not meant to |
| 102 # be for public use. |
| 103 actual_members = [x for x in sorted(dir(object)) |
| 104 if not x.startswith('_')] |
| 105 self.assertEqual(actual_members, sorted(members)) |
| 106 |
| 107 |
| 108 class PresubmitUnittest(PresubmitTestsBase): |
| 109 """General presubmit.py tests (excluding InputApi and OutputApi).""" |
| 110 def testMembersChanged(self): |
| 111 members = [ |
| 112 'AffectedFile', 'DoPresubmitChecks', 'GclChange', 'InputApi', |
| 113 'ListRelevantPresubmitFiles', 'Main', 'NotImplementedException', |
| 114 'OutputApi', 'ParseFiles', 'PresubmitExecuter', 'SPECIAL_KEYS', |
| 115 'ScanSubDirs', 'cPickle', 'cStringIO', 'exceptions', |
| 116 'fnmatch', 'gcl', 'glob', 'marshal', 'normpath', 'optparse', 'os', |
| 117 'pickle', 'presubmit_canned_checks', 're', 'subprocess', 'sys', |
| 118 'tempfile', 'types', 'urllib2', |
| 119 ] |
| 120 # If this test fails, you should add the relevant test. |
| 121 self.compareMembers(presubmit, members) |
| 122 |
| 123 def testListRelevantPresubmitFiles(self): |
| 124 presubmit_files = presubmit.ListRelevantPresubmitFiles([ |
| 125 'blat.cc', |
| 126 'foo/haspresubmit/yodle/smart.h', |
| 127 'moo/mat/gat/yo.h', |
| 128 'foo/luck.h']) |
| 129 self.failUnless(len(presubmit_files) == 2) |
| 130 self.failUnless(presubmit.normpath('PRESUBMIT.py') in presubmit_files) |
| 131 self.failUnless(presubmit.normpath('foo/haspresubmit/PRESUBMIT.py') in |
| 132 presubmit_files) |
| 133 |
| 134 def testTagLineRe(self): |
| 135 m = presubmit._tag_line_re.match(' BUG =1223, 1445 \t') |
| 136 self.failUnless(m) |
| 137 self.failUnlessEqual(m.group('key'), 'BUG') |
| 138 self.failUnlessEqual(m.group('value'), '1223, 1445') |
| 139 |
| 140 def testGclChange(self): |
| 141 description_lines = ('Hello there', |
| 142 'this is a change', |
| 143 'BUG=123', |
| 144 ' STORY =http://foo/ \t', |
| 145 'and some more regular text \t') |
| 146 files = [ |
| 147 ['A', 'foo/blat.cc'], |
| 148 ['M', 'binary.dll'], # a binary file |
| 149 ['A', 'isdir'], # a directory |
| 150 ['M', 'flop/notfound.txt'], # not found in SVN, still exists locally |
| 151 ['D', 'boo/flap.h'], |
| 152 ] |
| 153 |
| 154 ci = gcl.ChangeInfo(name='mychange', |
| 155 description='\n'.join(description_lines), |
| 156 files=files) |
| 157 change = presubmit.GclChange(ci) |
| 158 |
| 159 self.failUnless(change.Change() == 'mychange') |
| 160 self.failUnless(change.Changelist() == 'mychange') |
| 161 self.failUnless(change.DescriptionText() == |
| 162 'Hello there\nthis is a change\nand some more regular text') |
| 163 self.failUnless(change.FullDescriptionText() == |
| 164 '\n'.join(description_lines)) |
| 165 |
| 166 self.failUnless(change.BugIDs == '123') |
| 167 self.failUnless(change.BUG == '123') |
| 168 self.failUnless(change.STORY == 'http://foo/') |
| 169 |
| 170 self.failUnless(len(change.AffectedFiles()) == 4) |
| 171 self.failUnless(len(change.AffectedFiles(include_dirs=True)) == 5) |
| 172 self.failUnless(len(change.AffectedFiles(include_deletes=False)) == 3) |
| 173 self.failUnless(len(change.AffectedFiles(include_dirs=True, |
| 174 include_deletes=False)) == 4) |
| 175 |
| 176 affected_text_files = change.AffectedTextFiles(include_deletes=True) |
| 177 self.failUnless(len(affected_text_files) == 3) |
| 178 self.failIf(filter(lambda x: x.LocalPath() == 'binary.dll', |
| 179 affected_text_files)) |
| 180 |
| 181 local_paths = change.LocalPaths() |
| 182 expected_paths = [presubmit.normpath(f[1]) for f in files] |
| 183 self.failUnless( |
| 184 len(filter(lambda x: x in expected_paths, local_paths)) == 4) |
| 185 |
| 186 server_paths = change.ServerPaths() |
| 187 expected_paths = ['svn:/foo/%s' % f[1] for f in files if |
| 188 f[1] != 'flop/notfound.txt'] |
| 189 expected_paths.append('') # one unknown file |
| 190 self.failUnless( |
| 191 len(filter(lambda x: x in expected_paths, server_paths)) == 4) |
| 192 |
| 193 files = [[x[0], presubmit.normpath(x[1])] for x in files] |
| 194 |
| 195 rhs_lines = [] |
| 196 for line in change.RightHandSideLines(): |
| 197 rhs_lines.append(line) |
| 198 self.failUnless(rhs_lines[0][0].LocalPath() == files[0][1]) |
| 199 self.failUnless(rhs_lines[0][1] == 1) |
| 200 self.failUnless(rhs_lines[0][2] == 'one:%s' % files[0][1]) |
| 201 |
| 202 self.failUnless(rhs_lines[1][0].LocalPath() == files[0][1]) |
| 203 self.failUnless(rhs_lines[1][1] == 2) |
| 204 self.failUnless(rhs_lines[1][2] == 'two:%s' % files[0][1]) |
| 205 |
| 206 self.failUnless(rhs_lines[2][0].LocalPath() == files[3][1]) |
| 207 self.failUnless(rhs_lines[2][1] == 1) |
| 208 self.failUnless(rhs_lines[2][2] == 'one:%s' % files[3][1]) |
| 209 |
| 210 self.failUnless(rhs_lines[3][0].LocalPath() == files[3][1]) |
| 211 self.failUnless(rhs_lines[3][1] == 2) |
| 212 self.failUnless(rhs_lines[3][2] == 'two:%s' % files[3][1]) |
| 213 |
| 214 def testAffectedFile(self): |
| 215 af = presubmit.AffectedFile('foo/blat.cc', 'M') |
| 216 self.failUnless(af.ServerPath() == 'svn:/foo/foo/blat.cc') |
| 217 self.failUnless(af.LocalPath() == presubmit.normpath('foo/blat.cc')) |
| 218 self.failUnless(af.Action() == 'M') |
| 219 self.failUnless(af.NewContents() == ['one:%s' % af.LocalPath(), |
| 220 'two:%s' % af.LocalPath()]) |
| 221 |
| 222 af = presubmit.AffectedFile('notfound.cc', 'A') |
| 223 self.failUnless(af.ServerPath() == '') |
| 224 |
| 225 def testExecPresubmitScript(self): |
| 226 description_lines = ('Hello there', |
| 227 'this is a change', |
| 228 'STORY=http://tracker/123') |
| 229 files = [ |
| 230 ['A', 'foo\\blat.cc'], |
| 231 ] |
| 232 ci = gcl.ChangeInfo(name='mychange', |
| 233 description='\n'.join(description_lines), |
| 234 files=files) |
| 235 |
| 236 executer = presubmit.PresubmitExecuter(ci, False) |
| 237 self.failIf(executer.ExecPresubmitScript('', 'PRESUBMIT.py')) |
| 238 # No error if no on-upload entry point |
| 239 self.failIf(executer.ExecPresubmitScript( |
| 240 ('def CheckChangeOnCommit(input_api, output_api):\n' |
| 241 ' return (output_api.PresubmitError("!!"))\n'), |
| 242 'PRESUBMIT.py' |
| 243 )) |
| 244 |
| 245 executer = presubmit.PresubmitExecuter(ci, True) |
| 246 # No error if no on-commit entry point |
| 247 self.failIf(executer.ExecPresubmitScript( |
| 248 ('def CheckChangeOnUpload(input_api, output_api):\n' |
| 249 ' return (output_api.PresubmitError("!!"))\n'), |
| 250 'PRESUBMIT.py' |
| 251 )) |
| 252 |
| 253 self.failIf(executer.ExecPresubmitScript( |
| 254 ('def CheckChangeOnUpload(input_api, output_api):\n' |
| 255 ' if not input_api.change.STORY:\n' |
| 256 ' return (output_api.PresubmitError("!!"))\n' |
| 257 ' else:\n' |
| 258 ' return ()'), |
| 259 'PRESUBMIT.py' |
| 260 )) |
| 261 |
| 262 self.failUnless(executer.ExecPresubmitScript( |
| 263 ('def CheckChangeOnCommit(input_api, output_api):\n' |
| 264 ' if not input_api.change.NOSUCHKEY:\n' |
| 265 ' return [output_api.PresubmitError("!!")]\n' |
| 266 ' else:\n' |
| 267 ' return ()'), |
| 268 'PRESUBMIT.py' |
| 269 )) |
| 270 |
| 271 try: |
| 272 executer.ExecPresubmitScript( |
| 273 ('def CheckChangeOnCommit(input_api, output_api):\n' |
| 274 ' return "foo"'), |
| 275 'PRESUBMIT.py') |
| 276 self.fail() |
| 277 except: |
| 278 pass # expected case |
| 279 |
| 280 try: |
| 281 executer.ExecPresubmitScript( |
| 282 ('def CheckChangeOnCommit(input_api, output_api):\n' |
| 283 ' return ["foo"]'), |
| 284 'PRESUBMIT.py') |
| 285 self.fail() |
| 286 except: |
| 287 pass # expected case |
| 288 |
| 289 def testDoPresubmitChecks(self): |
| 290 description_lines = ('Hello there', |
| 291 'this is a change', |
| 292 'STORY=http://tracker/123') |
| 293 files = [ |
| 294 ['A', 'haspresubmit\\blat.cc'], |
| 295 ] |
| 296 ci = gcl.ChangeInfo(name='mychange', |
| 297 description='\n'.join(description_lines), |
| 298 files=files) |
| 299 |
| 300 output = StringIO.StringIO() |
| 301 input = StringIO.StringIO('y\n') |
| 302 |
| 303 self.failIf(presubmit.DoPresubmitChecks(ci, False, False, output, input, |
| 304 None)) |
| 305 self.assertEqual(output.getvalue().count('!!'), 2) |
| 306 |
| 307 def testDoPresubmitChecksPromptsAfterWarnings(self): |
| 308 description_lines = ('Hello there', |
| 309 'this is a change', |
| 310 'NOSUCHKEY=http://tracker/123') |
| 311 files = [ |
| 312 ['A', 'haspresubmit\\blat.cc'], |
| 313 ] |
| 314 ci = gcl.ChangeInfo(name='mychange', |
| 315 description='\n'.join(description_lines), |
| 316 files=files) |
| 317 |
| 318 output = StringIO.StringIO() |
| 319 input = StringIO.StringIO('n\n') # say no to the warning |
| 320 |
| 321 self.failIf(presubmit.DoPresubmitChecks(ci, False, False, output, input, |
| 322 None)) |
| 323 self.assertEqual(output.getvalue().count('??'), 2) |
| 324 |
| 325 output = StringIO.StringIO() |
| 326 input = StringIO.StringIO('y\n') # say yes to the warning |
| 327 |
| 328 self.failUnless(presubmit.DoPresubmitChecks(ci, |
| 329 False, |
| 330 False, |
| 331 output, |
| 332 input, |
| 333 None)) |
| 334 self.failUnless(output.getvalue().count('??')) |
| 335 |
| 336 def testDoPresubmitChecksNoWarningPromptIfErrors(self): |
| 337 description_lines = ('Hello there', |
| 338 'this is a change', |
| 339 'NOSUCHKEY=http://tracker/123', |
| 340 'REALLYNOSUCHKEY=http://tracker/123') |
| 341 files = [ |
| 342 ['A', 'haspresubmit\\blat.cc'], |
| 343 ] |
| 344 ci = gcl.ChangeInfo(name='mychange', |
| 345 description='\n'.join(description_lines), |
| 346 files=files) |
| 347 |
| 348 output = StringIO.StringIO() |
| 349 input = StringIO.StringIO() # should be unused |
| 350 |
| 351 self.failIf(presubmit.DoPresubmitChecks(ci, False, False, output, input, |
| 352 None)) |
| 353 self.assertEqual(output.getvalue().count('??'), 2) |
| 354 self.assertEqual(output.getvalue().count('XX!!XX'), 2) |
| 355 self.assertEqual(output.getvalue().count('(y/N)'), 0) |
| 356 |
| 357 def testDoDefaultPresubmitChecks(self): |
| 358 description_lines = ('Hello there', |
| 359 'this is a change', |
| 360 'STORY=http://tracker/123') |
| 361 files = [ |
| 362 ['A', 'haspresubmit\\blat.cc'], |
| 363 ] |
| 364 ci = gcl.ChangeInfo(name='mychange', |
| 365 description='\n'.join(description_lines), |
| 366 files=files) |
| 367 |
| 368 output = StringIO.StringIO() |
| 369 input = StringIO.StringIO('y\n') |
| 370 DEFAULT_SCRIPT = """ |
| 371 def CheckChangeOnUpload(input_api, output_api): |
| 372 return [output_api.PresubmitError("!!")] |
| 373 """ |
| 374 def MockReadFile(dummy): |
| 375 return '' |
| 376 gcl.ReadFile = MockReadFile |
| 377 def MockIsFile(dummy): |
| 378 return False |
| 379 os.path.isfile = MockIsFile |
| 380 self.failUnless(presubmit.DoPresubmitChecks(ci, False, False, output, input, |
| 381 DEFAULT_SCRIPT)) |
| 382 self.failIf(output.getvalue().count('!!') == 1) |
| 383 |
| 384 def testDirectoryHandling(self): |
| 385 files = [ |
| 386 ['A', 'isdir'], |
| 387 ['A', 'isdir\\blat.cc'], |
| 388 ] |
| 389 ci = gcl.ChangeInfo(name='mychange', |
| 390 description='foo', |
| 391 files=files) |
| 392 change = presubmit.GclChange(ci) |
| 393 |
| 394 affected_files = change.AffectedFiles(include_dirs=False) |
| 395 self.failUnless(len(affected_files) == 1) |
| 396 self.failUnless(affected_files[0].LocalPath().endswith('blat.cc')) |
| 397 |
| 398 affected_files_and_dirs = change.AffectedFiles(include_dirs=True) |
| 399 self.failUnless(len(affected_files_and_dirs) == 2) |
| 400 |
| 401 def testSvnProperty(self): |
| 402 affected_file = presubmit.AffectedFile('foo.cc', 'A') |
| 403 self.failUnless(affected_file.SvnProperty('svn:secret-property') == |
| 404 'secret-property-value') |
| 405 |
| 406 |
| 407 class InputApiUnittest(PresubmitTestsBase): |
| 408 """Tests presubmit.InputApi.""" |
| 409 def testMembersChanged(self): |
| 410 members = [ |
| 411 'AbsoluteLocalPaths', 'AffectedFiles', 'AffectedTextFiles', |
| 412 'DepotToLocalPath', 'FilterTextFiles', 'LocalPaths', 'LocalToDepotPath', |
| 413 'PresubmitLocalPath', 'RightHandSideLines', 'ServerPaths', |
| 414 'basename', 'cPickle', 'cStringIO', 'canned_checks', 'change', |
| 415 'current_presubmit_path', 'marshal', 'os_path', 'pickle', 'platform', |
| 416 're', 'subprocess', 'tempfile', 'urllib2', |
| 417 ] |
| 418 # If this test fails, you should add the relevant test. |
| 419 self.compareMembers(presubmit.InputApi(None, None), members) |
| 420 |
| 421 def testDepotToLocalPath(self): |
| 422 path = presubmit.InputApi.DepotToLocalPath('svn:/foo/smurf') |
| 423 self.failUnless(path == 'smurf') |
| 424 path = presubmit.InputApi.DepotToLocalPath('svn:/foo/notfound/burp') |
| 425 self.failUnless(path == None) |
| 426 |
| 427 def testLocalToDepotPath(self): |
| 428 path = presubmit.InputApi.LocalToDepotPath('smurf') |
| 429 self.failUnless(path == 'svn:/foo/smurf') |
| 430 path = presubmit.InputApi.LocalToDepotPath('notfound-food') |
| 431 self.failUnless(path == None) |
| 432 |
| 433 def testInputApiConstruction(self): |
| 434 # Fudge the change object, it's not used during construction anyway |
| 435 api = presubmit.InputApi(change=42, presubmit_path='foo/path') |
| 436 self.failUnless(api.PresubmitLocalPath() == 'foo/path') |
| 437 self.failUnless(api.change == 42) |
| 438 |
| 439 def testFilterTextFiles(self): |
| 440 class MockAffectedFile(object): |
| 441 def __init__(self, path, action): |
| 442 self.path = path |
| 443 self.action = action |
| 444 def Action(self): |
| 445 return self.action |
| 446 def LocalPath(self): |
| 447 return self.path |
| 448 def AbsoluteLocalPath(self): |
| 449 return self.path |
| 450 |
| 451 list = [MockAffectedFile('foo/blat.txt', 'M'), |
| 452 MockAffectedFile('foo/binary.blob', 'M'), |
| 453 MockAffectedFile('blat/flop.txt', 'D')] |
| 454 |
| 455 output = presubmit.InputApi.FilterTextFiles(list, include_deletes=True) |
| 456 self.failUnless(len(output) == 2) |
| 457 self.failUnless(list[0] in output and list[2] in output) |
| 458 |
| 459 output = presubmit.InputApi.FilterTextFiles(list, include_deletes=False) |
| 460 self.failUnless(len(output) == 1) |
| 461 self.failUnless(list[0] in output) |
| 462 |
| 463 def testInputApiPresubmitScriptFiltering(self): |
| 464 description_lines = ('Hello there', |
| 465 'this is a change', |
| 466 'BUG=123', |
| 467 ' STORY =http://foo/ \t', |
| 468 'and some more regular text') |
| 469 files = [ |
| 470 ['A', os.path.join('foo', 'blat.cc')], |
| 471 ['M', os.path.join('foo', 'blat', 'binary.dll')], |
| 472 ['D', 'foo/mat/beingdeleted.txt'], |
| 473 ['M', 'flop/notfound.txt'], |
| 474 ['A', 'boo/flap.h'], |
| 475 ] |
| 476 |
| 477 ci = gcl.ChangeInfo(name='mychange', |
| 478 description='\n'.join(description_lines), |
| 479 files=files) |
| 480 change = presubmit.GclChange(ci) |
| 481 |
| 482 api = presubmit.InputApi(change, 'foo/PRESUBMIT.py') |
| 483 |
| 484 affected_files = api.AffectedFiles() |
| 485 self.failUnless(len(affected_files) == 3) |
| 486 self.failUnless(affected_files[0].LocalPath() == |
| 487 presubmit.normpath('foo/blat.cc')) |
| 488 self.failUnless(affected_files[1].LocalPath() == |
| 489 presubmit.normpath('foo/blat/binary.dll')) |
| 490 self.failUnless(affected_files[2].LocalPath() == |
| 491 presubmit.normpath('foo/mat/beingdeleted.txt')) |
| 492 |
| 493 rhs_lines = [] |
| 494 for line in api.RightHandSideLines(): |
| 495 rhs_lines.append(line) |
| 496 self.failUnless(len(rhs_lines) == 2) |
| 497 self.failUnless(rhs_lines[0][0].LocalPath() == |
| 498 presubmit.normpath('foo/blat.cc')) |
| 499 |
| 500 def testGetAbsoluteLocalPath(self): |
| 501 # Regression test for bug of presubmit stuff that relies on invoking |
| 502 # SVN (e.g. to get mime type of file) not working unless gcl invoked |
| 503 # from the client root (e.g. if you were at 'src' and did 'cd base' before |
| 504 # invoking 'gcl upload' it would fail because svn wouldn't find the files |
| 505 # the presubmit script was asking about). |
| 506 files = [ |
| 507 ['A', 'isdir'], |
| 508 ['A', os.path.join('isdir', 'blat.cc')] |
| 509 ] |
| 510 ci = gcl.ChangeInfo(name='mychange', |
| 511 description='', |
| 512 files=files) |
| 513 # It doesn't make sense on non-Windows platform. This is somewhat hacky, |
| 514 # but it is needed since we can't just use os.path.join('c:', 'temp'). |
| 515 change = presubmit.GclChange(ci, 'c:' + os.sep + 'temp') |
| 516 affected_files = change.AffectedFiles(include_dirs=True) |
| 517 # Local paths should remain the same |
| 518 self.failUnless(affected_files[0].LocalPath() == |
| 519 presubmit.normpath('isdir')) |
| 520 self.failUnless(affected_files[1].LocalPath() == |
| 521 presubmit.normpath('isdir/blat.cc')) |
| 522 # Absolute paths should be prefixed |
| 523 self.failUnless(affected_files[0].AbsoluteLocalPath() == |
| 524 presubmit.normpath('c:/temp/isdir')) |
| 525 self.failUnless(affected_files[1].AbsoluteLocalPath() == |
| 526 presubmit.normpath('c:/temp/isdir/blat.cc')) |
| 527 |
| 528 # New helper functions need to work |
| 529 absolute_paths_from_change = change.AbsoluteLocalPaths(include_dirs=True) |
| 530 api = presubmit.InputApi(change=change, presubmit_path='isdir/PRESUBMIT.py') |
| 531 absolute_paths_from_api = api.AbsoluteLocalPaths(include_dirs=True) |
| 532 for absolute_paths in [absolute_paths_from_change, |
| 533 absolute_paths_from_api]: |
| 534 self.failUnless(absolute_paths[0] == presubmit.normpath('c:/temp/isdir')) |
| 535 self.failUnless(absolute_paths[1] == |
| 536 presubmit.normpath('c:/temp/isdir/blat.cc')) |
| 537 |
| 538 |
| 539 class OuputApiUnittest(PresubmitTestsBase): |
| 540 """Tests presubmit.OutputApi.""" |
| 541 def testMembersChanged(self): |
| 542 members = [ |
| 543 'MailTextResult', 'PresubmitError', 'PresubmitNotifyResult', |
| 544 'PresubmitPromptWarning', 'PresubmitResult', |
| 545 ] |
| 546 # If this test fails, you should add the relevant test. |
| 547 self.compareMembers(presubmit.OutputApi(), members) |
| 548 |
| 549 def testOutputApiBasics(self): |
| 550 self.failUnless(presubmit.OutputApi.PresubmitError('').IsFatal()) |
| 551 self.failIf(presubmit.OutputApi.PresubmitError('').ShouldPrompt()) |
| 552 |
| 553 self.failIf(presubmit.OutputApi.PresubmitPromptWarning('').IsFatal()) |
| 554 self.failUnless( |
| 555 presubmit.OutputApi.PresubmitPromptWarning('').ShouldPrompt()) |
| 556 |
| 557 self.failIf(presubmit.OutputApi.PresubmitNotifyResult('').IsFatal()) |
| 558 self.failIf(presubmit.OutputApi.PresubmitNotifyResult('').ShouldPrompt()) |
| 559 |
| 560 # TODO(joi) Test MailTextResult once implemented. |
| 561 |
| 562 def testOutputApiHandling(self): |
| 563 output = StringIO.StringIO() |
| 564 unused_input = StringIO.StringIO() |
| 565 error = presubmit.OutputApi.PresubmitError('!!!') |
| 566 self.failIf(error._Handle(output, unused_input)) |
| 567 self.failUnless(output.getvalue().count('!!!')) |
| 568 |
| 569 output = StringIO.StringIO() |
| 570 notify = presubmit.OutputApi.PresubmitNotifyResult('?see?') |
| 571 self.failUnless(notify._Handle(output, unused_input)) |
| 572 self.failUnless(output.getvalue().count('?see?')) |
| 573 |
| 574 output = StringIO.StringIO() |
| 575 input = StringIO.StringIO('y') |
| 576 warning = presubmit.OutputApi.PresubmitPromptWarning('???') |
| 577 self.failUnless(warning._Handle(output, input)) |
| 578 self.failUnless(output.getvalue().count('???')) |
| 579 |
| 580 output = StringIO.StringIO() |
| 581 input = StringIO.StringIO('n') |
| 582 warning = presubmit.OutputApi.PresubmitPromptWarning('???') |
| 583 self.failIf(warning._Handle(output, input)) |
| 584 self.failUnless(output.getvalue().count('???')) |
| 585 |
| 586 output = StringIO.StringIO() |
| 587 input = StringIO.StringIO('\n') |
| 588 warning = presubmit.OutputApi.PresubmitPromptWarning('???') |
| 589 self.failIf(warning._Handle(output, input)) |
| 590 self.failUnless(output.getvalue().count('???')) |
| 591 |
| 592 |
| 593 class CannedChecksUnittest(PresubmitTestsBase): |
| 594 """Tests presubmit_canned_checks.py.""" |
| 595 class MockInputApi(object): |
| 596 class MockUrllib2(object): |
| 597 class urlopen(object): |
| 598 def __init__(self, url): |
| 599 if url == 'url_to_open': |
| 600 self.result = '1' |
| 601 else: |
| 602 self.result = '0' |
| 603 def read(self): |
| 604 return self.result |
| 605 def close(self): |
| 606 pass |
| 607 def __init__(self, lines=None): |
| 608 self.lines = lines |
| 609 self.basename = lambda x: x |
| 610 self.urllib2 = self.MockUrllib2() |
| 611 self.re = presubmit.re |
| 612 |
| 613 def RightHandSideLines(self): |
| 614 for line in self.lines: |
| 615 yield (presubmit.AffectedFile('bingo', 'M'), 1, line) |
| 616 |
| 617 def testMembersChanged(self): |
| 618 members = [ |
| 619 'CheckChangeHasNoTabs', 'CheckChangeHasQaField', |
| 620 'CheckChangeHasTestedField', 'CheckDoNotSubmit', |
| 621 'CheckDoNotSubmitInDescription', 'CheckDoNotSubmitInFiles', |
| 622 'CheckLongLines', 'CheckTreeIsOpen', |
| 623 ] |
| 624 # If this test fails, you should add the relevant test. |
| 625 self.compareMembers(presubmit_canned_checks, members) |
| 626 |
| 627 def testCannedCheckChangeHasTestedField(self): |
| 628 change = self.MakeBasicChange('foo', |
| 629 'Foo\nTESTED=did some stuff') |
| 630 api = presubmit.InputApi(change, 'PRESUBMIT.py') |
| 631 self.failIf(presubmit_canned_checks.CheckChangeHasTestedField( |
| 632 api, presubmit.OutputApi)) |
| 633 |
| 634 change = self.MakeBasicChange('foo', |
| 635 'Foo\nNEVERTESTED=did some stuff') |
| 636 api = presubmit.InputApi(change, 'PRESUBMIT.py') |
| 637 self.failUnless(presubmit_canned_checks.CheckChangeHasTestedField( |
| 638 api, presubmit.OutputApi)) |
| 639 |
| 640 def testCannedCheckChangeHasQAField(self): |
| 641 change = self.MakeBasicChange('foo', |
| 642 'Foo\nQA=test floop feature very well') |
| 643 api = presubmit.InputApi(change, 'PRESUBMIT.py') |
| 644 self.failIf(presubmit_canned_checks.CheckChangeHasQaField( |
| 645 api, presubmit.OutputApi)) |
| 646 |
| 647 change = self.MakeBasicChange('foo', |
| 648 'Foo\nNOTFORQA=test floop feature very well') |
| 649 api = presubmit.InputApi(change, 'PRESUBMIT.py') |
| 650 self.failUnless(presubmit_canned_checks.CheckChangeHasQaField( |
| 651 api, presubmit.OutputApi)) |
| 652 |
| 653 def testCannedCheckDoNotSubmitInDescription(self): |
| 654 change = self.MakeBasicChange('foo', 'hello') |
| 655 api = presubmit.InputApi(change, 'PRESUBMIT.py') |
| 656 self.failIf(presubmit_canned_checks.CheckDoNotSubmitInDescription( |
| 657 api, presubmit.OutputApi)) |
| 658 |
| 659 change = self.MakeBasicChange('foo', |
| 660 'DO NOT ' + 'SUBMIT') |
| 661 api = presubmit.InputApi(change, 'PRESUBMIT.py') |
| 662 self.failUnless(presubmit_canned_checks.CheckDoNotSubmitInDescription( |
| 663 api, presubmit.OutputApi)) |
| 664 |
| 665 def testCannedCheckDoNotSubmitInFiles(self): |
| 666 self.failIf(presubmit_canned_checks.CheckDoNotSubmitInFiles( |
| 667 self.MockInputApi(['hello', 'there']), presubmit.OutputApi |
| 668 )) |
| 669 self.failUnless(presubmit_canned_checks.CheckDoNotSubmitInFiles( |
| 670 self.MockInputApi(['hello', 'yo, DO NOT ' + 'SUBMIT']), |
| 671 presubmit.OutputApi)) |
| 672 |
| 673 def testCannedCheckChangeHasNoTabs(self): |
| 674 self.failIf(presubmit_canned_checks.CheckChangeHasNoTabs( |
| 675 self.MockInputApi(['hello', 'there']), presubmit.OutputApi |
| 676 )) |
| 677 self.failUnless(presubmit_canned_checks.CheckChangeHasNoTabs( |
| 678 self.MockInputApi(['hello', 'there\tit is']), presubmit.OutputApi |
| 679 )) |
| 680 |
| 681 def testCannedCheckLongLines(self): |
| 682 self.failIf(presubmit_canned_checks.CheckLongLines( |
| 683 self.MockInputApi(['hello', 'there']), presubmit.OutputApi, 5 |
| 684 )) |
| 685 self.failUnless(presubmit_canned_checks.CheckLongLines( |
| 686 self.MockInputApi(['hello', 'there!']), presubmit.OutputApi, 5 |
| 687 )) |
| 688 |
| 689 def testCannedCheckTreeIsOpen(self): |
| 690 self.failIf(presubmit_canned_checks.CheckTreeIsOpen( |
| 691 self.MockInputApi(), presubmit.OutputApi, url='url_to_open', closed='0' |
| 692 )) |
| 693 self.failUnless(presubmit_canned_checks.CheckTreeIsOpen( |
| 694 self.MockInputApi(), presubmit.OutputApi, url='url_to_closed', closed='0' |
| 695 )) |
| 696 |
| 697 |
| 698 if __name__ == '__main__': |
| 699 unittest.main() |
OLD | NEW |