blob: c25a47bdeb230766959bf0f0fa9b6ba93390e895 [file] [log] [blame]
Simon Glassdf1bc5c2017-05-29 15:31:31 -06001# -*- coding: utf-8 -*-
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glassdf1bc5c2017-05-29 15:31:31 -06003#
4# Copyright 2017 Google, Inc
5#
Simon Glassdf1bc5c2017-05-29 15:31:31 -06006
Simon Glasseb209e52020-10-29 21:46:15 -06007"""Functional tests for checking that patman behaves correctly"""
8
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -05009import contextlib
Simon Glassdf1bc5c2017-05-29 15:31:31 -060010import os
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050011import pathlib
Simon Glassdf1bc5c2017-05-29 15:31:31 -060012import re
13import shutil
14import sys
15import tempfile
16import unittest
17
Simon Glass3db916d2020-10-29 21:46:35 -060018
19from patman.commit import Commit
Simon Glass54f1c5b2020-07-05 21:41:50 -060020from patman import control
Simon Glassa997ea52020-04-17 18:09:04 -060021from patman import gitutil
22from patman import patchstream
Simon Glassa7fadab2020-10-29 21:46:26 -060023from patman.patchstream import PatchStream
Simon Glass3db916d2020-10-29 21:46:35 -060024from patman.series import Series
Simon Glassa997ea52020-04-17 18:09:04 -060025from patman import settings
Simon Glass54f1c5b2020-07-05 21:41:50 -060026from patman import terminal
Simon Glassa997ea52020-04-17 18:09:04 -060027from patman import tools
Simon Glass54f1c5b2020-07-05 21:41:50 -060028from patman.test_util import capture_sys_output
Simon Glassdf1bc5c2017-05-29 15:31:31 -060029
Tom Rini488ea972021-02-26 07:52:31 -050030import pygit2
31from patman import status
Simon Glassdf1bc5c2017-05-29 15:31:31 -060032
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050033PATMAN_DIR = pathlib.Path(__file__).parent
34TEST_DATA_DIR = PATMAN_DIR / 'test/'
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050035
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050036
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050037@contextlib.contextmanager
38def directory_excursion(directory):
39 """Change directory to `directory` for a limited to the context block."""
40 current = os.getcwd()
41 try:
42 os.chdir(directory)
43 yield
44 finally:
45 os.chdir(current)
46
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050047
Simon Glassdf1bc5c2017-05-29 15:31:31 -060048class TestFunctional(unittest.TestCase):
Simon Glasseb209e52020-10-29 21:46:15 -060049 """Functional tests for checking that patman behaves correctly"""
Simon Glass06202d62020-10-29 21:46:27 -060050 leb = (b'Lord Edmund Blackadd\xc3\xabr <weasel@blackadder.org>'.
51 decode('utf-8'))
Simon Glass3b762cc2020-10-29 21:46:28 -060052 fred = 'Fred Bloggs <f.bloggs@napier.net>'
53 joe = 'Joe Bloggs <joe@napierwallies.co.nz>'
54 mary = 'Mary Bloggs <mary@napierwallies.co.nz>'
Simon Glass3db916d2020-10-29 21:46:35 -060055 commits = None
56 patches = None
Simon Glass06202d62020-10-29 21:46:27 -060057
Simon Glassdf1bc5c2017-05-29 15:31:31 -060058 def setUp(self):
59 self.tmpdir = tempfile.mkdtemp(prefix='patman.')
Simon Glass54f1c5b2020-07-05 21:41:50 -060060 self.gitdir = os.path.join(self.tmpdir, 'git')
61 self.repo = None
Simon Glassdf1bc5c2017-05-29 15:31:31 -060062
63 def tearDown(self):
64 shutil.rmtree(self.tmpdir)
Simon Glass02811582022-01-29 14:14:18 -070065 terminal.set_print_test_mode(False)
Simon Glassdf1bc5c2017-05-29 15:31:31 -060066
67 @staticmethod
Simon Glasseb209e52020-10-29 21:46:15 -060068 def _get_path(fname):
69 """Get the path to a test file
70
71 Args:
72 fname (str): Filename to obtain
73
74 Returns:
75 str: Full path to file in the test directory
76 """
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050077 return TEST_DATA_DIR / fname
Simon Glassdf1bc5c2017-05-29 15:31:31 -060078
79 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -060080 def _get_text(cls, fname):
81 """Read a file as text
82
83 Args:
84 fname (str): Filename to read
85
86 Returns:
87 str: Contents of file
88 """
89 return open(cls._get_path(fname), encoding='utf-8').read()
Simon Glassdf1bc5c2017-05-29 15:31:31 -060090
91 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -060092 def _get_patch_name(cls, subject):
93 """Get the filename of a patch given its subject
94
95 Args:
96 subject (str): Patch subject
97
98 Returns:
99 str: Filename for that patch
100 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600101 fname = re.sub('[ :]', '-', subject)
102 return fname.replace('--', '-')
103
Simon Glasseb209e52020-10-29 21:46:15 -0600104 def _create_patches_for_test(self, series):
105 """Create patch files for use by tests
106
107 This copies patch files from the test directory as needed by the series
108
109 Args:
110 series (Series): Series containing commits to convert
111
112 Returns:
113 tuple:
114 str: Cover-letter filename, or None if none
115 fname_list: list of str, each a patch filename
116 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600117 cover_fname = None
118 fname_list = []
119 for i, commit in enumerate(series.commits):
Simon Glasseb209e52020-10-29 21:46:15 -0600120 clean_subject = self._get_patch_name(commit.subject)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600121 src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
122 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600123 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600124 fname_list.append(fname)
125 if series.get('cover'):
126 src_fname = '0000-cover-letter.patch'
127 cover_fname = os.path.join(self.tmpdir, src_fname)
128 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600129 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600130
131 return cover_fname, fname_list
132
Simon Glassd85bb8f2022-01-29 14:14:09 -0700133 def test_basic(self):
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600134 """Tests the basic flow of patman
135
136 This creates a series from some hard-coded patches build from a simple
137 tree with the following metadata in the top commit:
138
139 Series-to: u-boot
140 Series-prefix: RFC
Sean Andersondc1cd132021-10-22 19:07:04 -0400141 Series-postfix: some-branch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600142 Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
143 Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Sean Andersoncf13b862020-05-04 16:28:36 -0400144 Series-version: 3
145 Patch-cc: fred
146 Series-process-log: sort, uniq
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600147 Series-changes: 4
148 - Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400149 - Multi
150 line
151 change
152
153 Commit-changes: 2
154 - Changes only for this commit
155
Simon Glass6a222e62021-08-01 16:02:39 -0600156' Cover-changes: 4
Sean Andersoncf13b862020-05-04 16:28:36 -0400157 - Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600158
159 Cover-letter:
160 test: A test patch series
161 This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400162 letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600163 works
164 END
165
166 and this in the first commit:
167
Sean Andersoncf13b862020-05-04 16:28:36 -0400168 Commit-changes: 2
169 - second revision change
170
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600171 Series-notes:
172 some notes
173 about some things
174 from the first commit
175 END
176
177 Commit-notes:
178 Some notes about
179 the first commit
180 END
181
182 with the following commands:
183
184 git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
185 git format-patch --subject-prefix RFC --cover-letter HEAD~2
186 mv 00* /path/to/tools/patman/test
187
188 It checks these aspects:
189 - git log can be processed by patchstream
190 - emailing patches uses the correct command
191 - CC file has information on each commit
192 - cover letter has the expected text and subject
193 - each patch has the correct subject
194 - dry-run information prints out correctly
195 - unicode is handled correctly
Sean Andersondc1cd132021-10-22 19:07:04 -0400196 - Series-to, Series-cc, Series-prefix, Series-postfix, Cover-letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600197 - Cover-letter-cc, Series-version, Series-changes, Series-notes
198 - Commit-notes
199 """
200 process_tags = True
Simon Glass1f975b92021-01-23 08:56:15 -0700201 ignore_bad_tags = False
Simon Glass4f817892019-05-14 15:53:53 -0600202 stefan = b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600203 rick = 'Richard III <richard@palace.gov>'
Simon Glass4f817892019-05-14 15:53:53 -0600204 mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600205 add_maintainers = [stefan, rick]
206 dry_run = True
207 in_reply_to = mel
208 count = 2
209 settings.alias = {
Simon Glass95745aa2020-10-29 21:46:13 -0600210 'fdt': ['simon'],
211 'u-boot': ['u-boot@lists.denx.de'],
Simon Glass06202d62020-10-29 21:46:27 -0600212 'simon': [self.leb],
Simon Glass3b762cc2020-10-29 21:46:28 -0600213 'fred': [self.fred],
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600214 }
215
Simon Glasseb209e52020-10-29 21:46:15 -0600216 text = self._get_text('test01.txt')
Simon Glass93f61c02020-10-29 21:46:19 -0600217 series = patchstream.get_metadata_for_test(text)
Simon Glasseb209e52020-10-29 21:46:15 -0600218 cover_fname, args = self._create_patches_for_test(series)
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500219 get_maintainer_script = str(pathlib.Path(__file__).parent.parent.parent
220 / 'get_maintainer.pl') + ' --norolestats'
Simon Glass59a70bb2020-10-29 21:46:14 -0600221 with capture_sys_output() as out:
Simon Glass93f61c02020-10-29 21:46:19 -0600222 patchstream.fix_patches(series, args)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600223 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -0600224 patchstream.insert_cover_letter(cover_fname, series, count)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600225 series.DoChecks()
226 cc_file = series.MakeCcFile(process_tags, cover_fname,
Chris Packhamb84fb482018-06-07 20:45:06 +1200227 not ignore_bad_tags, add_maintainers,
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500228 None, get_maintainer_script)
Simon Glass761648b2022-01-29 14:14:11 -0700229 cmd = gitutil.email_patches(
Simon Glass95745aa2020-10-29 21:46:13 -0600230 series, cover_fname, args, dry_run, not ignore_bad_tags,
231 cc_file, in_reply_to=in_reply_to, thread=None)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600232 series.ShowActions(args, cmd, process_tags)
Simon Glassf544a2d2019-10-31 07:42:51 -0600233 cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600234 os.remove(cc_file)
235
Simon Glass42e3d392020-10-29 21:46:29 -0600236 lines = iter(out[0].getvalue().splitlines())
237 self.assertEqual('Cleaned %s patches' % len(series.commits),
238 next(lines))
239 self.assertEqual('Change log missing for v2', next(lines))
240 self.assertEqual('Change log missing for v3', next(lines))
241 self.assertEqual('Change log for unknown version v4', next(lines))
242 self.assertEqual("Alias 'pci' not found", next(lines))
243 self.assertIn('Dry run', next(lines))
244 self.assertEqual('', next(lines))
245 self.assertIn('Send a total of %d patches' % count, next(lines))
246 prev = next(lines)
247 for i, commit in enumerate(series.commits):
248 self.assertEqual(' %s' % args[i], prev)
249 while True:
250 prev = next(lines)
251 if 'Cc:' not in prev:
252 break
253 self.assertEqual('To: u-boot@lists.denx.de', prev)
Simon Glass9dfb3112020-11-08 20:36:18 -0700254 self.assertEqual('Cc: %s' % stefan, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600255 self.assertEqual('Version: 3', next(lines))
256 self.assertEqual('Prefix:\t RFC', next(lines))
Sean Andersondc1cd132021-10-22 19:07:04 -0400257 self.assertEqual('Postfix:\t some-branch', next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600258 self.assertEqual('Cover: 4 lines', next(lines))
259 self.assertEqual(' Cc: %s' % self.fred, next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700260 self.assertEqual(' Cc: %s' % self.leb,
Simon Glass42e3d392020-10-29 21:46:29 -0600261 next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700262 self.assertEqual(' Cc: %s' % mel, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600263 self.assertEqual(' Cc: %s' % rick, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600264 expected = ('Git command: git send-email --annotate '
265 '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
Simon Glass1ee91c12020-11-03 13:54:10 -0700266 '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600267 % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
Simon Glass4f817892019-05-14 15:53:53 -0600268 ' '.join(args)))
Simon Glass9dfb3112020-11-08 20:36:18 -0700269 self.assertEqual(expected, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600270
Simon Glass9dfb3112020-11-08 20:36:18 -0700271 self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0])
Simon Glass95745aa2020-10-29 21:46:13 -0600272 self.assertEqual(
Simon Glass3b762cc2020-10-29 21:46:28 -0600273 '%s %s\0%s\0%s\0%s' % (args[1], self.fred, self.leb, rick, stefan),
Simon Glass9dfb3112020-11-08 20:36:18 -0700274 cc_lines[1])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600275
276 expected = '''
277This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400278letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600279works
280
281some notes
282about some things
283from the first commit
284
285Changes in v4:
Sean Andersoncf13b862020-05-04 16:28:36 -0400286- Multi
287 line
288 change
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600289- Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400290- Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600291
292Simon Glass (2):
293 pci: Correct cast for sandbox
Siva Durga Prasad Paladugub3d55ea2018-07-16 15:56:11 +0530294 fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600295
296 cmd/pci.c | 3 ++-
297 fs/fat/fat.c | 1 +
298 lib/efi_loader/efi_memory.c | 1 +
299 lib/fdtdec.c | 3 ++-
300 4 files changed, 6 insertions(+), 2 deletions(-)
301
302--\x20
3032.7.4
304
305'''
Simon Glassf544a2d2019-10-31 07:42:51 -0600306 lines = open(cover_fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600307 self.assertEqual(
Sean Andersondc1cd132021-10-22 19:07:04 -0400308 'Subject: [RFC PATCH some-branch v3 0/2] test: A test patch series',
Simon Glass95745aa2020-10-29 21:46:13 -0600309 lines[3])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600310 self.assertEqual(expected.splitlines(), lines[7:])
311
312 for i, fname in enumerate(args):
Simon Glassf544a2d2019-10-31 07:42:51 -0600313 lines = open(fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600314 subject = [line for line in lines if line.startswith('Subject')]
315 self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
316 subject[0][:18])
Sean Andersoncf13b862020-05-04 16:28:36 -0400317
318 # Check that we got our commit notes
319 start = 0
320 expected = ''
321
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600322 if i == 0:
Sean Andersoncf13b862020-05-04 16:28:36 -0400323 start = 17
324 expected = '''---
325Some notes about
326the first commit
327
328(no changes since v2)
329
330Changes in v2:
331- second revision change'''
332 elif i == 1:
333 start = 17
334 expected = '''---
335
336Changes in v4:
337- Multi
338 line
339 change
340- Some changes
341
342Changes in v2:
343- Changes only for this commit'''
344
345 if expected:
346 expected = expected.splitlines()
347 self.assertEqual(expected, lines[start:(start+len(expected))])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600348
349 def make_commit_with_file(self, subject, body, fname, text):
350 """Create a file and add it to the git repo with a new commit
351
352 Args:
353 subject (str): Subject for the commit
354 body (str): Body text of the commit
355 fname (str): Filename of file to create
356 text (str): Text to put into the file
357 """
358 path = os.path.join(self.gitdir, fname)
Simon Glass80025522022-01-29 14:14:04 -0700359 tools.write_file(path, text, binary=False)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600360 index = self.repo.index
361 index.add(fname)
Simon Glass547cba62022-02-11 13:23:18 -0700362 # pylint doesn't seem to find this
363 # pylint: disable=E1101
Simon Glass95745aa2020-10-29 21:46:13 -0600364 author = pygit2.Signature('Test user', 'test@email.com')
Simon Glass54f1c5b2020-07-05 21:41:50 -0600365 committer = author
366 tree = index.write_tree()
367 message = subject + '\n' + body
368 self.repo.create_commit('HEAD', author, committer, message, tree,
369 [self.repo.head.target])
370
371 def make_git_tree(self):
372 """Make a simple git tree suitable for testing
373
374 It has three branches:
375 'base' has two commits: PCI, main
376 'first' has base as upstream and two more commits: I2C, SPI
377 'second' has base as upstream and three more: video, serial, bootm
378
379 Returns:
Simon Glasseb209e52020-10-29 21:46:15 -0600380 pygit2.Repository: repository
Simon Glass54f1c5b2020-07-05 21:41:50 -0600381 """
382 repo = pygit2.init_repository(self.gitdir)
383 self.repo = repo
384 new_tree = repo.TreeBuilder().write()
385
Simon Glass547cba62022-02-11 13:23:18 -0700386 # pylint doesn't seem to find this
387 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600388 author = pygit2.Signature('Test user', 'test@email.com')
389 committer = author
Simon Glasseb209e52020-10-29 21:46:15 -0600390 _ = repo.create_commit('HEAD', author, committer, 'Created master',
391 new_tree, [])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600392
393 self.make_commit_with_file('Initial commit', '''
394Add a README
395
396''', 'README', '''This is the README file
397describing this project
398in very little detail''')
399
400 self.make_commit_with_file('pci: PCI implementation', '''
401Here is a basic PCI implementation
402
403''', 'pci.c', '''This is a file
404it has some contents
405and some more things''')
406 self.make_commit_with_file('main: Main program', '''
407Hello here is the second commit.
408''', 'main.c', '''This is the main file
409there is very little here
410but we can always add more later
411if we want to
412
413Series-to: u-boot
414Series-cc: Barry Crump <bcrump@whataroa.nz>
415''')
416 base_target = repo.revparse_single('HEAD')
417 self.make_commit_with_file('i2c: I2C things', '''
418This has some stuff to do with I2C
419''', 'i2c.c', '''And this is the file contents
420with some I2C-related things in it''')
421 self.make_commit_with_file('spi: SPI fixes', '''
422SPI needs some fixes
423and here they are
Simon Glassd0a0a582020-10-29 21:46:36 -0600424
425Signed-off-by: %s
426
427Series-to: u-boot
428Commit-notes:
429title of the series
430This is the cover letter for the series
431with various details
432END
433''' % self.leb, 'spi.c', '''Some fixes for SPI in this
Simon Glass54f1c5b2020-07-05 21:41:50 -0600434file to make SPI work
435better than before''')
436 first_target = repo.revparse_single('HEAD')
437
438 target = repo.revparse_single('HEAD~2')
Simon Glass547cba62022-02-11 13:23:18 -0700439 # pylint doesn't seem to find this
440 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600441 repo.reset(target.oid, pygit2.GIT_CHECKOUT_FORCE)
442 self.make_commit_with_file('video: Some video improvements', '''
443Fix up the video so that
444it looks more purple. Purple is
445a very nice colour.
446''', 'video.c', '''More purple here
447Purple and purple
448Even more purple
449Could not be any more purple''')
450 self.make_commit_with_file('serial: Add a serial driver', '''
451Here is the serial driver
452for my chip.
453
454Cover-letter:
455Series for my board
456This series implements support
457for my glorious board.
458END
Simon Glassa80986c2020-10-29 21:46:16 -0600459Series-links: 183237
Simon Glass54f1c5b2020-07-05 21:41:50 -0600460''', 'serial.c', '''The code for the
461serial driver is here''')
462 self.make_commit_with_file('bootm: Make it boot', '''
463This makes my board boot
464with a fix to the bootm
465command
466''', 'bootm.c', '''Fix up the bootm
467command to make the code as
468complicated as possible''')
469 second_target = repo.revparse_single('HEAD')
470
471 repo.branches.local.create('first', first_target)
472 repo.config.set_multivar('branch.first.remote', '', '.')
473 repo.config.set_multivar('branch.first.merge', '', 'refs/heads/base')
474
475 repo.branches.local.create('second', second_target)
476 repo.config.set_multivar('branch.second.remote', '', '.')
477 repo.config.set_multivar('branch.second.merge', '', 'refs/heads/base')
478
479 repo.branches.local.create('base', base_target)
480 return repo
481
Simon Glassd85bb8f2022-01-29 14:14:09 -0700482 def test_branch(self):
Simon Glass54f1c5b2020-07-05 21:41:50 -0600483 """Test creating patches from a branch"""
484 repo = self.make_git_tree()
485 target = repo.lookup_reference('refs/heads/first')
Simon Glass547cba62022-02-11 13:23:18 -0700486 # pylint doesn't seem to find this
487 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600488 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
489 control.setup()
490 try:
491 orig_dir = os.getcwd()
492 os.chdir(self.gitdir)
493
494 # Check that it can detect the current branch
Simon Glass761648b2022-01-29 14:14:11 -0700495 self.assertEqual(2, gitutil.count_commits_to_branch(None))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600496 col = terminal.Color()
497 with capture_sys_output() as _:
498 _, cover_fname, patch_files = control.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600499 col, branch=None, count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100500 ignore_binary=False, signoff=True)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600501 self.assertIsNone(cover_fname)
502 self.assertEqual(2, len(patch_files))
Simon Glass2eb4da72020-07-05 21:41:51 -0600503
504 # Check that it can detect a different branch
Simon Glass761648b2022-01-29 14:14:11 -0700505 self.assertEqual(3, gitutil.count_commits_to_branch('second'))
Simon Glass2eb4da72020-07-05 21:41:51 -0600506 with capture_sys_output() as _:
507 _, cover_fname, patch_files = control.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600508 col, branch='second', count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100509 ignore_binary=False, signoff=True)
Simon Glass2eb4da72020-07-05 21:41:51 -0600510 self.assertIsNotNone(cover_fname)
511 self.assertEqual(3, len(patch_files))
Simon Glassb3bf4e12020-07-05 21:41:52 -0600512
513 # Check that it can skip patches at the end
514 with capture_sys_output() as _:
515 _, cover_fname, patch_files = control.prepare_patches(
516 col, branch='second', count=-1, start=0, end=1,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100517 ignore_binary=False, signoff=True)
Simon Glassb3bf4e12020-07-05 21:41:52 -0600518 self.assertIsNotNone(cover_fname)
519 self.assertEqual(2, len(patch_files))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600520 finally:
521 os.chdir(orig_dir)
Simon Glass06202d62020-10-29 21:46:27 -0600522
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500523 def test_custom_get_maintainer_script(self):
524 """Validate that a custom get_maintainer script gets used."""
525 self.make_git_tree()
526 with directory_excursion(self.gitdir):
527 # Setup git.
528 os.environ['GIT_CONFIG_GLOBAL'] = '/dev/null'
529 os.environ['GIT_CONFIG_SYSTEM'] = '/dev/null'
530 tools.run('git', 'config', 'user.name', 'Dummy')
531 tools.run('git', 'config', 'user.email', 'dumdum@dummy.com')
532 tools.run('git', 'branch', 'upstream')
533 tools.run('git', 'branch', '--set-upstream-to=upstream')
534 tools.run('git', 'add', '.')
535 tools.run('git', 'commit', '-m', 'new commit')
536
537 # Setup patman configuration.
538 with open('.patman', 'w', buffering=1) as f:
539 f.write('[settings]\n'
540 'get_maintainer_script: dummy-script.sh\n'
541 'check_patch: False\n')
542 with open('dummy-script.sh', 'w', buffering=1) as f:
543 f.write('#!/usr/bin/env python\n'
544 'print("hello@there.com")\n')
545 os.chmod('dummy-script.sh', 0x555)
546
547 # Finally, do the test
548 with capture_sys_output():
549 output = tools.run(PATMAN_DIR / 'patman', '--dry-run')
550 # Assert the email address is part of the dry-run
551 # output.
552 self.assertIn('hello@there.com', output)
553
Simon Glassd85bb8f2022-01-29 14:14:09 -0700554 def test_tags(self):
Simon Glass06202d62020-10-29 21:46:27 -0600555 """Test collection of tags in a patchstream"""
556 text = '''This is a patch
557
558Signed-off-by: Terminator
Simon Glass3b762cc2020-10-29 21:46:28 -0600559Reviewed-by: %s
560Reviewed-by: %s
Simon Glass06202d62020-10-29 21:46:27 -0600561Tested-by: %s
Simon Glass3b762cc2020-10-29 21:46:28 -0600562''' % (self.joe, self.mary, self.leb)
Simon Glass06202d62020-10-29 21:46:27 -0600563 pstrm = PatchStream.process_text(text)
564 self.assertEqual(pstrm.commit.rtags, {
Simon Glass3b762cc2020-10-29 21:46:28 -0600565 'Reviewed-by': {self.joe, self.mary},
Simon Glass06202d62020-10-29 21:46:27 -0600566 'Tested-by': {self.leb}})
Simon Glass3b762cc2020-10-29 21:46:28 -0600567
Simon Glassd85bb8f2022-01-29 14:14:09 -0700568 def test_invalid_tag(self):
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200569 """Test invalid tag in a patchstream"""
570 text = '''This is a patch
571
572Serie-version: 2
573'''
574 with self.assertRaises(ValueError) as exc:
575 pstrm = PatchStream.process_text(text)
576 self.assertEqual("Line 3: Invalid tag = 'Serie-version: 2'",
577 str(exc.exception))
578
Simon Glassd85bb8f2022-01-29 14:14:09 -0700579 def test_missing_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600580 """Test a missing END tag"""
581 text = '''This is a patch
582
583Cover-letter:
584This is the title
585missing END after this line
586Signed-off-by: Fred
587'''
588 pstrm = PatchStream.process_text(text)
589 self.assertEqual(["Missing 'END' in section 'cover'"],
590 pstrm.commit.warn)
591
Simon Glassd85bb8f2022-01-29 14:14:09 -0700592 def test_missing_blank_line(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600593 """Test a missing blank line after a tag"""
594 text = '''This is a patch
595
596Series-changes: 2
597- First line of changes
598- Missing blank line after this line
599Signed-off-by: Fred
600'''
601 pstrm = PatchStream.process_text(text)
602 self.assertEqual(["Missing 'blank line' in section 'Series-changes'"],
603 pstrm.commit.warn)
604
Simon Glassd85bb8f2022-01-29 14:14:09 -0700605 def test_invalid_commit_tag(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600606 """Test an invalid Commit-xxx tag"""
607 text = '''This is a patch
608
609Commit-fred: testing
610'''
611 pstrm = PatchStream.process_text(text)
612 self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn)
613
Simon Glassd85bb8f2022-01-29 14:14:09 -0700614 def test_self_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600615 """Test a tested by tag by this user"""
616 test_line = 'Tested-by: %s@napier.com' % os.getenv('USER')
617 text = '''This is a patch
618
619%s
620''' % test_line
621 pstrm = PatchStream.process_text(text)
622 self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn)
623
Simon Glassd85bb8f2022-01-29 14:14:09 -0700624 def test_space_before_tab(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600625 """Test a space before a tab"""
626 text = '''This is a patch
627
628+ \tSomething
629'''
630 pstrm = PatchStream.process_text(text)
631 self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn)
632
Simon Glassd85bb8f2022-01-29 14:14:09 -0700633 def test_lines_after_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600634 """Test detecting lines after TEST= line"""
635 text = '''This is a patch
636
637TEST=sometest
638more lines
639here
640'''
641 pstrm = PatchStream.process_text(text)
642 self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn)
643
Simon Glassd85bb8f2022-01-29 14:14:09 -0700644 def test_blank_line_at_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600645 """Test detecting a blank line at the end of a file"""
646 text = '''This is a patch
647
648diff --git a/lib/fdtdec.c b/lib/fdtdec.c
649index c072e54..942244f 100644
650--- a/lib/fdtdec.c
651+++ b/lib/fdtdec.c
652@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void)
653 }
654
655 gd->ram_size = (phys_size_t)(res.end - res.start + 1);
656- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size);
657+ debug("%s: Initial DRAM size %llx\n", __func__,
658+ (unsigned long long)gd->ram_size);
659+
660diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
661
662--
6632.7.4
664
665 '''
666 pstrm = PatchStream.process_text(text)
667 self.assertEqual(
668 ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"],
669 pstrm.commit.warn)
Simon Glass1c1f2072020-10-29 21:46:34 -0600670
Simon Glassd85bb8f2022-01-29 14:14:09 -0700671 def test_no_upstream(self):
Simon Glass1c1f2072020-10-29 21:46:34 -0600672 """Test CountCommitsToBranch when there is no upstream"""
673 repo = self.make_git_tree()
674 target = repo.lookup_reference('refs/heads/base')
Simon Glass547cba62022-02-11 13:23:18 -0700675 # pylint doesn't seem to find this
676 # pylint: disable=E1101
Simon Glass1c1f2072020-10-29 21:46:34 -0600677 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
678
679 # Check that it can detect the current branch
680 try:
681 orig_dir = os.getcwd()
682 os.chdir(self.gitdir)
683 with self.assertRaises(ValueError) as exc:
Simon Glass761648b2022-01-29 14:14:11 -0700684 gitutil.count_commits_to_branch(None)
Simon Glass1c1f2072020-10-29 21:46:34 -0600685 self.assertIn(
686 "Failed to determine upstream: fatal: no upstream configured for branch 'base'",
687 str(exc.exception))
688 finally:
689 os.chdir(orig_dir)
Simon Glass3db916d2020-10-29 21:46:35 -0600690
691 @staticmethod
Simon Glassf9b03cf2020-11-03 13:54:14 -0700692 def _fake_patchwork(url, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600693 """Fake Patchwork server for the function below
694
695 This handles accessing a series, providing a list consisting of a
696 single patch
Simon Glassf9b03cf2020-11-03 13:54:14 -0700697
698 Args:
699 url (str): URL of patchwork server
700 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600701 """
702 re_series = re.match(r'series/(\d*)/$', subpath)
703 if re_series:
704 series_num = re_series.group(1)
705 if series_num == '1234':
706 return {'patches': [
707 {'id': '1', 'name': 'Some patch'}]}
708 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
709
Simon Glassd85bb8f2022-01-29 14:14:09 -0700710 def test_status_mismatch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600711 """Test Patchwork patches not matching the series"""
712 series = Series()
713
714 with capture_sys_output() as (_, err):
Simon Glassf9b03cf2020-11-03 13:54:14 -0700715 status.collect_patches(series, 1234, None, self._fake_patchwork)
Simon Glass3db916d2020-10-29 21:46:35 -0600716 self.assertIn('Warning: Patchwork reports 1 patches, series has 0',
717 err.getvalue())
718
Simon Glassd85bb8f2022-01-29 14:14:09 -0700719 def test_status_read_patch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600720 """Test handling a single patch in Patchwork"""
721 series = Series()
722 series.commits = [Commit('abcd')]
723
Simon Glassf9b03cf2020-11-03 13:54:14 -0700724 patches = status.collect_patches(series, 1234, None,
725 self._fake_patchwork)
Simon Glass3db916d2020-10-29 21:46:35 -0600726 self.assertEqual(1, len(patches))
727 patch = patches[0]
728 self.assertEqual('1', patch.id)
729 self.assertEqual('Some patch', patch.raw_subject)
730
Simon Glassd85bb8f2022-01-29 14:14:09 -0700731 def test_parse_subject(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600732 """Test parsing of the patch subject"""
733 patch = status.Patch('1')
734
735 # Simple patch not in a series
736 patch.parse_subject('Testing')
737 self.assertEqual('Testing', patch.raw_subject)
738 self.assertEqual('Testing', patch.subject)
739 self.assertEqual(1, patch.seq)
740 self.assertEqual(1, patch.count)
741 self.assertEqual(None, patch.prefix)
742 self.assertEqual(None, patch.version)
743
744 # First patch in a series
745 patch.parse_subject('[1/2] Testing')
746 self.assertEqual('[1/2] Testing', patch.raw_subject)
747 self.assertEqual('Testing', patch.subject)
748 self.assertEqual(1, patch.seq)
749 self.assertEqual(2, patch.count)
750 self.assertEqual(None, patch.prefix)
751 self.assertEqual(None, patch.version)
752
753 # Second patch in a series
754 patch.parse_subject('[2/2] Testing')
755 self.assertEqual('Testing', patch.subject)
756 self.assertEqual(2, patch.seq)
757 self.assertEqual(2, patch.count)
758 self.assertEqual(None, patch.prefix)
759 self.assertEqual(None, patch.version)
760
761 # RFC patch
762 patch.parse_subject('[RFC,3/7] Testing')
763 self.assertEqual('Testing', patch.subject)
764 self.assertEqual(3, patch.seq)
765 self.assertEqual(7, patch.count)
766 self.assertEqual('RFC', patch.prefix)
767 self.assertEqual(None, patch.version)
768
769 # Version patch
770 patch.parse_subject('[v2,3/7] Testing')
771 self.assertEqual('Testing', patch.subject)
772 self.assertEqual(3, patch.seq)
773 self.assertEqual(7, patch.count)
774 self.assertEqual(None, patch.prefix)
775 self.assertEqual('v2', patch.version)
776
777 # All fields
778 patch.parse_subject('[RESEND,v2,3/7] Testing')
779 self.assertEqual('Testing', patch.subject)
780 self.assertEqual(3, patch.seq)
781 self.assertEqual(7, patch.count)
782 self.assertEqual('RESEND', patch.prefix)
783 self.assertEqual('v2', patch.version)
784
785 # RFC only
786 patch.parse_subject('[RESEND] Testing')
787 self.assertEqual('Testing', patch.subject)
788 self.assertEqual(1, patch.seq)
789 self.assertEqual(1, patch.count)
790 self.assertEqual('RESEND', patch.prefix)
791 self.assertEqual(None, patch.version)
792
Simon Glassd85bb8f2022-01-29 14:14:09 -0700793 def test_compare_series(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600794 """Test operation of compare_with_series()"""
795 commit1 = Commit('abcd')
796 commit1.subject = 'Subject 1'
797 commit2 = Commit('ef12')
798 commit2.subject = 'Subject 2'
799 commit3 = Commit('3456')
800 commit3.subject = 'Subject 2'
801
802 patch1 = status.Patch('1')
803 patch1.subject = 'Subject 1'
804 patch2 = status.Patch('2')
805 patch2.subject = 'Subject 2'
806 patch3 = status.Patch('3')
807 patch3.subject = 'Subject 2'
808
809 series = Series()
810 series.commits = [commit1]
811 patches = [patch1]
812 patch_for_commit, commit_for_patch, warnings = (
813 status.compare_with_series(series, patches))
814 self.assertEqual(1, len(patch_for_commit))
815 self.assertEqual(patch1, patch_for_commit[0])
816 self.assertEqual(1, len(commit_for_patch))
817 self.assertEqual(commit1, commit_for_patch[0])
818
819 series.commits = [commit1]
820 patches = [patch1, patch2]
821 patch_for_commit, commit_for_patch, warnings = (
822 status.compare_with_series(series, patches))
823 self.assertEqual(1, len(patch_for_commit))
824 self.assertEqual(patch1, patch_for_commit[0])
825 self.assertEqual(1, len(commit_for_patch))
826 self.assertEqual(commit1, commit_for_patch[0])
827 self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"],
828 warnings)
829
830 series.commits = [commit1, commit2]
831 patches = [patch1]
832 patch_for_commit, commit_for_patch, warnings = (
833 status.compare_with_series(series, patches))
834 self.assertEqual(1, len(patch_for_commit))
835 self.assertEqual(patch1, patch_for_commit[0])
836 self.assertEqual(1, len(commit_for_patch))
837 self.assertEqual(commit1, commit_for_patch[0])
838 self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"],
839 warnings)
840
841 series.commits = [commit1, commit2, commit3]
842 patches = [patch1, patch2]
843 patch_for_commit, commit_for_patch, warnings = (
844 status.compare_with_series(series, patches))
845 self.assertEqual(2, len(patch_for_commit))
846 self.assertEqual(patch1, patch_for_commit[0])
847 self.assertEqual(patch2, patch_for_commit[1])
848 self.assertEqual(1, len(commit_for_patch))
849 self.assertEqual(commit1, commit_for_patch[0])
850 self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')",
851 "Multiple commits match patch 2 ('Subject 2'):\n"
852 ' Subject 2\n Subject 2'],
853 warnings)
854
855 series.commits = [commit1, commit2]
856 patches = [patch1, patch2, patch3]
857 patch_for_commit, commit_for_patch, warnings = (
858 status.compare_with_series(series, patches))
859 self.assertEqual(1, len(patch_for_commit))
860 self.assertEqual(patch1, patch_for_commit[0])
861 self.assertEqual(2, len(commit_for_patch))
862 self.assertEqual(commit1, commit_for_patch[0])
863 self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n"
864 ' Subject 2\n Subject 2',
865 "Cannot find commit for patch 3 ('Subject 2')"],
866 warnings)
867
Simon Glassf9b03cf2020-11-03 13:54:14 -0700868 def _fake_patchwork2(self, url, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600869 """Fake Patchwork server for the function below
870
871 This handles accessing series, patches and comments, providing the data
872 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700873
874 Args:
875 url (str): URL of patchwork server
876 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600877 """
878 re_series = re.match(r'series/(\d*)/$', subpath)
879 re_patch = re.match(r'patches/(\d*)/$', subpath)
880 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
881 if re_series:
882 series_num = re_series.group(1)
883 if series_num == '1234':
884 return {'patches': self.patches}
885 elif re_patch:
886 patch_num = int(re_patch.group(1))
887 patch = self.patches[patch_num - 1]
888 return patch
889 elif re_comments:
890 patch_num = int(re_comments.group(1))
891 patch = self.patches[patch_num - 1]
892 return patch.comments
893 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
894
Simon Glassd85bb8f2022-01-29 14:14:09 -0700895 def test_find_new_responses(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600896 """Test operation of find_new_responses()"""
897 commit1 = Commit('abcd')
898 commit1.subject = 'Subject 1'
899 commit2 = Commit('ef12')
900 commit2.subject = 'Subject 2'
901
902 patch1 = status.Patch('1')
903 patch1.parse_subject('[1/2] Subject 1')
904 patch1.name = patch1.raw_subject
905 patch1.content = 'This is my patch content'
906 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
907
908 patch1.comments = [comment1a]
909
910 patch2 = status.Patch('2')
911 patch2.parse_subject('[2/2] Subject 2')
912 patch2.name = patch2.raw_subject
913 patch2.content = 'Some other patch content'
914 comment2a = {
915 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
916 (self.mary, self.leb)}
917 comment2b = {'content': 'Reviewed-by: %s' % self.fred}
918 patch2.comments = [comment2a, comment2b]
919
920 # This test works by setting up commits and patch for use by the fake
921 # Rest API function _fake_patchwork2(). It calls various functions in
922 # the status module after setting up tags in the commits, checking that
923 # things behaves as expected
924 self.commits = [commit1, commit2]
925 self.patches = [patch1, patch2]
926 count = 2
927 new_rtag_list = [None] * count
Simon Glass2112d072020-10-29 21:46:38 -0600928 review_list = [None, None]
Simon Glass3db916d2020-10-29 21:46:35 -0600929
930 # Check that the tags are picked up on the first patch
Simon Glass2112d072020-10-29 21:46:38 -0600931 status.find_new_responses(new_rtag_list, review_list, 0, commit1,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700932 patch1, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600933 self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}})
934
935 # Now the second patch
Simon Glass2112d072020-10-29 21:46:38 -0600936 status.find_new_responses(new_rtag_list, review_list, 1, commit2,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700937 patch2, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600938 self.assertEqual(new_rtag_list[1], {
939 'Reviewed-by': {self.mary, self.fred},
940 'Tested-by': {self.leb}})
941
942 # Now add some tags to the commit, which means they should not appear as
943 # 'new' tags when scanning comments
944 new_rtag_list = [None] * count
945 commit1.rtags = {'Reviewed-by': {self.joe}}
Simon Glass2112d072020-10-29 21:46:38 -0600946 status.find_new_responses(new_rtag_list, review_list, 0, commit1,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700947 patch1, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600948 self.assertEqual(new_rtag_list[0], {})
949
950 # For the second commit, add Ed and Fred, so only Mary should be left
951 commit2.rtags = {
952 'Tested-by': {self.leb},
953 'Reviewed-by': {self.fred}}
Simon Glass2112d072020-10-29 21:46:38 -0600954 status.find_new_responses(new_rtag_list, review_list, 1, commit2,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700955 patch2, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600956 self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}})
957
958 # Check that the output patches expectations:
959 # 1 Subject 1
960 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
961 # 2 Subject 2
962 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
963 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
964 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
965 # 1 new response available in patchwork
966
967 series = Series()
968 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -0700969 terminal.set_print_test_mode()
Simon Glass2112d072020-10-29 21:46:38 -0600970 status.check_patchwork_status(series, '1234', None, None, False, False,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700971 None, self._fake_patchwork2)
Simon Glass02811582022-01-29 14:14:18 -0700972 lines = iter(terminal.get_print_test_lines())
Simon Glass3db916d2020-10-29 21:46:35 -0600973 col = terminal.Color()
974 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
975 next(lines))
976 self.assertEqual(
977 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
978 bright=False),
979 next(lines))
980 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False),
981 next(lines))
982
983 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE),
984 next(lines))
985 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -0600986 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -0600987 bright=False),
988 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -0600989 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -0600990 next(lines))
991 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -0600992 terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -0600993 bright=False),
994 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -0600995 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -0600996 next(lines))
997 self.assertEqual(
998 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
999 next(lines))
1000 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1001 next(lines))
1002 self.assertEqual(terminal.PrintLine(
Simon Glassd0a0a582020-10-29 21:46:36 -06001003 '1 new response available in patchwork (use -d to write them to a new branch)',
1004 None), next(lines))
1005
Simon Glassf9b03cf2020-11-03 13:54:14 -07001006 def _fake_patchwork3(self, url, subpath):
Simon Glassd0a0a582020-10-29 21:46:36 -06001007 """Fake Patchwork server for the function below
1008
1009 This handles accessing series, patches and comments, providing the data
1010 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -07001011
1012 Args:
1013 url (str): URL of patchwork server
1014 subpath (str): URL subpath to use
Simon Glassd0a0a582020-10-29 21:46:36 -06001015 """
1016 re_series = re.match(r'series/(\d*)/$', subpath)
1017 re_patch = re.match(r'patches/(\d*)/$', subpath)
1018 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
1019 if re_series:
1020 series_num = re_series.group(1)
1021 if series_num == '1234':
1022 return {'patches': self.patches}
1023 elif re_patch:
1024 patch_num = int(re_patch.group(1))
1025 patch = self.patches[patch_num - 1]
1026 return patch
1027 elif re_comments:
1028 patch_num = int(re_comments.group(1))
1029 patch = self.patches[patch_num - 1]
1030 return patch.comments
1031 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
1032
Simon Glassd85bb8f2022-01-29 14:14:09 -07001033 def test_create_branch(self):
Simon Glassd0a0a582020-10-29 21:46:36 -06001034 """Test operation of create_branch()"""
1035 repo = self.make_git_tree()
1036 branch = 'first'
1037 dest_branch = 'first2'
1038 count = 2
1039 gitdir = os.path.join(self.gitdir, '.git')
1040
1041 # Set up the test git tree. We use branch 'first' which has two commits
1042 # in it
1043 series = patchstream.get_metadata_for_list(branch, gitdir, count)
1044 self.assertEqual(2, len(series.commits))
1045
1046 patch1 = status.Patch('1')
1047 patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
1048 patch1.name = patch1.raw_subject
1049 patch1.content = 'This is my patch content'
1050 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1051
1052 patch1.comments = [comment1a]
1053
1054 patch2 = status.Patch('2')
1055 patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
1056 patch2.name = patch2.raw_subject
1057 patch2.content = 'Some other patch content'
1058 comment2a = {
1059 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1060 (self.mary, self.leb)}
1061 comment2b = {
1062 'content': 'Reviewed-by: %s' % self.fred}
1063 patch2.comments = [comment2a, comment2b]
1064
1065 # This test works by setting up patches for use by the fake Rest API
1066 # function _fake_patchwork3(). The fake patch comments above should
1067 # result in new review tags that are collected and added to the commits
1068 # created in the destination branch.
1069 self.patches = [patch1, patch2]
1070 count = 2
1071
1072 # Expected output:
1073 # 1 i2c: I2C things
1074 # + Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1075 # 2 spi: SPI fixes
1076 # + Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1077 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1078 # + Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1079 # 4 new responses available in patchwork
1080 # 4 responses added from patchwork into new branch 'first2'
1081 # <unittest.result.TestResult run=8 errors=0 failures=0>
1082
Simon Glass02811582022-01-29 14:14:18 -07001083 terminal.set_print_test_mode()
Simon Glassd0a0a582020-10-29 21:46:36 -06001084 status.check_patchwork_status(series, '1234', branch, dest_branch,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001085 False, False, None, self._fake_patchwork3,
1086 repo)
Simon Glass02811582022-01-29 14:14:18 -07001087 lines = terminal.get_print_test_lines()
Simon Glassd0a0a582020-10-29 21:46:36 -06001088 self.assertEqual(12, len(lines))
1089 self.assertEqual(
1090 "4 responses added from patchwork into new branch 'first2'",
1091 lines[11].text)
1092
1093 # Check that the destination branch has the new tags
1094 new_series = patchstream.get_metadata_for_list(dest_branch, gitdir,
1095 count)
1096 self.assertEqual(
1097 {'Reviewed-by': {self.joe}},
1098 new_series.commits[0].rtags)
1099 self.assertEqual(
1100 {'Tested-by': {self.leb},
1101 'Reviewed-by': {self.fred, self.mary}},
1102 new_series.commits[1].rtags)
1103
1104 # Now check the actual test of the first commit message. We expect to
1105 # see the new tags immediately below the old ones.
1106 stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir)
1107 lines = iter([line.strip() for line in stdout.splitlines()
1108 if '-by:' in line])
1109
1110 # First patch should have the review tag
1111 self.assertEqual('Reviewed-by: %s' % self.joe, next(lines))
1112
1113 # Second patch should have the sign-off then the tested-by and two
1114 # reviewed-by tags
1115 self.assertEqual('Signed-off-by: %s' % self.leb, next(lines))
1116 self.assertEqual('Reviewed-by: %s' % self.fred, next(lines))
1117 self.assertEqual('Reviewed-by: %s' % self.mary, next(lines))
1118 self.assertEqual('Tested-by: %s' % self.leb, next(lines))
Simon Glassda8a2922020-10-29 21:46:37 -06001119
Simon Glassd85bb8f2022-01-29 14:14:09 -07001120 def test_parse_snippets(self):
Simon Glassda8a2922020-10-29 21:46:37 -06001121 """Test parsing of review snippets"""
1122 text = '''Hi Fred,
1123
1124This is a comment from someone.
1125
1126Something else
1127
1128On some recent date, Fred wrote:
1129> This is why I wrote the patch
1130> so here it is
1131
1132Now a comment about the commit message
1133A little more to say
1134
1135Even more
1136
1137> diff --git a/file.c b/file.c
1138> Some more code
1139> Code line 2
1140> Code line 3
1141> Code line 4
1142> Code line 5
1143> Code line 6
1144> Code line 7
1145> Code line 8
1146> Code line 9
1147
1148And another comment
1149
Simon Glassd85bb8f2022-01-29 14:14:09 -07001150> @@ -153,8 +143,13 @@ def check_patch(fname, show_types=False):
Simon Glassda8a2922020-10-29 21:46:37 -06001151> further down on the file
1152> and more code
1153> +Addition here
1154> +Another addition here
1155> codey
1156> more codey
1157
1158and another thing in same file
1159
1160> @@ -253,8 +243,13 @@
1161> with no function context
1162
1163one more thing
1164
1165> diff --git a/tools/patman/main.py b/tools/patman/main.py
1166> +line of code
1167now a very long comment in a different file
1168line2
1169line3
1170line4
1171line5
1172line6
1173line7
1174line8
1175'''
1176 pstrm = PatchStream.process_text(text, True)
1177 self.assertEqual([], pstrm.commit.warn)
1178
1179 # We expect to the filename and up to 5 lines of code context before
1180 # each comment. The 'On xxx wrote:' bit should be removed.
1181 self.assertEqual(
1182 [['Hi Fred,',
1183 'This is a comment from someone.',
1184 'Something else'],
1185 ['> This is why I wrote the patch',
1186 '> so here it is',
1187 'Now a comment about the commit message',
1188 'A little more to say', 'Even more'],
1189 ['> File: file.c', '> Code line 5', '> Code line 6',
1190 '> Code line 7', '> Code line 8', '> Code line 9',
1191 'And another comment'],
1192 ['> File: file.c',
Simon Glassd85bb8f2022-01-29 14:14:09 -07001193 '> Line: 153 / 143: def check_patch(fname, show_types=False):',
Simon Glassda8a2922020-10-29 21:46:37 -06001194 '> and more code', '> +Addition here', '> +Another addition here',
1195 '> codey', '> more codey', 'and another thing in same file'],
1196 ['> File: file.c', '> Line: 253 / 243',
1197 '> with no function context', 'one more thing'],
1198 ['> File: tools/patman/main.py', '> +line of code',
1199 'now a very long comment in a different file',
1200 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']],
1201 pstrm.snippets)
Simon Glass2112d072020-10-29 21:46:38 -06001202
Simon Glassd85bb8f2022-01-29 14:14:09 -07001203 def test_review_snippets(self):
Simon Glass2112d072020-10-29 21:46:38 -06001204 """Test showing of review snippets"""
1205 def _to_submitter(who):
1206 m_who = re.match('(.*) <(.*)>', who)
1207 return {
1208 'name': m_who.group(1),
1209 'email': m_who.group(2)
1210 }
1211
1212 commit1 = Commit('abcd')
1213 commit1.subject = 'Subject 1'
1214 commit2 = Commit('ef12')
1215 commit2.subject = 'Subject 2'
1216
1217 patch1 = status.Patch('1')
1218 patch1.parse_subject('[1/2] Subject 1')
1219 patch1.name = patch1.raw_subject
1220 patch1.content = 'This is my patch content'
1221 comment1a = {'submitter': _to_submitter(self.joe),
1222 'content': '''Hi Fred,
1223
1224On some date Fred wrote:
1225
1226> diff --git a/file.c b/file.c
1227> Some code
1228> and more code
1229
1230Here is my comment above the above...
1231
1232
1233Reviewed-by: %s
1234''' % self.joe}
1235
1236 patch1.comments = [comment1a]
1237
1238 patch2 = status.Patch('2')
1239 patch2.parse_subject('[2/2] Subject 2')
1240 patch2.name = patch2.raw_subject
1241 patch2.content = 'Some other patch content'
1242 comment2a = {
1243 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1244 (self.mary, self.leb)}
1245 comment2b = {'submitter': _to_submitter(self.fred),
1246 'content': '''Hi Fred,
1247
1248On some date Fred wrote:
1249
1250> diff --git a/tools/patman/commit.py b/tools/patman/commit.py
1251> @@ -41,6 +41,9 @@ class Commit:
1252> self.rtags = collections.defaultdict(set)
1253> self.warn = []
1254>
1255> + def __str__(self):
1256> + return self.subject
1257> +
Simon Glassd85bb8f2022-01-29 14:14:09 -07001258> def add_change(self, version, info):
Simon Glass2112d072020-10-29 21:46:38 -06001259> """Add a new change line to the change list for a version.
1260>
1261A comment
1262
1263Reviewed-by: %s
1264''' % self.fred}
1265 patch2.comments = [comment2a, comment2b]
1266
1267 # This test works by setting up commits and patch for use by the fake
1268 # Rest API function _fake_patchwork2(). It calls various functions in
1269 # the status module after setting up tags in the commits, checking that
1270 # things behaves as expected
1271 self.commits = [commit1, commit2]
1272 self.patches = [patch1, patch2]
1273
1274 # Check that the output patches expectations:
1275 # 1 Subject 1
1276 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1277 # 2 Subject 2
1278 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1279 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1280 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1281 # 1 new response available in patchwork
1282
1283 series = Series()
1284 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001285 terminal.set_print_test_mode()
Simon Glass2112d072020-10-29 21:46:38 -06001286 status.check_patchwork_status(series, '1234', None, None, False, True,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001287 None, self._fake_patchwork2)
Simon Glass02811582022-01-29 14:14:18 -07001288 lines = iter(terminal.get_print_test_lines())
Simon Glass2112d072020-10-29 21:46:38 -06001289 col = terminal.Color()
1290 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
1291 next(lines))
1292 self.assertEqual(
1293 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1294 next(lines))
1295 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(lines))
1296
1297 self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED),
1298 next(lines))
1299 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1300 self.assertEqual(terminal.PrintLine('', None), next(lines))
1301 self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA),
1302 next(lines))
1303 self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA),
1304 next(lines))
1305 self.assertEqual(terminal.PrintLine(' > and more code', col.MAGENTA),
1306 next(lines))
1307 self.assertEqual(terminal.PrintLine(
1308 ' Here is my comment above the above...', None), next(lines))
1309 self.assertEqual(terminal.PrintLine('', None), next(lines))
1310
1311 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE),
1312 next(lines))
1313 self.assertEqual(
1314 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1315 next(lines))
1316 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE),
1317 next(lines))
1318 self.assertEqual(
1319 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1320 next(lines))
1321 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1322 next(lines))
1323 self.assertEqual(
1324 terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False),
1325 next(lines))
1326 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE),
1327 next(lines))
1328
1329 self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED),
1330 next(lines))
1331 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1332 self.assertEqual(terminal.PrintLine('', None), next(lines))
1333 self.assertEqual(terminal.PrintLine(
1334 ' > File: tools/patman/commit.py', col.MAGENTA), next(lines))
1335 self.assertEqual(terminal.PrintLine(
1336 ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(lines))
1337 self.assertEqual(terminal.PrintLine(
1338 ' > + return self.subject', col.MAGENTA), next(lines))
1339 self.assertEqual(terminal.PrintLine(
1340 ' > +', col.MAGENTA), next(lines))
1341 self.assertEqual(
Simon Glassd85bb8f2022-01-29 14:14:09 -07001342 terminal.PrintLine(' > def add_change(self, version, info):',
Simon Glass2112d072020-10-29 21:46:38 -06001343 col.MAGENTA),
1344 next(lines))
1345 self.assertEqual(terminal.PrintLine(
1346 ' > """Add a new change line to the change list for a version.',
1347 col.MAGENTA), next(lines))
1348 self.assertEqual(terminal.PrintLine(
1349 ' >', col.MAGENTA), next(lines))
1350 self.assertEqual(terminal.PrintLine(
1351 ' A comment', None), next(lines))
1352 self.assertEqual(terminal.PrintLine('', None), next(lines))
1353
1354 self.assertEqual(terminal.PrintLine(
1355 '4 new responses available in patchwork (use -d to write them to a new branch)',
1356 None), next(lines))
Simon Glass6a222e62021-08-01 16:02:39 -06001357
Simon Glassd85bb8f2022-01-29 14:14:09 -07001358 def test_insert_tags(self):
Simon Glass6a222e62021-08-01 16:02:39 -06001359 """Test inserting of review tags"""
1360 msg = '''first line
1361second line.'''
1362 tags = [
1363 'Reviewed-by: Bin Meng <bmeng.cn@gmail.com>',
1364 'Tested-by: Bin Meng <bmeng.cn@gmail.com>'
1365 ]
1366 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.com>'
1367 tag_str = '\n'.join(tags)
1368
1369 new_msg = patchstream.insert_tags(msg, tags)
1370 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1371
1372 new_msg = patchstream.insert_tags(msg + '\n', tags)
1373 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1374
1375 msg += '\n\n' + signoff
1376 new_msg = patchstream.insert_tags(msg, tags)
1377 self.assertEqual(msg + '\n' + tag_str, new_msg)