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