blob: 50fb53787d8e6016456404f5e7650d3620133371 [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 patchstream
Simon Glassa7fadab2020-10-29 21:46:26 -060022from patman.patchstream import PatchStream
Simon Glass232eefd2025-04-29 07:22:14 -060023from patman import patchwork
Simon Glassc0257982025-04-29 07:22:11 -060024from patman import send
Simon Glass3db916d2020-10-29 21:46:35 -060025from patman.series import Series
Simon Glassa997ea52020-04-17 18:09:04 -060026from patman import settings
Simon Glassba1b3b92025-02-09 14:26:00 -070027from u_boot_pylib import gitutil
Simon Glass131444f2023-02-23 18:18:04 -070028from u_boot_pylib import terminal
29from u_boot_pylib import tools
Simon Glassdf1bc5c2017-05-29 15:31:31 -060030
Tom Rini488ea972021-02-26 07:52:31 -050031import pygit2
32from patman import status
Simon Glassdf1bc5c2017-05-29 15:31:31 -060033
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050034PATMAN_DIR = pathlib.Path(__file__).parent
35TEST_DATA_DIR = PATMAN_DIR / 'test/'
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050036
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050037
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050038@contextlib.contextmanager
39def directory_excursion(directory):
40 """Change directory to `directory` for a limited to the context block."""
41 current = os.getcwd()
42 try:
43 os.chdir(directory)
44 yield
45 finally:
46 os.chdir(current)
47
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050048
Simon Glassdf1bc5c2017-05-29 15:31:31 -060049class TestFunctional(unittest.TestCase):
Simon Glasseb209e52020-10-29 21:46:15 -060050 """Functional tests for checking that patman behaves correctly"""
Simon Glass06202d62020-10-29 21:46:27 -060051 leb = (b'Lord Edmund Blackadd\xc3\xabr <weasel@blackadder.org>'.
52 decode('utf-8'))
Simon Glass3b762cc2020-10-29 21:46:28 -060053 fred = 'Fred Bloggs <f.bloggs@napier.net>'
54 joe = 'Joe Bloggs <joe@napierwallies.co.nz>'
55 mary = 'Mary Bloggs <mary@napierwallies.co.nz>'
Simon Glass3db916d2020-10-29 21:46:35 -060056 commits = None
57 patches = None
Simon Glassed831d12025-04-29 07:22:10 -060058 verbosity = False
59 preserve_outdirs = False
60
61 @classmethod
62 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
63 toolpath=None, verbosity=None, no_capture=False):
64 """Accept arguments controlling test execution
65
66 Args:
67 preserve_indir: not used
68 preserve_outdir: Preserve the output directories used by tests.
69 Each test has its own, so this is normally only useful when
70 running a single test.
71 toolpath: not used
72 """
73 cls.preserve_outdirs = preserve_outdirs
74 cls.toolpath = toolpath
75 cls.verbosity = verbosity
76 cls.no_capture = no_capture
Simon Glass06202d62020-10-29 21:46:27 -060077
Simon Glassdf1bc5c2017-05-29 15:31:31 -060078 def setUp(self):
79 self.tmpdir = tempfile.mkdtemp(prefix='patman.')
Simon Glass54f1c5b2020-07-05 21:41:50 -060080 self.gitdir = os.path.join(self.tmpdir, 'git')
81 self.repo = None
Simon Glassdf1bc5c2017-05-29 15:31:31 -060082
83 def tearDown(self):
Simon Glassed831d12025-04-29 07:22:10 -060084 if self.preserve_outdirs:
85 print(f'Output dir: {self.tmpdir}')
86 else:
87 shutil.rmtree(self.tmpdir)
Simon Glass02811582022-01-29 14:14:18 -070088 terminal.set_print_test_mode(False)
Simon Glassdf1bc5c2017-05-29 15:31:31 -060089
90 @staticmethod
Simon Glasseb209e52020-10-29 21:46:15 -060091 def _get_path(fname):
92 """Get the path to a test file
93
94 Args:
95 fname (str): Filename to obtain
96
97 Returns:
98 str: Full path to file in the test directory
99 """
Maxim Cournoyer0331edb2022-12-19 17:32:39 -0500100 return TEST_DATA_DIR / fname
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600101
102 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600103 def _get_text(cls, fname):
104 """Read a file as text
105
106 Args:
107 fname (str): Filename to read
108
109 Returns:
110 str: Contents of file
111 """
112 return open(cls._get_path(fname), encoding='utf-8').read()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600113
114 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600115 def _get_patch_name(cls, subject):
116 """Get the filename of a patch given its subject
117
118 Args:
119 subject (str): Patch subject
120
121 Returns:
122 str: Filename for that patch
123 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600124 fname = re.sub('[ :]', '-', subject)
125 return fname.replace('--', '-')
126
Simon Glasseb209e52020-10-29 21:46:15 -0600127 def _create_patches_for_test(self, series):
128 """Create patch files for use by tests
129
130 This copies patch files from the test directory as needed by the series
131
132 Args:
133 series (Series): Series containing commits to convert
134
135 Returns:
136 tuple:
137 str: Cover-letter filename, or None if none
138 fname_list: list of str, each a patch filename
139 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600140 cover_fname = None
141 fname_list = []
142 for i, commit in enumerate(series.commits):
Simon Glasseb209e52020-10-29 21:46:15 -0600143 clean_subject = self._get_patch_name(commit.subject)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600144 src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
145 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600146 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600147 fname_list.append(fname)
148 if series.get('cover'):
149 src_fname = '0000-cover-letter.patch'
150 cover_fname = os.path.join(self.tmpdir, src_fname)
151 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600152 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600153
154 return cover_fname, fname_list
155
Simon Glassd85bb8f2022-01-29 14:14:09 -0700156 def test_basic(self):
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600157 """Tests the basic flow of patman
158
159 This creates a series from some hard-coded patches build from a simple
160 tree with the following metadata in the top commit:
161
162 Series-to: u-boot
163 Series-prefix: RFC
Sean Andersondc1cd132021-10-22 19:07:04 -0400164 Series-postfix: some-branch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600165 Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
166 Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Sean Andersoncf13b862020-05-04 16:28:36 -0400167 Series-version: 3
168 Patch-cc: fred
169 Series-process-log: sort, uniq
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600170 Series-changes: 4
171 - Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400172 - Multi
173 line
174 change
175
176 Commit-changes: 2
177 - Changes only for this commit
178
Simon Glassf1aab6f2025-04-29 07:22:07 -0600179 Cover-changes: 4
Sean Andersoncf13b862020-05-04 16:28:36 -0400180 - Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600181
182 Cover-letter:
183 test: A test patch series
184 This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400185 letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600186 works
187 END
188
189 and this in the first commit:
190
Sean Andersoncf13b862020-05-04 16:28:36 -0400191 Commit-changes: 2
192 - second revision change
193
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600194 Series-notes:
195 some notes
196 about some things
197 from the first commit
198 END
199
200 Commit-notes:
201 Some notes about
202 the first commit
203 END
204
205 with the following commands:
206
207 git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
208 git format-patch --subject-prefix RFC --cover-letter HEAD~2
209 mv 00* /path/to/tools/patman/test
210
211 It checks these aspects:
212 - git log can be processed by patchstream
213 - emailing patches uses the correct command
214 - CC file has information on each commit
215 - cover letter has the expected text and subject
216 - each patch has the correct subject
217 - dry-run information prints out correctly
218 - unicode is handled correctly
Sean Andersondc1cd132021-10-22 19:07:04 -0400219 - Series-to, Series-cc, Series-prefix, Series-postfix, Cover-letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600220 - Cover-letter-cc, Series-version, Series-changes, Series-notes
221 - Commit-notes
222 """
223 process_tags = True
Simon Glass1f975b92021-01-23 08:56:15 -0700224 ignore_bad_tags = False
Simon Glass4f817892019-05-14 15:53:53 -0600225 stefan = b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600226 rick = 'Richard III <richard@palace.gov>'
Simon Glass4f817892019-05-14 15:53:53 -0600227 mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600228 add_maintainers = [stefan, rick]
229 dry_run = True
230 in_reply_to = mel
231 count = 2
232 settings.alias = {
Simon Glass95745aa2020-10-29 21:46:13 -0600233 'fdt': ['simon'],
234 'u-boot': ['u-boot@lists.denx.de'],
Simon Glass06202d62020-10-29 21:46:27 -0600235 'simon': [self.leb],
Simon Glass3b762cc2020-10-29 21:46:28 -0600236 'fred': [self.fred],
Sean Anderson25978092024-04-18 22:36:31 -0400237 'joe': [self.joe],
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600238 }
239
Simon Glasseb209e52020-10-29 21:46:15 -0600240 text = self._get_text('test01.txt')
Simon Glass93f61c02020-10-29 21:46:19 -0600241 series = patchstream.get_metadata_for_test(text)
Simon Glass414f1e02025-02-27 12:27:30 -0700242 series.base_commit = Commit('1a44532')
243 series.branch = 'mybranch'
Simon Glasseb209e52020-10-29 21:46:15 -0600244 cover_fname, args = self._create_patches_for_test(series)
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500245 get_maintainer_script = str(pathlib.Path(__file__).parent.parent.parent
246 / 'get_maintainer.pl') + ' --norolestats'
Simon Glass14d64e32025-04-29 07:21:59 -0600247 with terminal.capture() as out:
Simon Glass93f61c02020-10-29 21:46:19 -0600248 patchstream.fix_patches(series, args)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600249 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -0600250 patchstream.insert_cover_letter(cover_fname, series, count)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600251 series.DoChecks()
252 cc_file = series.MakeCcFile(process_tags, cover_fname,
Chris Packhamb84fb482018-06-07 20:45:06 +1200253 not ignore_bad_tags, add_maintainers,
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500254 None, get_maintainer_script)
Simon Glass761648b2022-01-29 14:14:11 -0700255 cmd = gitutil.email_patches(
Simon Glass95745aa2020-10-29 21:46:13 -0600256 series, cover_fname, args, dry_run, not ignore_bad_tags,
257 cc_file, in_reply_to=in_reply_to, thread=None)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600258 series.ShowActions(args, cmd, process_tags)
Simon Glassf544a2d2019-10-31 07:42:51 -0600259 cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600260 os.remove(cc_file)
261
Simon Glass42e3d392020-10-29 21:46:29 -0600262 lines = iter(out[0].getvalue().splitlines())
263 self.assertEqual('Cleaned %s patches' % len(series.commits),
264 next(lines))
265 self.assertEqual('Change log missing for v2', next(lines))
266 self.assertEqual('Change log missing for v3', next(lines))
267 self.assertEqual('Change log for unknown version v4', next(lines))
268 self.assertEqual("Alias 'pci' not found", next(lines))
Simon Glass620639c2023-03-08 10:52:54 -0800269 while next(lines) != 'Cc processing complete':
270 pass
Simon Glass42e3d392020-10-29 21:46:29 -0600271 self.assertIn('Dry run', next(lines))
272 self.assertEqual('', next(lines))
273 self.assertIn('Send a total of %d patches' % count, next(lines))
274 prev = next(lines)
275 for i, commit in enumerate(series.commits):
276 self.assertEqual(' %s' % args[i], prev)
277 while True:
278 prev = next(lines)
279 if 'Cc:' not in prev:
280 break
281 self.assertEqual('To: u-boot@lists.denx.de', prev)
Simon Glass9dfb3112020-11-08 20:36:18 -0700282 self.assertEqual('Cc: %s' % stefan, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600283 self.assertEqual('Version: 3', next(lines))
284 self.assertEqual('Prefix:\t RFC', next(lines))
Sean Andersondc1cd132021-10-22 19:07:04 -0400285 self.assertEqual('Postfix:\t some-branch', next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600286 self.assertEqual('Cover: 4 lines', next(lines))
287 self.assertEqual(' Cc: %s' % self.fred, next(lines))
Sean Anderson25978092024-04-18 22:36:31 -0400288 self.assertEqual(' Cc: %s' % self.joe, next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700289 self.assertEqual(' Cc: %s' % self.leb,
Simon Glass42e3d392020-10-29 21:46:29 -0600290 next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700291 self.assertEqual(' Cc: %s' % mel, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600292 self.assertEqual(' Cc: %s' % rick, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600293 expected = ('Git command: git send-email --annotate '
294 '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
Simon Glass1ee91c12020-11-03 13:54:10 -0700295 '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600296 % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
Simon Glass4f817892019-05-14 15:53:53 -0600297 ' '.join(args)))
Simon Glass9dfb3112020-11-08 20:36:18 -0700298 self.assertEqual(expected, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600299
Simon Glass9dfb3112020-11-08 20:36:18 -0700300 self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0])
Simon Glass95745aa2020-10-29 21:46:13 -0600301 self.assertEqual(
Sean Anderson25978092024-04-18 22:36:31 -0400302 '%s %s\0%s\0%s\0%s\0%s' % (args[1], self.fred, self.joe, self.leb,
303 rick, stefan),
Simon Glass9dfb3112020-11-08 20:36:18 -0700304 cc_lines[1])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600305
306 expected = '''
307This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400308letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600309works
310
311some notes
312about some things
313from the first commit
314
315Changes in v4:
Sean Andersoncf13b862020-05-04 16:28:36 -0400316- Multi
317 line
318 change
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600319- Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400320- Some notes for the cover letter
Sean Andersone45678c2024-04-18 22:36:32 -0400321- fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600322
323Simon Glass (2):
324 pci: Correct cast for sandbox
Siva Durga Prasad Paladugub3d55ea2018-07-16 15:56:11 +0530325 fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600326
327 cmd/pci.c | 3 ++-
328 fs/fat/fat.c | 1 +
329 lib/efi_loader/efi_memory.c | 1 +
330 lib/fdtdec.c | 3 ++-
331 4 files changed, 6 insertions(+), 2 deletions(-)
332
333--\x20
3342.7.4
335
Simon Glass414f1e02025-02-27 12:27:30 -0700336base-commit: 1a44532
337branch: mybranch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600338'''
Simon Glassf544a2d2019-10-31 07:42:51 -0600339 lines = open(cover_fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600340 self.assertEqual(
Sean Andersondc1cd132021-10-22 19:07:04 -0400341 'Subject: [RFC PATCH some-branch v3 0/2] test: A test patch series',
Simon Glass95745aa2020-10-29 21:46:13 -0600342 lines[3])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600343 self.assertEqual(expected.splitlines(), lines[7:])
344
345 for i, fname in enumerate(args):
Simon Glassf544a2d2019-10-31 07:42:51 -0600346 lines = open(fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600347 subject = [line for line in lines if line.startswith('Subject')]
348 self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
349 subject[0][:18])
Sean Andersoncf13b862020-05-04 16:28:36 -0400350
351 # Check that we got our commit notes
352 start = 0
353 expected = ''
354
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600355 if i == 0:
Sean Andersoncf13b862020-05-04 16:28:36 -0400356 start = 17
357 expected = '''---
358Some notes about
359the first commit
360
361(no changes since v2)
362
363Changes in v2:
364- second revision change'''
365 elif i == 1:
366 start = 17
367 expected = '''---
368
369Changes in v4:
370- Multi
371 line
372 change
Sean Andersone45678c2024-04-18 22:36:32 -0400373- New
Sean Andersoncf13b862020-05-04 16:28:36 -0400374- Some changes
375
376Changes in v2:
377- Changes only for this commit'''
378
379 if expected:
380 expected = expected.splitlines()
381 self.assertEqual(expected, lines[start:(start+len(expected))])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600382
Simon Glassda1a6ec2025-03-28 07:02:20 -0600383 def test_base_commit(self):
384 """Test adding a base commit with no cover letter"""
385 orig_text = self._get_text('test01.txt')
386 pos = orig_text.index('commit 5ab48490f03051875ab13d288a4bf32b507d76fd')
387 text = orig_text[:pos]
388 series = patchstream.get_metadata_for_test(text)
389 series.base_commit = Commit('1a44532')
390 series.branch = 'mybranch'
391 cover_fname, args = self._create_patches_for_test(series)
392 self.assertFalse(cover_fname)
Simon Glass14d64e32025-04-29 07:21:59 -0600393 with terminal.capture() as out:
Simon Glassda1a6ec2025-03-28 07:02:20 -0600394 patchstream.fix_patches(series, args, insert_base_commit=True)
395 self.assertEqual('Cleaned 1 patch\n', out[0].getvalue())
396 lines = tools.read_file(args[0], binary=False).splitlines()
397 pos = lines.index('-- ')
398
399 # We expect these lines at the end:
400 # -- (with trailing space)
401 # 2.7.4
402 # (empty)
403 # base-commit: xxx
404 # branch: xxx
405 self.assertEqual('base-commit: 1a44532', lines[pos + 3])
406 self.assertEqual('branch: mybranch', lines[pos + 4])
407
Simon Glass54f1c5b2020-07-05 21:41:50 -0600408 def make_commit_with_file(self, subject, body, fname, text):
409 """Create a file and add it to the git repo with a new commit
410
411 Args:
412 subject (str): Subject for the commit
413 body (str): Body text of the commit
414 fname (str): Filename of file to create
415 text (str): Text to put into the file
416 """
417 path = os.path.join(self.gitdir, fname)
Simon Glass80025522022-01-29 14:14:04 -0700418 tools.write_file(path, text, binary=False)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600419 index = self.repo.index
420 index.add(fname)
Simon Glass547cba62022-02-11 13:23:18 -0700421 # pylint doesn't seem to find this
422 # pylint: disable=E1101
Simon Glass95745aa2020-10-29 21:46:13 -0600423 author = pygit2.Signature('Test user', 'test@email.com')
Simon Glass54f1c5b2020-07-05 21:41:50 -0600424 committer = author
425 tree = index.write_tree()
426 message = subject + '\n' + body
427 self.repo.create_commit('HEAD', author, committer, message, tree,
428 [self.repo.head.target])
429
430 def make_git_tree(self):
431 """Make a simple git tree suitable for testing
432
433 It has three branches:
434 'base' has two commits: PCI, main
435 'first' has base as upstream and two more commits: I2C, SPI
436 'second' has base as upstream and three more: video, serial, bootm
437
438 Returns:
Simon Glasseb209e52020-10-29 21:46:15 -0600439 pygit2.Repository: repository
Simon Glass54f1c5b2020-07-05 21:41:50 -0600440 """
441 repo = pygit2.init_repository(self.gitdir)
442 self.repo = repo
443 new_tree = repo.TreeBuilder().write()
444
Simon Glass547cba62022-02-11 13:23:18 -0700445 # pylint doesn't seem to find this
446 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600447 author = pygit2.Signature('Test user', 'test@email.com')
448 committer = author
Simon Glasseb209e52020-10-29 21:46:15 -0600449 _ = repo.create_commit('HEAD', author, committer, 'Created master',
450 new_tree, [])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600451
452 self.make_commit_with_file('Initial commit', '''
453Add a README
454
455''', 'README', '''This is the README file
456describing this project
457in very little detail''')
458
459 self.make_commit_with_file('pci: PCI implementation', '''
460Here is a basic PCI implementation
461
462''', 'pci.c', '''This is a file
463it has some contents
464and some more things''')
465 self.make_commit_with_file('main: Main program', '''
466Hello here is the second commit.
467''', 'main.c', '''This is the main file
468there is very little here
469but we can always add more later
470if we want to
471
472Series-to: u-boot
473Series-cc: Barry Crump <bcrump@whataroa.nz>
474''')
475 base_target = repo.revparse_single('HEAD')
476 self.make_commit_with_file('i2c: I2C things', '''
477This has some stuff to do with I2C
478''', 'i2c.c', '''And this is the file contents
479with some I2C-related things in it''')
480 self.make_commit_with_file('spi: SPI fixes', '''
481SPI needs some fixes
482and here they are
Simon Glassd0a0a582020-10-29 21:46:36 -0600483
484Signed-off-by: %s
485
486Series-to: u-boot
487Commit-notes:
488title of the series
489This is the cover letter for the series
490with various details
491END
492''' % self.leb, 'spi.c', '''Some fixes for SPI in this
Simon Glass54f1c5b2020-07-05 21:41:50 -0600493file to make SPI work
494better than before''')
495 first_target = repo.revparse_single('HEAD')
496
497 target = repo.revparse_single('HEAD~2')
Simon Glass547cba62022-02-11 13:23:18 -0700498 # pylint doesn't seem to find this
499 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600500 repo.reset(target.oid, pygit2.GIT_CHECKOUT_FORCE)
501 self.make_commit_with_file('video: Some video improvements', '''
502Fix up the video so that
503it looks more purple. Purple is
504a very nice colour.
505''', 'video.c', '''More purple here
506Purple and purple
507Even more purple
508Could not be any more purple''')
509 self.make_commit_with_file('serial: Add a serial driver', '''
510Here is the serial driver
511for my chip.
512
513Cover-letter:
514Series for my board
515This series implements support
516for my glorious board.
517END
Simon Glassa80986c2020-10-29 21:46:16 -0600518Series-links: 183237
Simon Glass54f1c5b2020-07-05 21:41:50 -0600519''', 'serial.c', '''The code for the
520serial driver is here''')
521 self.make_commit_with_file('bootm: Make it boot', '''
522This makes my board boot
523with a fix to the bootm
524command
525''', 'bootm.c', '''Fix up the bootm
526command to make the code as
527complicated as possible''')
528 second_target = repo.revparse_single('HEAD')
529
530 repo.branches.local.create('first', first_target)
531 repo.config.set_multivar('branch.first.remote', '', '.')
532 repo.config.set_multivar('branch.first.merge', '', 'refs/heads/base')
533
534 repo.branches.local.create('second', second_target)
535 repo.config.set_multivar('branch.second.remote', '', '.')
536 repo.config.set_multivar('branch.second.merge', '', 'refs/heads/base')
537
538 repo.branches.local.create('base', base_target)
539 return repo
540
Simon Glassd85bb8f2022-01-29 14:14:09 -0700541 def test_branch(self):
Simon Glass54f1c5b2020-07-05 21:41:50 -0600542 """Test creating patches from a branch"""
543 repo = self.make_git_tree()
544 target = repo.lookup_reference('refs/heads/first')
Simon Glass547cba62022-02-11 13:23:18 -0700545 # pylint doesn't seem to find this
546 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600547 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
548 control.setup()
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200549 orig_dir = os.getcwd()
Simon Glass54f1c5b2020-07-05 21:41:50 -0600550 try:
Simon Glass54f1c5b2020-07-05 21:41:50 -0600551 os.chdir(self.gitdir)
552
553 # Check that it can detect the current branch
Simon Glass761648b2022-01-29 14:14:11 -0700554 self.assertEqual(2, gitutil.count_commits_to_branch(None))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600555 col = terminal.Color()
Simon Glass14d64e32025-04-29 07:21:59 -0600556 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600557 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600558 col, branch=None, count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100559 ignore_binary=False, signoff=True)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600560 self.assertIsNone(cover_fname)
561 self.assertEqual(2, len(patch_files))
Simon Glass2eb4da72020-07-05 21:41:51 -0600562
563 # Check that it can detect a different branch
Simon Glass761648b2022-01-29 14:14:11 -0700564 self.assertEqual(3, gitutil.count_commits_to_branch('second'))
Simon Glass14d64e32025-04-29 07:21:59 -0600565 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600566 series, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600567 col, branch='second', count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100568 ignore_binary=False, signoff=True)
Simon Glass2eb4da72020-07-05 21:41:51 -0600569 self.assertIsNotNone(cover_fname)
570 self.assertEqual(3, len(patch_files))
Simon Glassb3bf4e12020-07-05 21:41:52 -0600571
Simon Glass414f1e02025-02-27 12:27:30 -0700572 cover = tools.read_file(cover_fname, binary=False)
573 lines = cover.splitlines()[-2:]
574 base = repo.lookup_reference('refs/heads/base').target
575 self.assertEqual(f'base-commit: {base}', lines[0])
576 self.assertEqual('branch: second', lines[1])
577
Simon Glassda1a6ec2025-03-28 07:02:20 -0600578 # Make sure that the base-commit is not present when it is in the
579 # cover letter
580 for fname in patch_files:
581 self.assertNotIn(b'base-commit:', tools.read_file(fname))
582
Simon Glassb3bf4e12020-07-05 21:41:52 -0600583 # Check that it can skip patches at the end
Simon Glass14d64e32025-04-29 07:21:59 -0600584 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600585 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600586 col, branch='second', count=-1, start=0, end=1,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100587 ignore_binary=False, signoff=True)
Simon Glassb3bf4e12020-07-05 21:41:52 -0600588 self.assertIsNotNone(cover_fname)
589 self.assertEqual(2, len(patch_files))
Simon Glass414f1e02025-02-27 12:27:30 -0700590
591 cover = tools.read_file(cover_fname, binary=False)
592 lines = cover.splitlines()[-2:]
593 base2 = repo.lookup_reference('refs/heads/second')
594 ref = base2.peel(pygit2.GIT_OBJ_COMMIT).parents[0].parents[0].id
595 self.assertEqual(f'base-commit: {ref}', lines[0])
596 self.assertEqual('branch: second', lines[1])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600597 finally:
598 os.chdir(orig_dir)
Simon Glass06202d62020-10-29 21:46:27 -0600599
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500600 def test_custom_get_maintainer_script(self):
601 """Validate that a custom get_maintainer script gets used."""
602 self.make_git_tree()
603 with directory_excursion(self.gitdir):
604 # Setup git.
605 os.environ['GIT_CONFIG_GLOBAL'] = '/dev/null'
606 os.environ['GIT_CONFIG_SYSTEM'] = '/dev/null'
607 tools.run('git', 'config', 'user.name', 'Dummy')
608 tools.run('git', 'config', 'user.email', 'dumdum@dummy.com')
609 tools.run('git', 'branch', 'upstream')
610 tools.run('git', 'branch', '--set-upstream-to=upstream')
611 tools.run('git', 'add', '.')
612 tools.run('git', 'commit', '-m', 'new commit')
613
614 # Setup patman configuration.
615 with open('.patman', 'w', buffering=1) as f:
616 f.write('[settings]\n'
617 'get_maintainer_script: dummy-script.sh\n'
Sean Andersona06df742024-04-18 22:36:30 -0400618 'check_patch: False\n'
619 'add_maintainers: True\n')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500620 with open('dummy-script.sh', 'w', buffering=1) as f:
621 f.write('#!/usr/bin/env python\n'
622 'print("hello@there.com")\n')
623 os.chmod('dummy-script.sh', 0x555)
624
625 # Finally, do the test
Simon Glass14d64e32025-04-29 07:21:59 -0600626 with terminal.capture():
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500627 output = tools.run(PATMAN_DIR / 'patman', '--dry-run')
628 # Assert the email address is part of the dry-run
629 # output.
630 self.assertIn('hello@there.com', output)
631
Simon Glassd85bb8f2022-01-29 14:14:09 -0700632 def test_tags(self):
Simon Glass06202d62020-10-29 21:46:27 -0600633 """Test collection of tags in a patchstream"""
634 text = '''This is a patch
635
636Signed-off-by: Terminator
Simon Glass3b762cc2020-10-29 21:46:28 -0600637Reviewed-by: %s
638Reviewed-by: %s
Simon Glass06202d62020-10-29 21:46:27 -0600639Tested-by: %s
Simon Glass3b762cc2020-10-29 21:46:28 -0600640''' % (self.joe, self.mary, self.leb)
Simon Glass06202d62020-10-29 21:46:27 -0600641 pstrm = PatchStream.process_text(text)
642 self.assertEqual(pstrm.commit.rtags, {
Simon Glass3b762cc2020-10-29 21:46:28 -0600643 'Reviewed-by': {self.joe, self.mary},
Simon Glass06202d62020-10-29 21:46:27 -0600644 'Tested-by': {self.leb}})
Simon Glass3b762cc2020-10-29 21:46:28 -0600645
Simon Glassd85bb8f2022-01-29 14:14:09 -0700646 def test_invalid_tag(self):
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200647 """Test invalid tag in a patchstream"""
648 text = '''This is a patch
649
650Serie-version: 2
651'''
652 with self.assertRaises(ValueError) as exc:
653 pstrm = PatchStream.process_text(text)
654 self.assertEqual("Line 3: Invalid tag = 'Serie-version: 2'",
655 str(exc.exception))
656
Simon Glassd85bb8f2022-01-29 14:14:09 -0700657 def test_missing_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600658 """Test a missing END tag"""
659 text = '''This is a patch
660
661Cover-letter:
662This is the title
663missing END after this line
664Signed-off-by: Fred
665'''
666 pstrm = PatchStream.process_text(text)
667 self.assertEqual(["Missing 'END' in section 'cover'"],
668 pstrm.commit.warn)
669
Simon Glassd85bb8f2022-01-29 14:14:09 -0700670 def test_missing_blank_line(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600671 """Test a missing blank line after a tag"""
672 text = '''This is a patch
673
674Series-changes: 2
675- First line of changes
676- Missing blank line after this line
677Signed-off-by: Fred
678'''
679 pstrm = PatchStream.process_text(text)
680 self.assertEqual(["Missing 'blank line' in section 'Series-changes'"],
681 pstrm.commit.warn)
682
Simon Glassd85bb8f2022-01-29 14:14:09 -0700683 def test_invalid_commit_tag(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600684 """Test an invalid Commit-xxx tag"""
685 text = '''This is a patch
686
687Commit-fred: testing
688'''
689 pstrm = PatchStream.process_text(text)
690 self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn)
691
Simon Glassd85bb8f2022-01-29 14:14:09 -0700692 def test_self_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600693 """Test a tested by tag by this user"""
694 test_line = 'Tested-by: %s@napier.com' % os.getenv('USER')
695 text = '''This is a patch
696
697%s
698''' % test_line
699 pstrm = PatchStream.process_text(text)
700 self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn)
701
Simon Glassd85bb8f2022-01-29 14:14:09 -0700702 def test_space_before_tab(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600703 """Test a space before a tab"""
704 text = '''This is a patch
705
706+ \tSomething
707'''
708 pstrm = PatchStream.process_text(text)
709 self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn)
710
Simon Glassd85bb8f2022-01-29 14:14:09 -0700711 def test_lines_after_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600712 """Test detecting lines after TEST= line"""
713 text = '''This is a patch
714
715TEST=sometest
716more lines
717here
718'''
719 pstrm = PatchStream.process_text(text)
720 self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn)
721
Simon Glassd85bb8f2022-01-29 14:14:09 -0700722 def test_blank_line_at_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600723 """Test detecting a blank line at the end of a file"""
724 text = '''This is a patch
725
726diff --git a/lib/fdtdec.c b/lib/fdtdec.c
727index c072e54..942244f 100644
728--- a/lib/fdtdec.c
729+++ b/lib/fdtdec.c
730@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void)
731 }
732
733 gd->ram_size = (phys_size_t)(res.end - res.start + 1);
734- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size);
735+ debug("%s: Initial DRAM size %llx\n", __func__,
736+ (unsigned long long)gd->ram_size);
737+
738diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
739
740--
7412.7.4
742
743 '''
744 pstrm = PatchStream.process_text(text)
745 self.assertEqual(
746 ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"],
747 pstrm.commit.warn)
Simon Glass1c1f2072020-10-29 21:46:34 -0600748
Simon Glassd85bb8f2022-01-29 14:14:09 -0700749 def test_no_upstream(self):
Simon Glass1c1f2072020-10-29 21:46:34 -0600750 """Test CountCommitsToBranch when there is no upstream"""
751 repo = self.make_git_tree()
752 target = repo.lookup_reference('refs/heads/base')
Simon Glass547cba62022-02-11 13:23:18 -0700753 # pylint doesn't seem to find this
754 # pylint: disable=E1101
Simon Glass1c1f2072020-10-29 21:46:34 -0600755 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
756
757 # Check that it can detect the current branch
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200758 orig_dir = os.getcwd()
Simon Glass1c1f2072020-10-29 21:46:34 -0600759 try:
Simon Glass1c1f2072020-10-29 21:46:34 -0600760 os.chdir(self.gitdir)
761 with self.assertRaises(ValueError) as exc:
Simon Glass761648b2022-01-29 14:14:11 -0700762 gitutil.count_commits_to_branch(None)
Simon Glass1c1f2072020-10-29 21:46:34 -0600763 self.assertIn(
764 "Failed to determine upstream: fatal: no upstream configured for branch 'base'",
765 str(exc.exception))
766 finally:
767 os.chdir(orig_dir)
Simon Glass3db916d2020-10-29 21:46:35 -0600768
769 @staticmethod
Simon Glassf9b03cf2020-11-03 13:54:14 -0700770 def _fake_patchwork(url, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600771 """Fake Patchwork server for the function below
772
773 This handles accessing a series, providing a list consisting of a
774 single patch
Simon Glassf9b03cf2020-11-03 13:54:14 -0700775
776 Args:
777 url (str): URL of patchwork server
778 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600779 """
780 re_series = re.match(r'series/(\d*)/$', subpath)
781 if re_series:
782 series_num = re_series.group(1)
783 if series_num == '1234':
784 return {'patches': [
785 {'id': '1', 'name': 'Some patch'}]}
786 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
787
Simon Glassd85bb8f2022-01-29 14:14:09 -0700788 def test_status_mismatch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600789 """Test Patchwork patches not matching the series"""
790 series = Series()
791
Simon Glass14d64e32025-04-29 07:21:59 -0600792 with terminal.capture() as (_, err):
Simon Glassf9b03cf2020-11-03 13:54:14 -0700793 status.collect_patches(series, 1234, None, self._fake_patchwork)
Simon Glass3db916d2020-10-29 21:46:35 -0600794 self.assertIn('Warning: Patchwork reports 1 patches, series has 0',
795 err.getvalue())
796
Simon Glassd85bb8f2022-01-29 14:14:09 -0700797 def test_status_read_patch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600798 """Test handling a single patch in Patchwork"""
799 series = Series()
800 series.commits = [Commit('abcd')]
801
Simon Glassf9b03cf2020-11-03 13:54:14 -0700802 patches = status.collect_patches(series, 1234, None,
803 self._fake_patchwork)
Simon Glass3db916d2020-10-29 21:46:35 -0600804 self.assertEqual(1, len(patches))
805 patch = patches[0]
806 self.assertEqual('1', patch.id)
807 self.assertEqual('Some patch', patch.raw_subject)
808
Simon Glassd85bb8f2022-01-29 14:14:09 -0700809 def test_parse_subject(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600810 """Test parsing of the patch subject"""
Simon Glass232eefd2025-04-29 07:22:14 -0600811 patch = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600812
813 # Simple patch not in a series
814 patch.parse_subject('Testing')
815 self.assertEqual('Testing', patch.raw_subject)
816 self.assertEqual('Testing', patch.subject)
817 self.assertEqual(1, patch.seq)
818 self.assertEqual(1, patch.count)
819 self.assertEqual(None, patch.prefix)
820 self.assertEqual(None, patch.version)
821
822 # First patch in a series
823 patch.parse_subject('[1/2] Testing')
824 self.assertEqual('[1/2] Testing', patch.raw_subject)
825 self.assertEqual('Testing', patch.subject)
826 self.assertEqual(1, patch.seq)
827 self.assertEqual(2, patch.count)
828 self.assertEqual(None, patch.prefix)
829 self.assertEqual(None, patch.version)
830
831 # Second patch in a series
832 patch.parse_subject('[2/2] Testing')
833 self.assertEqual('Testing', patch.subject)
834 self.assertEqual(2, patch.seq)
835 self.assertEqual(2, patch.count)
836 self.assertEqual(None, patch.prefix)
837 self.assertEqual(None, patch.version)
838
839 # RFC patch
840 patch.parse_subject('[RFC,3/7] Testing')
841 self.assertEqual('Testing', patch.subject)
842 self.assertEqual(3, patch.seq)
843 self.assertEqual(7, patch.count)
844 self.assertEqual('RFC', patch.prefix)
845 self.assertEqual(None, patch.version)
846
847 # Version patch
848 patch.parse_subject('[v2,3/7] Testing')
849 self.assertEqual('Testing', patch.subject)
850 self.assertEqual(3, patch.seq)
851 self.assertEqual(7, patch.count)
852 self.assertEqual(None, patch.prefix)
853 self.assertEqual('v2', patch.version)
854
855 # All fields
856 patch.parse_subject('[RESEND,v2,3/7] Testing')
857 self.assertEqual('Testing', patch.subject)
858 self.assertEqual(3, patch.seq)
859 self.assertEqual(7, patch.count)
860 self.assertEqual('RESEND', patch.prefix)
861 self.assertEqual('v2', patch.version)
862
863 # RFC only
864 patch.parse_subject('[RESEND] Testing')
865 self.assertEqual('Testing', patch.subject)
866 self.assertEqual(1, patch.seq)
867 self.assertEqual(1, patch.count)
868 self.assertEqual('RESEND', patch.prefix)
869 self.assertEqual(None, patch.version)
870
Simon Glassd85bb8f2022-01-29 14:14:09 -0700871 def test_compare_series(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600872 """Test operation of compare_with_series()"""
873 commit1 = Commit('abcd')
874 commit1.subject = 'Subject 1'
875 commit2 = Commit('ef12')
876 commit2.subject = 'Subject 2'
877 commit3 = Commit('3456')
878 commit3.subject = 'Subject 2'
879
Simon Glass232eefd2025-04-29 07:22:14 -0600880 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600881 patch1.subject = 'Subject 1'
Simon Glass232eefd2025-04-29 07:22:14 -0600882 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600883 patch2.subject = 'Subject 2'
Simon Glass232eefd2025-04-29 07:22:14 -0600884 patch3 = patchwork.Patch('3')
Simon Glass3db916d2020-10-29 21:46:35 -0600885 patch3.subject = 'Subject 2'
886
887 series = Series()
888 series.commits = [commit1]
889 patches = [patch1]
890 patch_for_commit, commit_for_patch, warnings = (
891 status.compare_with_series(series, patches))
892 self.assertEqual(1, len(patch_for_commit))
893 self.assertEqual(patch1, patch_for_commit[0])
894 self.assertEqual(1, len(commit_for_patch))
895 self.assertEqual(commit1, commit_for_patch[0])
896
897 series.commits = [commit1]
898 patches = [patch1, patch2]
899 patch_for_commit, commit_for_patch, warnings = (
900 status.compare_with_series(series, patches))
901 self.assertEqual(1, len(patch_for_commit))
902 self.assertEqual(patch1, patch_for_commit[0])
903 self.assertEqual(1, len(commit_for_patch))
904 self.assertEqual(commit1, commit_for_patch[0])
905 self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"],
906 warnings)
907
908 series.commits = [commit1, commit2]
909 patches = [patch1]
910 patch_for_commit, commit_for_patch, warnings = (
911 status.compare_with_series(series, patches))
912 self.assertEqual(1, len(patch_for_commit))
913 self.assertEqual(patch1, patch_for_commit[0])
914 self.assertEqual(1, len(commit_for_patch))
915 self.assertEqual(commit1, commit_for_patch[0])
916 self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"],
917 warnings)
918
919 series.commits = [commit1, commit2, commit3]
920 patches = [patch1, patch2]
921 patch_for_commit, commit_for_patch, warnings = (
922 status.compare_with_series(series, patches))
923 self.assertEqual(2, len(patch_for_commit))
924 self.assertEqual(patch1, patch_for_commit[0])
925 self.assertEqual(patch2, patch_for_commit[1])
926 self.assertEqual(1, len(commit_for_patch))
927 self.assertEqual(commit1, commit_for_patch[0])
928 self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')",
929 "Multiple commits match patch 2 ('Subject 2'):\n"
930 ' Subject 2\n Subject 2'],
931 warnings)
932
933 series.commits = [commit1, commit2]
934 patches = [patch1, patch2, patch3]
935 patch_for_commit, commit_for_patch, warnings = (
936 status.compare_with_series(series, patches))
937 self.assertEqual(1, len(patch_for_commit))
938 self.assertEqual(patch1, patch_for_commit[0])
939 self.assertEqual(2, len(commit_for_patch))
940 self.assertEqual(commit1, commit_for_patch[0])
941 self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n"
942 ' Subject 2\n Subject 2',
943 "Cannot find commit for patch 3 ('Subject 2')"],
944 warnings)
945
Simon Glassf9b03cf2020-11-03 13:54:14 -0700946 def _fake_patchwork2(self, url, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600947 """Fake Patchwork server for the function below
948
949 This handles accessing series, patches and comments, providing the data
950 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700951
952 Args:
953 url (str): URL of patchwork server
954 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600955 """
956 re_series = re.match(r'series/(\d*)/$', subpath)
957 re_patch = re.match(r'patches/(\d*)/$', subpath)
958 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
959 if re_series:
960 series_num = re_series.group(1)
961 if series_num == '1234':
962 return {'patches': self.patches}
963 elif re_patch:
964 patch_num = int(re_patch.group(1))
965 patch = self.patches[patch_num - 1]
966 return patch
967 elif re_comments:
968 patch_num = int(re_comments.group(1))
969 patch = self.patches[patch_num - 1]
970 return patch.comments
971 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
972
Simon Glassd85bb8f2022-01-29 14:14:09 -0700973 def test_find_new_responses(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600974 """Test operation of find_new_responses()"""
975 commit1 = Commit('abcd')
976 commit1.subject = 'Subject 1'
977 commit2 = Commit('ef12')
978 commit2.subject = 'Subject 2'
979
Simon Glass232eefd2025-04-29 07:22:14 -0600980 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600981 patch1.parse_subject('[1/2] Subject 1')
982 patch1.name = patch1.raw_subject
983 patch1.content = 'This is my patch content'
984 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
985
986 patch1.comments = [comment1a]
987
Simon Glass232eefd2025-04-29 07:22:14 -0600988 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600989 patch2.parse_subject('[2/2] Subject 2')
990 patch2.name = patch2.raw_subject
991 patch2.content = 'Some other patch content'
992 comment2a = {
993 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
994 (self.mary, self.leb)}
995 comment2b = {'content': 'Reviewed-by: %s' % self.fred}
996 patch2.comments = [comment2a, comment2b]
997
998 # This test works by setting up commits and patch for use by the fake
999 # Rest API function _fake_patchwork2(). It calls various functions in
1000 # the status module after setting up tags in the commits, checking that
1001 # things behaves as expected
1002 self.commits = [commit1, commit2]
1003 self.patches = [patch1, patch2]
1004 count = 2
1005 new_rtag_list = [None] * count
Simon Glass2112d072020-10-29 21:46:38 -06001006 review_list = [None, None]
Simon Glass3db916d2020-10-29 21:46:35 -06001007
1008 # Check that the tags are picked up on the first patch
Simon Glass2112d072020-10-29 21:46:38 -06001009 status.find_new_responses(new_rtag_list, review_list, 0, commit1,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001010 patch1, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -06001011 self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}})
1012
1013 # Now the second patch
Simon Glass2112d072020-10-29 21:46:38 -06001014 status.find_new_responses(new_rtag_list, review_list, 1, commit2,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001015 patch2, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -06001016 self.assertEqual(new_rtag_list[1], {
1017 'Reviewed-by': {self.mary, self.fred},
1018 'Tested-by': {self.leb}})
1019
1020 # Now add some tags to the commit, which means they should not appear as
1021 # 'new' tags when scanning comments
1022 new_rtag_list = [None] * count
1023 commit1.rtags = {'Reviewed-by': {self.joe}}
Simon Glass2112d072020-10-29 21:46:38 -06001024 status.find_new_responses(new_rtag_list, review_list, 0, commit1,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001025 patch1, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -06001026 self.assertEqual(new_rtag_list[0], {})
1027
1028 # For the second commit, add Ed and Fred, so only Mary should be left
1029 commit2.rtags = {
1030 'Tested-by': {self.leb},
1031 'Reviewed-by': {self.fred}}
Simon Glass2112d072020-10-29 21:46:38 -06001032 status.find_new_responses(new_rtag_list, review_list, 1, commit2,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001033 patch2, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -06001034 self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}})
1035
1036 # Check that the output patches expectations:
1037 # 1 Subject 1
1038 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1039 # 2 Subject 2
1040 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1041 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1042 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1043 # 1 new response available in patchwork
1044
1045 series = Series()
1046 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001047 terminal.set_print_test_mode()
Simon Glass2112d072020-10-29 21:46:38 -06001048 status.check_patchwork_status(series, '1234', None, None, False, False,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001049 None, self._fake_patchwork2)
Simon Glass02811582022-01-29 14:14:18 -07001050 lines = iter(terminal.get_print_test_lines())
Simon Glass3db916d2020-10-29 21:46:35 -06001051 col = terminal.Color()
1052 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
1053 next(lines))
1054 self.assertEqual(
1055 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
1056 bright=False),
1057 next(lines))
1058 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False),
1059 next(lines))
1060
1061 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE),
1062 next(lines))
1063 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001064 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001065 bright=False),
1066 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -06001067 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -06001068 next(lines))
1069 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001070 terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001071 bright=False),
1072 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -06001073 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -06001074 next(lines))
1075 self.assertEqual(
1076 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1077 next(lines))
1078 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1079 next(lines))
1080 self.assertEqual(terminal.PrintLine(
Simon Glassd0a0a582020-10-29 21:46:36 -06001081 '1 new response available in patchwork (use -d to write them to a new branch)',
1082 None), next(lines))
1083
Simon Glassf9b03cf2020-11-03 13:54:14 -07001084 def _fake_patchwork3(self, url, subpath):
Simon Glassd0a0a582020-10-29 21:46:36 -06001085 """Fake Patchwork server for the function below
1086
1087 This handles accessing series, patches and comments, providing the data
1088 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -07001089
1090 Args:
1091 url (str): URL of patchwork server
1092 subpath (str): URL subpath to use
Simon Glassd0a0a582020-10-29 21:46:36 -06001093 """
1094 re_series = re.match(r'series/(\d*)/$', subpath)
1095 re_patch = re.match(r'patches/(\d*)/$', subpath)
1096 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
1097 if re_series:
1098 series_num = re_series.group(1)
1099 if series_num == '1234':
1100 return {'patches': self.patches}
1101 elif re_patch:
1102 patch_num = int(re_patch.group(1))
1103 patch = self.patches[patch_num - 1]
1104 return patch
1105 elif re_comments:
1106 patch_num = int(re_comments.group(1))
1107 patch = self.patches[patch_num - 1]
1108 return patch.comments
1109 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
1110
Simon Glassd85bb8f2022-01-29 14:14:09 -07001111 def test_create_branch(self):
Simon Glassd0a0a582020-10-29 21:46:36 -06001112 """Test operation of create_branch()"""
1113 repo = self.make_git_tree()
1114 branch = 'first'
1115 dest_branch = 'first2'
1116 count = 2
1117 gitdir = os.path.join(self.gitdir, '.git')
1118
1119 # Set up the test git tree. We use branch 'first' which has two commits
1120 # in it
1121 series = patchstream.get_metadata_for_list(branch, gitdir, count)
1122 self.assertEqual(2, len(series.commits))
1123
Simon Glass232eefd2025-04-29 07:22:14 -06001124 patch1 = patchwork.Patch('1')
Simon Glassd0a0a582020-10-29 21:46:36 -06001125 patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
1126 patch1.name = patch1.raw_subject
1127 patch1.content = 'This is my patch content'
1128 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1129
1130 patch1.comments = [comment1a]
1131
Simon Glass232eefd2025-04-29 07:22:14 -06001132 patch2 = patchwork.Patch('2')
Simon Glassd0a0a582020-10-29 21:46:36 -06001133 patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
1134 patch2.name = patch2.raw_subject
1135 patch2.content = 'Some other patch content'
1136 comment2a = {
1137 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1138 (self.mary, self.leb)}
1139 comment2b = {
1140 'content': 'Reviewed-by: %s' % self.fred}
1141 patch2.comments = [comment2a, comment2b]
1142
1143 # This test works by setting up patches for use by the fake Rest API
1144 # function _fake_patchwork3(). The fake patch comments above should
1145 # result in new review tags that are collected and added to the commits
1146 # created in the destination branch.
1147 self.patches = [patch1, patch2]
1148 count = 2
1149
1150 # Expected output:
1151 # 1 i2c: I2C things
1152 # + Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1153 # 2 spi: SPI fixes
1154 # + Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1155 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1156 # + Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1157 # 4 new responses available in patchwork
1158 # 4 responses added from patchwork into new branch 'first2'
1159 # <unittest.result.TestResult run=8 errors=0 failures=0>
1160
Simon Glass02811582022-01-29 14:14:18 -07001161 terminal.set_print_test_mode()
Simon Glassd0a0a582020-10-29 21:46:36 -06001162 status.check_patchwork_status(series, '1234', branch, dest_branch,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001163 False, False, None, self._fake_patchwork3,
1164 repo)
Simon Glass02811582022-01-29 14:14:18 -07001165 lines = terminal.get_print_test_lines()
Simon Glassd0a0a582020-10-29 21:46:36 -06001166 self.assertEqual(12, len(lines))
1167 self.assertEqual(
1168 "4 responses added from patchwork into new branch 'first2'",
1169 lines[11].text)
1170
1171 # Check that the destination branch has the new tags
1172 new_series = patchstream.get_metadata_for_list(dest_branch, gitdir,
1173 count)
1174 self.assertEqual(
1175 {'Reviewed-by': {self.joe}},
1176 new_series.commits[0].rtags)
1177 self.assertEqual(
1178 {'Tested-by': {self.leb},
1179 'Reviewed-by': {self.fred, self.mary}},
1180 new_series.commits[1].rtags)
1181
1182 # Now check the actual test of the first commit message. We expect to
1183 # see the new tags immediately below the old ones.
1184 stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir)
1185 lines = iter([line.strip() for line in stdout.splitlines()
1186 if '-by:' in line])
1187
1188 # First patch should have the review tag
1189 self.assertEqual('Reviewed-by: %s' % self.joe, next(lines))
1190
1191 # Second patch should have the sign-off then the tested-by and two
1192 # reviewed-by tags
1193 self.assertEqual('Signed-off-by: %s' % self.leb, next(lines))
1194 self.assertEqual('Reviewed-by: %s' % self.fred, next(lines))
1195 self.assertEqual('Reviewed-by: %s' % self.mary, next(lines))
1196 self.assertEqual('Tested-by: %s' % self.leb, next(lines))
Simon Glassda8a2922020-10-29 21:46:37 -06001197
Simon Glassd85bb8f2022-01-29 14:14:09 -07001198 def test_parse_snippets(self):
Simon Glassda8a2922020-10-29 21:46:37 -06001199 """Test parsing of review snippets"""
1200 text = '''Hi Fred,
1201
1202This is a comment from someone.
1203
1204Something else
1205
1206On some recent date, Fred wrote:
1207> This is why I wrote the patch
1208> so here it is
1209
1210Now a comment about the commit message
1211A little more to say
1212
1213Even more
1214
1215> diff --git a/file.c b/file.c
1216> Some more code
1217> Code line 2
1218> Code line 3
1219> Code line 4
1220> Code line 5
1221> Code line 6
1222> Code line 7
1223> Code line 8
1224> Code line 9
1225
1226And another comment
1227
Simon Glassd85bb8f2022-01-29 14:14:09 -07001228> @@ -153,8 +143,13 @@ def check_patch(fname, show_types=False):
Simon Glassda8a2922020-10-29 21:46:37 -06001229> further down on the file
1230> and more code
1231> +Addition here
1232> +Another addition here
1233> codey
1234> more codey
1235
1236and another thing in same file
1237
1238> @@ -253,8 +243,13 @@
1239> with no function context
1240
1241one more thing
1242
1243> diff --git a/tools/patman/main.py b/tools/patman/main.py
1244> +line of code
1245now a very long comment in a different file
1246line2
1247line3
1248line4
1249line5
1250line6
1251line7
1252line8
1253'''
1254 pstrm = PatchStream.process_text(text, True)
1255 self.assertEqual([], pstrm.commit.warn)
1256
1257 # We expect to the filename and up to 5 lines of code context before
1258 # each comment. The 'On xxx wrote:' bit should be removed.
1259 self.assertEqual(
1260 [['Hi Fred,',
1261 'This is a comment from someone.',
1262 'Something else'],
1263 ['> This is why I wrote the patch',
1264 '> so here it is',
1265 'Now a comment about the commit message',
1266 'A little more to say', 'Even more'],
1267 ['> File: file.c', '> Code line 5', '> Code line 6',
1268 '> Code line 7', '> Code line 8', '> Code line 9',
1269 'And another comment'],
1270 ['> File: file.c',
Simon Glassd85bb8f2022-01-29 14:14:09 -07001271 '> Line: 153 / 143: def check_patch(fname, show_types=False):',
Simon Glassda8a2922020-10-29 21:46:37 -06001272 '> and more code', '> +Addition here', '> +Another addition here',
1273 '> codey', '> more codey', 'and another thing in same file'],
1274 ['> File: file.c', '> Line: 253 / 243',
1275 '> with no function context', 'one more thing'],
1276 ['> File: tools/patman/main.py', '> +line of code',
1277 'now a very long comment in a different file',
1278 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']],
1279 pstrm.snippets)
Simon Glass2112d072020-10-29 21:46:38 -06001280
Simon Glassd85bb8f2022-01-29 14:14:09 -07001281 def test_review_snippets(self):
Simon Glass2112d072020-10-29 21:46:38 -06001282 """Test showing of review snippets"""
1283 def _to_submitter(who):
1284 m_who = re.match('(.*) <(.*)>', who)
1285 return {
1286 'name': m_who.group(1),
1287 'email': m_who.group(2)
1288 }
1289
1290 commit1 = Commit('abcd')
1291 commit1.subject = 'Subject 1'
1292 commit2 = Commit('ef12')
1293 commit2.subject = 'Subject 2'
1294
Simon Glass232eefd2025-04-29 07:22:14 -06001295 patch1 = patchwork.Patch('1')
Simon Glass2112d072020-10-29 21:46:38 -06001296 patch1.parse_subject('[1/2] Subject 1')
1297 patch1.name = patch1.raw_subject
1298 patch1.content = 'This is my patch content'
1299 comment1a = {'submitter': _to_submitter(self.joe),
1300 'content': '''Hi Fred,
1301
1302On some date Fred wrote:
1303
1304> diff --git a/file.c b/file.c
1305> Some code
1306> and more code
1307
1308Here is my comment above the above...
1309
1310
1311Reviewed-by: %s
1312''' % self.joe}
1313
1314 patch1.comments = [comment1a]
1315
Simon Glass232eefd2025-04-29 07:22:14 -06001316 patch2 = patchwork.Patch('2')
Simon Glass2112d072020-10-29 21:46:38 -06001317 patch2.parse_subject('[2/2] Subject 2')
1318 patch2.name = patch2.raw_subject
1319 patch2.content = 'Some other patch content'
1320 comment2a = {
1321 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1322 (self.mary, self.leb)}
1323 comment2b = {'submitter': _to_submitter(self.fred),
1324 'content': '''Hi Fred,
1325
1326On some date Fred wrote:
1327
1328> diff --git a/tools/patman/commit.py b/tools/patman/commit.py
1329> @@ -41,6 +41,9 @@ class Commit:
1330> self.rtags = collections.defaultdict(set)
1331> self.warn = []
1332>
1333> + def __str__(self):
1334> + return self.subject
1335> +
Simon Glassd85bb8f2022-01-29 14:14:09 -07001336> def add_change(self, version, info):
Simon Glass2112d072020-10-29 21:46:38 -06001337> """Add a new change line to the change list for a version.
1338>
1339A comment
1340
1341Reviewed-by: %s
1342''' % self.fred}
1343 patch2.comments = [comment2a, comment2b]
1344
1345 # This test works by setting up commits and patch for use by the fake
1346 # Rest API function _fake_patchwork2(). It calls various functions in
1347 # the status module after setting up tags in the commits, checking that
1348 # things behaves as expected
1349 self.commits = [commit1, commit2]
1350 self.patches = [patch1, patch2]
1351
1352 # Check that the output patches expectations:
1353 # 1 Subject 1
1354 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1355 # 2 Subject 2
1356 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1357 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1358 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1359 # 1 new response available in patchwork
1360
1361 series = Series()
1362 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001363 terminal.set_print_test_mode()
Simon Glass2112d072020-10-29 21:46:38 -06001364 status.check_patchwork_status(series, '1234', None, None, False, True,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001365 None, self._fake_patchwork2)
Simon Glass02811582022-01-29 14:14:18 -07001366 lines = iter(terminal.get_print_test_lines())
Simon Glass2112d072020-10-29 21:46:38 -06001367 col = terminal.Color()
1368 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
1369 next(lines))
1370 self.assertEqual(
1371 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1372 next(lines))
1373 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(lines))
1374
1375 self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED),
1376 next(lines))
1377 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1378 self.assertEqual(terminal.PrintLine('', None), next(lines))
1379 self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA),
1380 next(lines))
1381 self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA),
1382 next(lines))
1383 self.assertEqual(terminal.PrintLine(' > and more code', col.MAGENTA),
1384 next(lines))
1385 self.assertEqual(terminal.PrintLine(
1386 ' Here is my comment above the above...', None), next(lines))
1387 self.assertEqual(terminal.PrintLine('', None), next(lines))
1388
1389 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE),
1390 next(lines))
1391 self.assertEqual(
1392 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1393 next(lines))
1394 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE),
1395 next(lines))
1396 self.assertEqual(
1397 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1398 next(lines))
1399 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1400 next(lines))
1401 self.assertEqual(
1402 terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False),
1403 next(lines))
1404 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE),
1405 next(lines))
1406
1407 self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED),
1408 next(lines))
1409 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1410 self.assertEqual(terminal.PrintLine('', None), next(lines))
1411 self.assertEqual(terminal.PrintLine(
1412 ' > File: tools/patman/commit.py', col.MAGENTA), next(lines))
1413 self.assertEqual(terminal.PrintLine(
1414 ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(lines))
1415 self.assertEqual(terminal.PrintLine(
1416 ' > + return self.subject', col.MAGENTA), next(lines))
1417 self.assertEqual(terminal.PrintLine(
1418 ' > +', col.MAGENTA), next(lines))
1419 self.assertEqual(
Simon Glassd85bb8f2022-01-29 14:14:09 -07001420 terminal.PrintLine(' > def add_change(self, version, info):',
Simon Glass2112d072020-10-29 21:46:38 -06001421 col.MAGENTA),
1422 next(lines))
1423 self.assertEqual(terminal.PrintLine(
1424 ' > """Add a new change line to the change list for a version.',
1425 col.MAGENTA), next(lines))
1426 self.assertEqual(terminal.PrintLine(
1427 ' >', col.MAGENTA), next(lines))
1428 self.assertEqual(terminal.PrintLine(
1429 ' A comment', None), next(lines))
1430 self.assertEqual(terminal.PrintLine('', None), next(lines))
1431
1432 self.assertEqual(terminal.PrintLine(
1433 '4 new responses available in patchwork (use -d to write them to a new branch)',
1434 None), next(lines))
Simon Glass6a222e62021-08-01 16:02:39 -06001435
Simon Glassd85bb8f2022-01-29 14:14:09 -07001436 def test_insert_tags(self):
Simon Glass6a222e62021-08-01 16:02:39 -06001437 """Test inserting of review tags"""
1438 msg = '''first line
1439second line.'''
1440 tags = [
1441 'Reviewed-by: Bin Meng <bmeng.cn@gmail.com>',
1442 'Tested-by: Bin Meng <bmeng.cn@gmail.com>'
1443 ]
1444 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.com>'
1445 tag_str = '\n'.join(tags)
1446
1447 new_msg = patchstream.insert_tags(msg, tags)
1448 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1449
1450 new_msg = patchstream.insert_tags(msg + '\n', tags)
1451 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1452
1453 msg += '\n\n' + signoff
1454 new_msg = patchstream.insert_tags(msg, tags)
1455 self.assertEqual(msg + '\n' + tag_str, new_msg)