func_test.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. # -*- coding: utf-8 -*-
  2. # SPDX-License-Identifier: GPL-2.0+
  3. #
  4. # Copyright 2017 Google, Inc
  5. #
  6. import contextlib
  7. import os
  8. import re
  9. import shutil
  10. import sys
  11. import tempfile
  12. import unittest
  13. from io import StringIO
  14. from patman import gitutil
  15. from patman import patchstream
  16. from patman import settings
  17. from patman import tools
  18. @contextlib.contextmanager
  19. def capture():
  20. import sys
  21. oldout,olderr = sys.stdout, sys.stderr
  22. try:
  23. out=[StringIO(), StringIO()]
  24. sys.stdout,sys.stderr = out
  25. yield out
  26. finally:
  27. sys.stdout,sys.stderr = oldout, olderr
  28. out[0] = out[0].getvalue()
  29. out[1] = out[1].getvalue()
  30. class TestFunctional(unittest.TestCase):
  31. def setUp(self):
  32. self.tmpdir = tempfile.mkdtemp(prefix='patman.')
  33. def tearDown(self):
  34. shutil.rmtree(self.tmpdir)
  35. @staticmethod
  36. def GetPath(fname):
  37. return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
  38. 'test', fname)
  39. @classmethod
  40. def GetText(self, fname):
  41. return open(self.GetPath(fname), encoding='utf-8').read()
  42. @classmethod
  43. def GetPatchName(self, subject):
  44. fname = re.sub('[ :]', '-', subject)
  45. return fname.replace('--', '-')
  46. def CreatePatchesForTest(self, series):
  47. cover_fname = None
  48. fname_list = []
  49. for i, commit in enumerate(series.commits):
  50. clean_subject = self.GetPatchName(commit.subject)
  51. src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
  52. fname = os.path.join(self.tmpdir, src_fname)
  53. shutil.copy(self.GetPath(src_fname), fname)
  54. fname_list.append(fname)
  55. if series.get('cover'):
  56. src_fname = '0000-cover-letter.patch'
  57. cover_fname = os.path.join(self.tmpdir, src_fname)
  58. fname = os.path.join(self.tmpdir, src_fname)
  59. shutil.copy(self.GetPath(src_fname), fname)
  60. return cover_fname, fname_list
  61. def testBasic(self):
  62. """Tests the basic flow of patman
  63. This creates a series from some hard-coded patches build from a simple
  64. tree with the following metadata in the top commit:
  65. Series-to: u-boot
  66. Series-prefix: RFC
  67. Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
  68. Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
  69. Series-version: 2
  70. Series-changes: 4
  71. - Some changes
  72. Cover-letter:
  73. test: A test patch series
  74. This is a test of how the cover
  75. leter
  76. works
  77. END
  78. and this in the first commit:
  79. Series-notes:
  80. some notes
  81. about some things
  82. from the first commit
  83. END
  84. Commit-notes:
  85. Some notes about
  86. the first commit
  87. END
  88. with the following commands:
  89. git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
  90. git format-patch --subject-prefix RFC --cover-letter HEAD~2
  91. mv 00* /path/to/tools/patman/test
  92. It checks these aspects:
  93. - git log can be processed by patchstream
  94. - emailing patches uses the correct command
  95. - CC file has information on each commit
  96. - cover letter has the expected text and subject
  97. - each patch has the correct subject
  98. - dry-run information prints out correctly
  99. - unicode is handled correctly
  100. - Series-to, Series-cc, Series-prefix, Cover-letter
  101. - Cover-letter-cc, Series-version, Series-changes, Series-notes
  102. - Commit-notes
  103. """
  104. process_tags = True
  105. ignore_bad_tags = True
  106. stefan = b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'.decode('utf-8')
  107. rick = 'Richard III <richard@palace.gov>'
  108. mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
  109. ed = b'Lond Edmund Blackadd\xc3\xabr <weasel@blackadder.org'.decode('utf-8')
  110. fred = 'Fred Bloggs <f.bloggs@napier.net>'
  111. add_maintainers = [stefan, rick]
  112. dry_run = True
  113. in_reply_to = mel
  114. count = 2
  115. settings.alias = {
  116. 'fdt': ['simon'],
  117. 'u-boot': ['u-boot@lists.denx.de'],
  118. 'simon': [ed],
  119. 'fred': [fred],
  120. }
  121. text = self.GetText('test01.txt')
  122. series = patchstream.GetMetaDataForTest(text)
  123. cover_fname, args = self.CreatePatchesForTest(series)
  124. with capture() as out:
  125. patchstream.FixPatches(series, args)
  126. if cover_fname and series.get('cover'):
  127. patchstream.InsertCoverLetter(cover_fname, series, count)
  128. series.DoChecks()
  129. cc_file = series.MakeCcFile(process_tags, cover_fname,
  130. not ignore_bad_tags, add_maintainers,
  131. None)
  132. cmd = gitutil.EmailPatches(series, cover_fname, args,
  133. dry_run, not ignore_bad_tags, cc_file,
  134. in_reply_to=in_reply_to, thread=None)
  135. series.ShowActions(args, cmd, process_tags)
  136. cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
  137. os.remove(cc_file)
  138. lines = out[0].splitlines()
  139. self.assertEqual('Cleaned %s patches' % len(series.commits), lines[0])
  140. self.assertEqual('Change log missing for v2', lines[1])
  141. self.assertEqual('Change log missing for v3', lines[2])
  142. self.assertEqual('Change log for unknown version v4', lines[3])
  143. self.assertEqual("Alias 'pci' not found", lines[4])
  144. self.assertIn('Dry run', lines[5])
  145. self.assertIn('Send a total of %d patches' % count, lines[7])
  146. line = 8
  147. for i, commit in enumerate(series.commits):
  148. self.assertEqual(' %s' % args[i], lines[line + 0])
  149. line += 1
  150. while 'Cc:' in lines[line]:
  151. line += 1
  152. self.assertEqual('To: u-boot@lists.denx.de', lines[line])
  153. self.assertEqual('Cc: %s' % tools.FromUnicode(stefan),
  154. lines[line + 1])
  155. self.assertEqual('Version: 3', lines[line + 2])
  156. self.assertEqual('Prefix:\t RFC', lines[line + 3])
  157. self.assertEqual('Cover: 4 lines', lines[line + 4])
  158. line += 5
  159. self.assertEqual(' Cc: %s' % fred, lines[line + 0])
  160. self.assertEqual(' Cc: %s' % tools.FromUnicode(ed),
  161. lines[line + 1])
  162. self.assertEqual(' Cc: %s' % tools.FromUnicode(mel),
  163. lines[line + 2])
  164. self.assertEqual(' Cc: %s' % rick, lines[line + 3])
  165. expected = ('Git command: git send-email --annotate '
  166. '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
  167. '--cc "%s" --cc-cmd "%s --cc-cmd %s" %s %s'
  168. % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
  169. ' '.join(args)))
  170. line += 4
  171. self.assertEqual(expected, tools.ToUnicode(lines[line]))
  172. self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)),
  173. tools.ToUnicode(cc_lines[0]))
  174. self.assertEqual(('%s %s\0%s\0%s\0%s' % (args[1], fred, ed, rick,
  175. stefan)), tools.ToUnicode(cc_lines[1]))
  176. expected = '''
  177. This is a test of how the cover
  178. leter
  179. works
  180. some notes
  181. about some things
  182. from the first commit
  183. Changes in v4:
  184. - Some changes
  185. Simon Glass (2):
  186. pci: Correct cast for sandbox
  187. fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
  188. cmd/pci.c | 3 ++-
  189. fs/fat/fat.c | 1 +
  190. lib/efi_loader/efi_memory.c | 1 +
  191. lib/fdtdec.c | 3 ++-
  192. 4 files changed, 6 insertions(+), 2 deletions(-)
  193. --\x20
  194. 2.7.4
  195. '''
  196. lines = open(cover_fname, encoding='utf-8').read().splitlines()
  197. self.assertEqual(
  198. 'Subject: [RFC PATCH v3 0/2] test: A test patch series',
  199. lines[3])
  200. self.assertEqual(expected.splitlines(), lines[7:])
  201. for i, fname in enumerate(args):
  202. lines = open(fname, encoding='utf-8').read().splitlines()
  203. subject = [line for line in lines if line.startswith('Subject')]
  204. self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
  205. subject[0][:18])
  206. if i == 0:
  207. # Check that we got our commit notes
  208. self.assertEqual('---', lines[17])
  209. self.assertEqual('Some notes about', lines[18])
  210. self.assertEqual('the first commit', lines[19])