presubmit_canned_checks_test.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2021 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. import os.path
  6. import subprocess
  7. import sys
  8. import unittest
  9. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  10. sys.path.insert(0, ROOT_DIR)
  11. from testing_support.presubmit_canned_checks_test_mocks import MockFile
  12. from testing_support.presubmit_canned_checks_test_mocks import (MockInputApi,
  13. MockOutputApi)
  14. from testing_support.presubmit_canned_checks_test_mocks import MockChange
  15. import presubmit_canned_checks
  16. # TODO: Should fix these warnings.
  17. # pylint: disable=line-too-long
  18. class InclusiveLanguageCheckTest(unittest.TestCase):
  19. def testBlockedTerms(self):
  20. input_api = MockInputApi()
  21. input_api.change.RepositoryRoot = lambda: ''
  22. input_api.presubmit_local_path = ''
  23. input_api.files = [
  24. MockFile(
  25. os.path.normpath(
  26. 'infra/inclusive_language_presubmit_exempt_dirs.txt'), [
  27. 'some/dir 2 1',
  28. 'some/other/dir 2 1',
  29. ]),
  30. MockFile(
  31. os.path.normpath('some/ios/file.mm'),
  32. [
  33. 'TEST(SomeClassTest, SomeInteraction, blacklist) {', # nocheck
  34. '}'
  35. ]),
  36. MockFile(
  37. os.path.normpath('some/mac/file.mm'),
  38. [
  39. 'TEST(SomeClassTest, SomeInteraction, BlackList) {', # nocheck
  40. '}'
  41. ]),
  42. MockFile(os.path.normpath('another/ios_file.mm'),
  43. ['class SomeTest : public testing::Test blocklist {};']),
  44. MockFile(os.path.normpath('some/ios/file_egtest.mm'),
  45. ['- (void)testSomething { V(whitelist); }']), # nocheck
  46. MockFile(
  47. os.path.normpath('some/ios/file_unittest.mm'),
  48. ['TEST_F(SomeTest, Whitelist) { V(allowlist); }']), # nocheck
  49. MockFile(
  50. os.path.normpath('some/doc/file.md'),
  51. [
  52. '# Title',
  53. 'Some markdown text includes master.', # nocheck
  54. ]),
  55. MockFile(
  56. os.path.normpath('some/doc/ok_file.md'),
  57. [
  58. '# Title',
  59. # This link contains a '//' which the matcher thinks is a
  60. # C-style comment, and the 'master' term appears after the
  61. # '//' in the URL, so it gets ignored as a side-effect.
  62. '[Ignored](https://git/project.git/+/master/foo)', # nocheck
  63. ]),
  64. MockFile(
  65. os.path.normpath('some/doc/branch_name_file.md'),
  66. [
  67. '# Title',
  68. # Matches appearing before `//` still trigger the check.
  69. '[src/master](https://git/p.git/+/master/foo)', # nocheck
  70. ]),
  71. MockFile(
  72. os.path.normpath('some/java/file/TestJavaDoc.java'),
  73. [
  74. '/**',
  75. ' * This line contains the word master,', # nocheck
  76. '* ignored because this is a comment. See {@link',
  77. ' * https://s/src/+/master:tools/README.md}', # nocheck
  78. ' */'
  79. ]),
  80. MockFile(
  81. os.path.normpath('some/java/file/TestJava.java'),
  82. [
  83. 'class TestJava {',
  84. ' public String master;', # nocheck
  85. '}'
  86. ]),
  87. MockFile(
  88. os.path.normpath('some/html/file.html'),
  89. [
  90. '<-- an existing html multiline comment',
  91. 'says "master" here', # nocheck
  92. 'in the comment -->'
  93. ])
  94. ]
  95. errors = presubmit_canned_checks.CheckInclusiveLanguage(
  96. input_api, MockOutputApi())
  97. self.assertEqual(1, len(errors))
  98. self.assertTrue(
  99. os.path.normpath('some/ios/file.mm') in errors[0].message)
  100. self.assertTrue(
  101. os.path.normpath('another/ios_file.mm') not in errors[0].message)
  102. self.assertTrue(
  103. os.path.normpath('some/mac/file.mm') in errors[0].message)
  104. self.assertTrue(
  105. os.path.normpath('some/ios/file_egtest.mm') in errors[0].message)
  106. self.assertTrue(
  107. os.path.normpath('some/ios/file_unittest.mm') in errors[0].message)
  108. self.assertTrue(
  109. os.path.normpath('some/doc/file.md') not in errors[0].message)
  110. self.assertTrue(
  111. os.path.normpath('some/doc/ok_file.md') not in errors[0].message)
  112. self.assertTrue(
  113. os.path.normpath('some/doc/branch_name_file.md') not in
  114. errors[0].message)
  115. self.assertTrue(
  116. os.path.normpath('some/java/file/TestJavaDoc.java') not in
  117. errors[0].message)
  118. self.assertTrue(
  119. os.path.normpath('some/java/file/TestJava.java') not in
  120. errors[0].message)
  121. self.assertTrue(
  122. os.path.normpath('some/html/file.html') not in errors[0].message)
  123. def testBlockedTermsWithLegacy(self):
  124. input_api = MockInputApi()
  125. input_api.change.RepositoryRoot = lambda: ''
  126. input_api.presubmit_local_path = ''
  127. input_api.files = [
  128. MockFile(
  129. os.path.normpath(
  130. 'infra/inclusive_language_presubmit_exempt_dirs.txt'), [
  131. 'some/ios 2 1',
  132. 'some/other/dir 2 1',
  133. ]),
  134. MockFile(
  135. os.path.normpath('some/ios/file.mm'),
  136. [
  137. 'TEST(SomeClassTest, SomeInteraction, blacklist) {', # nocheck
  138. '}'
  139. ]),
  140. MockFile(
  141. os.path.normpath('some/ios/subdir/file.mm'),
  142. [
  143. 'TEST(SomeClassTest, SomeInteraction, blacklist) {', # nocheck
  144. '}'
  145. ]),
  146. MockFile(
  147. os.path.normpath('some/mac/file.mm'),
  148. [
  149. 'TEST(SomeClassTest, SomeInteraction, BlackList) {', # nocheck
  150. '}'
  151. ]),
  152. MockFile(os.path.normpath('another/ios_file.mm'),
  153. ['class SomeTest : public testing::Test blocklist {};']),
  154. MockFile(os.path.normpath('some/ios/file_egtest.mm'),
  155. ['- (void)testSomething { V(whitelist); }']), # nocheck
  156. MockFile(
  157. os.path.normpath('some/ios/file_unittest.mm'),
  158. ['TEST_F(SomeTest, Whitelist) { V(allowlist); }']), # nocheck
  159. ]
  160. errors = presubmit_canned_checks.CheckInclusiveLanguage(
  161. input_api, MockOutputApi())
  162. self.assertEqual(1, len(errors))
  163. self.assertTrue(
  164. os.path.normpath('some/ios/file.mm') not in errors[0].message)
  165. self.assertTrue(
  166. os.path.normpath('some/ios/subdir/file.mm') in errors[0].message)
  167. self.assertTrue(
  168. os.path.normpath('another/ios_file.mm') not in errors[0].message)
  169. self.assertTrue(
  170. os.path.normpath('some/mac/file.mm') in errors[0].message)
  171. self.assertTrue(
  172. os.path.normpath('some/ios/file_egtest.mm') not in
  173. errors[0].message)
  174. self.assertTrue(
  175. os.path.normpath('some/ios/file_unittest.mm') not in
  176. errors[0].message)
  177. def testBlockedTermsWithNocheck(self):
  178. input_api = MockInputApi()
  179. input_api.change.RepositoryRoot = lambda: ''
  180. input_api.presubmit_local_path = ''
  181. input_api.files = [
  182. MockFile(
  183. os.path.normpath(
  184. 'infra/inclusive_language_presubmit_exempt_dirs.txt'), [
  185. 'some/dir 2 1',
  186. 'some/other/dir 2 1',
  187. ]),
  188. MockFile(
  189. os.path.normpath('some/ios/file.mm'),
  190. [
  191. 'TEST(SomeClassTest, SomeInteraction, ',
  192. ' blacklist) { // nocheck', # nocheck
  193. '}'
  194. ]),
  195. MockFile(
  196. os.path.normpath('some/mac/file.mm'),
  197. [
  198. 'TEST(SomeClassTest, SomeInteraction, ',
  199. 'BlackList) { // nocheck', # nocheck
  200. '}'
  201. ]),
  202. MockFile(os.path.normpath('another/ios_file.mm'),
  203. ['class SomeTest : public testing::Test blocklist {};']),
  204. MockFile(os.path.normpath('some/ios/file_egtest.mm'),
  205. ['- (void)testSomething { ', 'V(whitelist); } // nocheck'
  206. ]), # nocheck
  207. MockFile(
  208. os.path.normpath('some/ios/file_unittest.mm'),
  209. [
  210. 'TEST_F(SomeTest, Whitelist) // nocheck', # nocheck
  211. ' { V(allowlist); }'
  212. ]),
  213. MockFile(
  214. os.path.normpath('some/doc/file.md'),
  215. [
  216. 'Master in markdown <!-- nocheck -->', # nocheck
  217. '## Subheading is okay'
  218. ]),
  219. MockFile(
  220. os.path.normpath('some/java/file/TestJava.java'),
  221. [
  222. 'class TestJava {',
  223. ' public String master; // nocheck', # nocheck
  224. '}'
  225. ]),
  226. MockFile(
  227. os.path.normpath('some/html/file.html'),
  228. [
  229. '<-- an existing html multiline comment',
  230. 'says "master" here --><!-- nocheck -->', # nocheck
  231. '<!-- in the comment -->'
  232. ])
  233. ]
  234. errors = presubmit_canned_checks.CheckInclusiveLanguage(
  235. input_api, MockOutputApi())
  236. self.assertEqual(0, len(errors))
  237. def testTopLevelDirExcempt(self):
  238. input_api = MockInputApi()
  239. input_api.change.RepositoryRoot = lambda: ''
  240. input_api.presubmit_local_path = ''
  241. input_api.files = [
  242. MockFile(
  243. os.path.normpath(
  244. 'infra/inclusive_language_presubmit_exempt_dirs.txt'), [
  245. '. 2 1',
  246. 'some/other/dir 2 1',
  247. ]),
  248. MockFile(
  249. os.path.normpath('presubmit_canned_checks_test.py'),
  250. [
  251. 'TEST(SomeClassTest, SomeInteraction, blacklist) {', # nocheck
  252. '}'
  253. ]),
  254. MockFile(
  255. os.path.normpath('presubmit_canned_checks.py'),
  256. ['- (void)testSth { V(whitelist); } // nocheck']), # nocheck
  257. ]
  258. errors = presubmit_canned_checks.CheckInclusiveLanguage(
  259. input_api, MockOutputApi())
  260. self.assertEqual(1, len(errors))
  261. self.assertTrue(
  262. os.path.normpath('presubmit_canned_checks_test.py') in
  263. errors[0].message)
  264. self.assertTrue(
  265. os.path.normpath('presubmit_canned_checks.py') not in
  266. errors[0].message)
  267. def testChangeIsForSomeOtherRepo(self):
  268. input_api = MockInputApi()
  269. input_api.change.RepositoryRoot = lambda: 'v8'
  270. input_api.presubmit_local_path = ''
  271. input_api.files = [
  272. MockFile(
  273. os.path.normpath('some_file'),
  274. [
  275. '# this is a blacklist', # nocheck
  276. ]),
  277. ]
  278. errors = presubmit_canned_checks.CheckInclusiveLanguage(
  279. input_api, MockOutputApi())
  280. self.assertEqual([], errors)
  281. class DescriptionChecksTest(unittest.TestCase):
  282. def testCheckDescriptionUsesColonInsteadOfEquals(self):
  283. input_api = MockInputApi()
  284. input_api.change.RepositoryRoot = lambda: ''
  285. input_api.presubmit_local_path = ''
  286. # Verify error in case of the attempt to use "Bug=".
  287. input_api.change = MockChange([], 'Broken description\nBug=123')
  288. errors = presubmit_canned_checks.CheckDescriptionUsesColonInsteadOfEquals(
  289. input_api, MockOutputApi())
  290. self.assertEqual(1, len(errors))
  291. self.assertTrue('Bug=' in errors[0].message)
  292. # Verify error in case of the attempt to use "Fixed=".
  293. input_api.change = MockChange([], 'Broken description\nFixed=123')
  294. errors = presubmit_canned_checks.CheckDescriptionUsesColonInsteadOfEquals(
  295. input_api, MockOutputApi())
  296. self.assertEqual(1, len(errors))
  297. self.assertTrue('Fixed=' in errors[0].message)
  298. # Verify error in case of the attempt to use the lower case "bug=".
  299. input_api.change = MockChange([],
  300. 'Broken description lowercase\nbug=123')
  301. errors = presubmit_canned_checks.CheckDescriptionUsesColonInsteadOfEquals(
  302. input_api, MockOutputApi())
  303. self.assertEqual(1, len(errors))
  304. self.assertTrue('Bug=' in errors[0].message)
  305. # Verify no error in case of "Bug:"
  306. input_api.change = MockChange([], 'Correct description\nBug: 123')
  307. errors = presubmit_canned_checks.CheckDescriptionUsesColonInsteadOfEquals(
  308. input_api, MockOutputApi())
  309. self.assertEqual(0, len(errors))
  310. # Verify no error in case of "Fixed:"
  311. input_api.change = MockChange([], 'Correct description\nFixed: 123')
  312. errors = presubmit_canned_checks.CheckDescriptionUsesColonInsteadOfEquals(
  313. input_api, MockOutputApi())
  314. self.assertEqual(0, len(errors))
  315. class ChromiumDependencyMetadataCheckTest(unittest.TestCase):
  316. def testDefaultFileFilter(self):
  317. """Checks the default file filter limits the scope to Chromium dependency
  318. metadata files.
  319. """
  320. input_api = MockInputApi()
  321. input_api.change.RepositoryRoot = lambda: ''
  322. input_api.files = [
  323. MockFile(os.path.normpath('foo/README.md'), ['Shipped: no?']),
  324. MockFile(os.path.normpath('foo/main.py'), ['Shipped: yes?']),
  325. ]
  326. results = presubmit_canned_checks.CheckChromiumDependencyMetadata(
  327. input_api, MockOutputApi())
  328. self.assertEqual(len(results), 0)
  329. def testSkipDeletedFiles(self):
  330. """Checks validation is skipped for deleted files."""
  331. input_api = MockInputApi()
  332. input_api.change.RepositoryRoot = lambda: ''
  333. input_api.files = [
  334. MockFile(os.path.normpath('foo/README.chromium'), ['No fields'],
  335. action='D'),
  336. ]
  337. results = presubmit_canned_checks.CheckChromiumDependencyMetadata(
  338. input_api, MockOutputApi())
  339. self.assertEqual(len(results), 0)
  340. def testFeedbackForNoMetadata(self):
  341. """Checks presubmit results are returned for files without any metadata."""
  342. input_api = MockInputApi()
  343. input_api.change.RepositoryRoot = lambda: ''
  344. input_api.files = [
  345. MockFile(os.path.normpath('foo/README.chromium'), ['No fields']),
  346. ]
  347. results = presubmit_canned_checks.CheckChromiumDependencyMetadata(
  348. input_api, MockOutputApi())
  349. self.assertEqual(len(results), 1)
  350. self.assertTrue("No dependency metadata" in results[0].message)
  351. def testFeedbackForInvalidMetadata(self):
  352. """Checks presubmit results are returned for files with invalid metadata."""
  353. input_api = MockInputApi()
  354. input_api.change.RepositoryRoot = lambda: ''
  355. test_file = MockFile(os.path.normpath('foo/README.chromium'),
  356. ['Shipped: yes?'])
  357. input_api.files = [test_file]
  358. results = presubmit_canned_checks.CheckChromiumDependencyMetadata(
  359. input_api, MockOutputApi())
  360. # There should be 9 results due to
  361. # - missing 5 mandatory fields: Name, URL, Version, License, and
  362. # Security Critical
  363. # - 1 error for insufficent versioning info
  364. # - missing 2 required fields: License File, and
  365. # License Android Compatible
  366. # - Shipped should be only 'yes' or 'no'.
  367. self.assertEqual(len(results), 9)
  368. # Check each presubmit result is associated with the test file.
  369. for result in results:
  370. self.assertEqual(len(result.items), 1)
  371. self.assertEqual(result.items[0], test_file)
  372. class CheckUpdateOwnersFileReferences(unittest.TestCase):
  373. def testShowsWarningIfDeleting(self):
  374. input_api = MockInputApi()
  375. input_api.files = [
  376. MockFile(os.path.normpath('foo/OWNERS'), [], [], action='D'),
  377. ]
  378. results = presubmit_canned_checks.CheckUpdateOwnersFileReferences(
  379. input_api, MockOutputApi())
  380. self.assertEqual(1, len(results))
  381. self.assertEqual('warning', results[0].type)
  382. self.assertEqual(1, len(results[0].items))
  383. def testShowsWarningIfMoving(self):
  384. input_api = MockInputApi()
  385. input_api.files = [
  386. MockFile(os.path.normpath('new_directory/OWNERS'), [], [],
  387. action='A'),
  388. MockFile(os.path.normpath('old_directory/OWNERS'), [], [],
  389. action='D'),
  390. ]
  391. results = presubmit_canned_checks.CheckUpdateOwnersFileReferences(
  392. input_api, MockOutputApi())
  393. self.assertEqual(1, len(results))
  394. self.assertEqual('warning', results[0].type)
  395. self.assertEqual(1, len(results[0].items))
  396. def testNoWarningIfAdding(self):
  397. input_api = MockInputApi()
  398. input_api.files = [
  399. MockFile(os.path.normpath('foo/OWNERS'), [], [], action='A'),
  400. ]
  401. results = presubmit_canned_checks.CheckUpdateOwnersFileReferences(
  402. input_api, MockOutputApi())
  403. self.assertEqual(0, len(results))
  404. if __name__ == '__main__':
  405. unittest.main()