git_footers_test.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #!/usr/bin/env vpython3
  2. """Tests for git_footers."""
  3. from io import StringIO
  4. import json
  5. import os
  6. import sys
  7. import unittest
  8. from unittest import mock
  9. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  10. import gclient_utils
  11. import git_footers
  12. class GitFootersTest(unittest.TestCase):
  13. _message = """
  14. This is my commit message. There are many like it, but this one is mine.
  15. My commit message is my best friend. It is my life.
  16. """
  17. _position = 'refs/heads/main@{#292272}'
  18. _position_footer = 'Cr-Commit-Position: %s\n' % _position
  19. def testFootersBasic(self):
  20. self.assertEqual(git_footers.split_footers('Not-A: footer'),
  21. (['Not-A: footer'], [], []))
  22. self.assertEqual(
  23. git_footers.split_footers('Header\n\nActual: footer'),
  24. (['Header', ''], ['Actual: footer'], [('Actual', 'footer')]))
  25. self.assertEqual(git_footers.split_footers('\nActual: footer'),
  26. ([''], ['Actual: footer'], [('Actual', 'footer')]))
  27. self.assertEqual(
  28. git_footers.split_footers('H\n\nBug:\nAlso: footer'),
  29. (['H', ''], ['Bug:', 'Also: footer'], [('Bug', ''),
  30. ('Also', 'footer')]))
  31. self.assertEqual(git_footers.split_footers('H\n\nBug: '),
  32. (['H', ''], ['Bug:'], [('Bug', '')]))
  33. self.assertEqual(git_footers.split_footers('H\n\nBug: 1234 '),
  34. (['H', ''], ['Bug: 1234'], [('Bug', '1234')]))
  35. self.assertEqual(
  36. git_footers.split_footers('H\n\nBug: 1234\nChange-Id: Ib4321 '),
  37. (['H', ''], ['Bug: 1234', 'Change-Id: Ib4321'
  38. ], [('Bug', '1234'), ('Change-Id', 'Ib4321')]))
  39. self.assertEqual(git_footers.parse_footers(self._message), {})
  40. self.assertEqual(
  41. git_footers.parse_footers(self._message + self._position_footer),
  42. {'Cr-Commit-Position': [self._position]})
  43. self.assertEqual(
  44. git_footers.parse_footers(self._message + self._position_footer +
  45. self._position_footer),
  46. {'Cr-Commit-Position': [self._position, self._position]})
  47. self.assertEqual(
  48. git_footers.parse_footers(self._message + 'Bug:\n' +
  49. self._position_footer),
  50. {
  51. 'Bug': [''],
  52. 'Cr-Commit-Position': [self._position]
  53. })
  54. def testSkippingBadFooterLines(self):
  55. message = ('Title.\n'
  56. '\n'
  57. 'Last: paragraph starts\n'
  58. 'It-may: contain\n'
  59. 'bad lines, which should be skipped\n'
  60. 'For: example\n'
  61. '(cherry picked from)\n'
  62. 'And-only-valid: footers taken')
  63. self.assertEqual(git_footers.split_footers(message), (['Title.', ''], [
  64. 'Last: paragraph starts', 'It-may: contain',
  65. 'bad lines, which should be skipped', 'For: example',
  66. '(cherry picked from)', 'And-only-valid: footers taken'
  67. ], [('Last', 'paragraph starts'), ('It-may', 'contain'),
  68. ('For', 'example'), ('And-only-valid', 'footers taken')]))
  69. self.assertEqual(
  70. git_footers.parse_footers(message), {
  71. 'Last': ['paragraph starts'],
  72. 'It-May': ['contain'],
  73. 'For': ['example'],
  74. 'And-Only-Valid': ['footers taken']
  75. })
  76. def testAvoidingURLs(self):
  77. message = ('Someone accidentally put a URL in the footers.\n'
  78. '\n'
  79. 'Followed: by\n'
  80. 'http://domain.tld\n'
  81. 'Some: footers')
  82. self.assertEqual(
  83. git_footers.split_footers(message),
  84. (['Someone accidentally put a URL in the footers.', ''], [
  85. 'Followed: by', 'http://domain.tld', 'Some: footers'
  86. ], [('Followed', 'by'), ('Some', 'footers')]))
  87. self.assertEqual(git_footers.parse_footers(message), {
  88. 'Followed': ['by'],
  89. 'Some': ['footers']
  90. })
  91. def testSplittingLastParagraph(self):
  92. message = ('Title.\n'
  93. '\n'
  94. 'The final paragraph has some normal text first.\n'
  95. 'Followed: by\n'
  96. 'nonsense trailers and\n'
  97. 'Some: footers')
  98. self.assertEqual(git_footers.split_footers(message), ([
  99. 'Title.', '', 'The final paragraph has some normal text first.', ''
  100. ], ['Followed: by', 'nonsense trailers and', 'Some: footers'
  101. ], [('Followed', 'by'), ('Some', 'footers')]))
  102. self.assertEqual(git_footers.parse_footers(message), {
  103. 'Followed': ['by'],
  104. 'Some': ['footers']
  105. })
  106. def testGetFooterChangeId(self):
  107. msg = '\n'.join([
  108. 'whatever',
  109. '',
  110. 'Change-Id: ignored',
  111. '', # Above is ignored because of this empty line.
  112. 'Change-Id: Ideadbeaf'
  113. ])
  114. self.assertEqual(['Ideadbeaf'], git_footers.get_footer_change_id(msg))
  115. self.assertEqual([],
  116. git_footers.get_footer_change_id(
  117. 'desc\nBUG=not-a-valid-footer\nChange-Id: Ixxx'))
  118. self.assertEqual(['Ixxx'],
  119. git_footers.get_footer_change_id(
  120. 'desc\nBUG=not-a-valid-footer\n\nChange-Id: Ixxx'))
  121. def testAddFooterChangeId(self):
  122. with self.assertRaises(AssertionError):
  123. git_footers.add_footer_change_id('Already has\n\nChange-Id: Ixxx',
  124. 'Izzz')
  125. self.assertEqual(
  126. git_footers.add_footer_change_id('header-only', 'Ixxx'),
  127. 'header-only\n\nChange-Id: Ixxx')
  128. self.assertEqual(
  129. git_footers.add_footer_change_id('header\n\nsome: footer', 'Ixxx'),
  130. 'header\n\nsome: footer\nChange-Id: Ixxx')
  131. self.assertEqual(
  132. git_footers.add_footer_change_id('header\n\nBUG: yy', 'Ixxx'),
  133. 'header\n\nBUG: yy\nChange-Id: Ixxx')
  134. self.assertEqual(
  135. git_footers.add_footer_change_id('header\n\nBUG: yy\nPos: 1',
  136. 'Ixxx'),
  137. 'header\n\nBUG: yy\nChange-Id: Ixxx\nPos: 1')
  138. self.assertEqual(
  139. git_footers.add_footer_change_id('header\n\nBUG: yy\n\nPos: 1',
  140. 'Ixxx'),
  141. 'header\n\nBUG: yy\n\nPos: 1\nChange-Id: Ixxx')
  142. # Special case: first line is never a footer, even if it looks line one.
  143. self.assertEqual(
  144. git_footers.add_footer_change_id('header: like footer', 'Ixxx'),
  145. 'header: like footer\n\nChange-Id: Ixxx')
  146. self.assertEqual(
  147. git_footers.add_footer_change_id('Header.\n\nBug: v8\nN=t\nT=z',
  148. 'Ix'),
  149. 'Header.\n\nBug: v8\nChange-Id: Ix\nN=t\nT=z')
  150. def testAddFooterChangeIdWithMultilineFooters(self):
  151. add_change_id = lambda lines: git_footers.add_footer_change_id(
  152. '\n'.join(lines), 'Ixxx')
  153. self.assertEqual(
  154. add_change_id([
  155. 'header',
  156. '',
  157. '',
  158. 'BUG: yy',
  159. 'Test: hello ',
  160. ' world',
  161. ]),
  162. '\n'.join([
  163. 'header',
  164. '',
  165. '',
  166. 'BUG: yy',
  167. 'Test: hello ',
  168. ' world',
  169. 'Change-Id: Ixxx',
  170. ]),
  171. )
  172. self.assertEqual(
  173. add_change_id([
  174. 'header',
  175. '',
  176. '',
  177. 'BUG: yy',
  178. 'Yeah: hello ',
  179. ' world',
  180. ]),
  181. '\n'.join([
  182. 'header',
  183. '',
  184. '',
  185. 'BUG: yy',
  186. 'Change-Id: Ixxx',
  187. 'Yeah: hello ',
  188. ' world',
  189. ]),
  190. )
  191. self.assertEqual(
  192. add_change_id([
  193. 'header',
  194. '',
  195. '',
  196. 'Something: ',
  197. ' looks great',
  198. 'BUG: yy',
  199. 'Test: hello ',
  200. ' world',
  201. ]),
  202. '\n'.join([
  203. 'header',
  204. '',
  205. '',
  206. 'Something: ',
  207. ' looks great',
  208. 'BUG: yy',
  209. 'Test: hello ',
  210. ' world',
  211. 'Change-Id: Ixxx',
  212. ]),
  213. )
  214. self.assertEqual(
  215. add_change_id([
  216. 'header',
  217. '',
  218. '',
  219. 'Something: ',
  220. 'BUG: yy',
  221. 'Something: ',
  222. ' looks great',
  223. 'Test: hello ',
  224. ' world',
  225. ]),
  226. '\n'.join([
  227. 'header',
  228. '',
  229. '',
  230. 'Something: ',
  231. 'BUG: yy',
  232. 'Something: ',
  233. ' looks great',
  234. 'Test: hello ',
  235. ' world',
  236. 'Change-Id: Ixxx',
  237. ]),
  238. )
  239. def testAddFooter(self):
  240. with self.assertRaises(ValueError):
  241. git_footers.add_footer('', 'Invalid Footer', 'Value')
  242. self.assertEqual(git_footers.add_footer('', 'Key', 'Value'),
  243. '\nKey: Value')
  244. self.assertEqual(
  245. git_footers.add_footer('Header with empty line.\n\n', 'Key',
  246. 'Value'),
  247. 'Header with empty line.\n\nKey: Value')
  248. self.assertEqual(
  249. git_footers.add_footer('Top\n\nSome: footer', 'Key', 'value'),
  250. 'Top\n\nSome: footer\nKey: value')
  251. self.assertEqual(
  252. git_footers.add_footer('Top\n\nSome: footer',
  253. 'Key',
  254. 'value',
  255. after_keys=['Any']),
  256. 'Top\n\nSome: footer\nKey: value')
  257. self.assertEqual(
  258. git_footers.add_footer('Top\n\nSome: footer',
  259. 'Key',
  260. 'value',
  261. after_keys=['Some']),
  262. 'Top\n\nSome: footer\nKey: value')
  263. self.assertEqual(
  264. git_footers.add_footer('Top\n\nSome: footer\nOther: footer',
  265. 'Key',
  266. 'value',
  267. after_keys=['Some']),
  268. 'Top\n\nSome: footer\nKey: value\nOther: footer')
  269. def testRemoveFooter(self):
  270. self.assertEqual(git_footers.remove_footer('message', 'Key'), 'message')
  271. self.assertEqual(
  272. git_footers.remove_footer('message\n\nSome: footer', 'Key'),
  273. 'message\n\nSome: footer')
  274. self.assertEqual(
  275. git_footers.remove_footer('message\n\nSome: footer\nKey: value',
  276. 'Key'), 'message\n\nSome: footer')
  277. self.assertEqual(
  278. git_footers.remove_footer(
  279. 'message\n\nKey: value\nSome: footer\nKey: value', 'Key'),
  280. 'message\n\nSome: footer')
  281. @unittest.skipIf(gclient_utils.IsEnvCog(),
  282. 'not supported in non-git environment')
  283. @mock.patch('sys.stdout', StringIO())
  284. @mock.patch(
  285. 'sys.stdin',
  286. StringIO('line\r\notherline\r\n\r\n\r\nFoo: baz\r\nStill: footer'))
  287. def testReadStdin(self):
  288. self.assertEqual(git_footers.main([]), 0)
  289. self.assertEqual(sys.stdout.getvalue(), 'Still: footer\nFoo: baz\n')
  290. @unittest.skipIf(gclient_utils.IsEnvCog(),
  291. 'not supported in non-git environment')
  292. @mock.patch('sys.stdin',
  293. StringIO('line\r\nany spaces\r\n\r\n\r\nFoo: 1\nBar: 2\nFoo: 3')
  294. )
  295. def testToJson(self):
  296. with gclient_utils.temporary_file() as tmp:
  297. self.assertEqual(git_footers.main(['--json', tmp]), 0)
  298. with open(tmp) as f:
  299. js = json.load(f)
  300. self.assertEqual(js, {'Foo': ['3', '1'], 'Bar': ['2']})
  301. if __name__ == '__main__':
  302. unittest.main()