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