presubmit_unittest.py 26 KB

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