blob: 3a34302fc57bc2d4cfb9d6fac8a6ba4dd75fba0f [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
Simon Glass25b91c12025-04-29 07:22:19 -06009import asyncio
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050010import contextlib
Simon Glassdf1bc5c2017-05-29 15:31:31 -060011import os
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050012import pathlib
Simon Glassdf1bc5c2017-05-29 15:31:31 -060013import re
14import shutil
15import sys
16import tempfile
17import unittest
18
Simon Glass3db916d2020-10-29 21:46:35 -060019
20from patman.commit import Commit
Simon Glass54f1c5b2020-07-05 21:41:50 -060021from patman import control
Simon Glassa997ea52020-04-17 18:09:04 -060022from patman import patchstream
Simon Glassa7fadab2020-10-29 21:46:26 -060023from patman.patchstream import PatchStream
Simon Glass232eefd2025-04-29 07:22:14 -060024from patman import patchwork
Simon Glassc0257982025-04-29 07:22:11 -060025from patman import send
Simon Glass3db916d2020-10-29 21:46:35 -060026from patman.series import Series
Simon Glassa997ea52020-04-17 18:09:04 -060027from patman import settings
Simon Glassba1b3b92025-02-09 14:26:00 -070028from u_boot_pylib import gitutil
Simon Glass131444f2023-02-23 18:18:04 -070029from u_boot_pylib import terminal
30from u_boot_pylib import tools
Simon Glassdf1bc5c2017-05-29 15:31:31 -060031
Tom Rini488ea972021-02-26 07:52:31 -050032import pygit2
33from patman import status
Simon Glassdf1bc5c2017-05-29 15:31:31 -060034
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050035PATMAN_DIR = pathlib.Path(__file__).parent
36TEST_DATA_DIR = PATMAN_DIR / 'test/'
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050037
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050038
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -050039@contextlib.contextmanager
40def directory_excursion(directory):
41 """Change directory to `directory` for a limited to the context block."""
42 current = os.getcwd()
43 try:
44 os.chdir(directory)
45 yield
46 finally:
47 os.chdir(current)
48
Maxim Cournoyer0331edb2022-12-19 17:32:39 -050049
Simon Glassdf1bc5c2017-05-29 15:31:31 -060050class TestFunctional(unittest.TestCase):
Simon Glasseb209e52020-10-29 21:46:15 -060051 """Functional tests for checking that patman behaves correctly"""
Simon Glass06202d62020-10-29 21:46:27 -060052 leb = (b'Lord Edmund Blackadd\xc3\xabr <weasel@blackadder.org>'.
53 decode('utf-8'))
Simon Glass3b762cc2020-10-29 21:46:28 -060054 fred = 'Fred Bloggs <f.bloggs@napier.net>'
55 joe = 'Joe Bloggs <joe@napierwallies.co.nz>'
56 mary = 'Mary Bloggs <mary@napierwallies.co.nz>'
Simon Glass3db916d2020-10-29 21:46:35 -060057 commits = None
58 patches = None
Simon Glassed831d12025-04-29 07:22:10 -060059 verbosity = False
60 preserve_outdirs = False
61
62 @classmethod
63 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
64 toolpath=None, verbosity=None, no_capture=False):
65 """Accept arguments controlling test execution
66
67 Args:
68 preserve_indir: not used
69 preserve_outdir: Preserve the output directories used by tests.
70 Each test has its own, so this is normally only useful when
71 running a single test.
72 toolpath: not used
73 """
74 cls.preserve_outdirs = preserve_outdirs
75 cls.toolpath = toolpath
76 cls.verbosity = verbosity
77 cls.no_capture = no_capture
Simon Glass06202d62020-10-29 21:46:27 -060078
Simon Glassdf1bc5c2017-05-29 15:31:31 -060079 def setUp(self):
80 self.tmpdir = tempfile.mkdtemp(prefix='patman.')
Simon Glass54f1c5b2020-07-05 21:41:50 -060081 self.gitdir = os.path.join(self.tmpdir, 'git')
82 self.repo = None
Simon Glassdf1bc5c2017-05-29 15:31:31 -060083
84 def tearDown(self):
Simon Glassed831d12025-04-29 07:22:10 -060085 if self.preserve_outdirs:
86 print(f'Output dir: {self.tmpdir}')
87 else:
88 shutil.rmtree(self.tmpdir)
Simon Glass02811582022-01-29 14:14:18 -070089 terminal.set_print_test_mode(False)
Simon Glassdf1bc5c2017-05-29 15:31:31 -060090
91 @staticmethod
Simon Glasseb209e52020-10-29 21:46:15 -060092 def _get_path(fname):
93 """Get the path to a test file
94
95 Args:
96 fname (str): Filename to obtain
97
98 Returns:
99 str: Full path to file in the test directory
100 """
Maxim Cournoyer0331edb2022-12-19 17:32:39 -0500101 return TEST_DATA_DIR / fname
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600102
103 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600104 def _get_text(cls, fname):
105 """Read a file as text
106
107 Args:
108 fname (str): Filename to read
109
110 Returns:
111 str: Contents of file
112 """
113 return open(cls._get_path(fname), encoding='utf-8').read()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600114
115 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600116 def _get_patch_name(cls, subject):
117 """Get the filename of a patch given its subject
118
119 Args:
120 subject (str): Patch subject
121
122 Returns:
123 str: Filename for that patch
124 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600125 fname = re.sub('[ :]', '-', subject)
126 return fname.replace('--', '-')
127
Simon Glasseb209e52020-10-29 21:46:15 -0600128 def _create_patches_for_test(self, series):
129 """Create patch files for use by tests
130
131 This copies patch files from the test directory as needed by the series
132
133 Args:
134 series (Series): Series containing commits to convert
135
136 Returns:
137 tuple:
138 str: Cover-letter filename, or None if none
139 fname_list: list of str, each a patch filename
140 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600141 cover_fname = None
142 fname_list = []
143 for i, commit in enumerate(series.commits):
Simon Glasseb209e52020-10-29 21:46:15 -0600144 clean_subject = self._get_patch_name(commit.subject)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600145 src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
146 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600147 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600148 fname_list.append(fname)
149 if series.get('cover'):
150 src_fname = '0000-cover-letter.patch'
151 cover_fname = os.path.join(self.tmpdir, src_fname)
152 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600153 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600154
155 return cover_fname, fname_list
156
Simon Glassd85bb8f2022-01-29 14:14:09 -0700157 def test_basic(self):
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600158 """Tests the basic flow of patman
159
160 This creates a series from some hard-coded patches build from a simple
161 tree with the following metadata in the top commit:
162
163 Series-to: u-boot
164 Series-prefix: RFC
Sean Andersondc1cd132021-10-22 19:07:04 -0400165 Series-postfix: some-branch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600166 Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
167 Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Sean Andersoncf13b862020-05-04 16:28:36 -0400168 Series-version: 3
169 Patch-cc: fred
170 Series-process-log: sort, uniq
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600171 Series-changes: 4
172 - Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400173 - Multi
174 line
175 change
176
177 Commit-changes: 2
178 - Changes only for this commit
179
Simon Glassf1aab6f2025-04-29 07:22:07 -0600180 Cover-changes: 4
Sean Andersoncf13b862020-05-04 16:28:36 -0400181 - Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600182
183 Cover-letter:
184 test: A test patch series
185 This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400186 letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600187 works
188 END
189
190 and this in the first commit:
191
Sean Andersoncf13b862020-05-04 16:28:36 -0400192 Commit-changes: 2
193 - second revision change
194
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600195 Series-notes:
196 some notes
197 about some things
198 from the first commit
199 END
200
201 Commit-notes:
202 Some notes about
203 the first commit
204 END
205
206 with the following commands:
207
208 git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
209 git format-patch --subject-prefix RFC --cover-letter HEAD~2
210 mv 00* /path/to/tools/patman/test
211
212 It checks these aspects:
213 - git log can be processed by patchstream
214 - emailing patches uses the correct command
215 - CC file has information on each commit
216 - cover letter has the expected text and subject
217 - each patch has the correct subject
218 - dry-run information prints out correctly
219 - unicode is handled correctly
Sean Andersondc1cd132021-10-22 19:07:04 -0400220 - Series-to, Series-cc, Series-prefix, Series-postfix, Cover-letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600221 - Cover-letter-cc, Series-version, Series-changes, Series-notes
222 - Commit-notes
223 """
224 process_tags = True
Simon Glass1f975b92021-01-23 08:56:15 -0700225 ignore_bad_tags = False
Simon Glass4f817892019-05-14 15:53:53 -0600226 stefan = b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600227 rick = 'Richard III <richard@palace.gov>'
Simon Glass4f817892019-05-14 15:53:53 -0600228 mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600229 add_maintainers = [stefan, rick]
230 dry_run = True
231 in_reply_to = mel
232 count = 2
Simon Glass5efa3662025-04-07 22:51:45 +1200233 alias = {
Simon Glass95745aa2020-10-29 21:46:13 -0600234 'fdt': ['simon'],
235 'u-boot': ['u-boot@lists.denx.de'],
Simon Glass06202d62020-10-29 21:46:27 -0600236 'simon': [self.leb],
Simon Glass3b762cc2020-10-29 21:46:28 -0600237 'fred': [self.fred],
Sean Anderson25978092024-04-18 22:36:31 -0400238 'joe': [self.joe],
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600239 }
240
Simon Glasseb209e52020-10-29 21:46:15 -0600241 text = self._get_text('test01.txt')
Simon Glass93f61c02020-10-29 21:46:19 -0600242 series = patchstream.get_metadata_for_test(text)
Simon Glass414f1e02025-02-27 12:27:30 -0700243 series.base_commit = Commit('1a44532')
244 series.branch = 'mybranch'
Simon Glasseb209e52020-10-29 21:46:15 -0600245 cover_fname, args = self._create_patches_for_test(series)
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500246 get_maintainer_script = str(pathlib.Path(__file__).parent.parent.parent
247 / 'get_maintainer.pl') + ' --norolestats'
Simon Glass14d64e32025-04-29 07:21:59 -0600248 with terminal.capture() as out:
Simon Glass93f61c02020-10-29 21:46:19 -0600249 patchstream.fix_patches(series, args)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600250 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -0600251 patchstream.insert_cover_letter(cover_fname, series, count)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600252 series.DoChecks()
253 cc_file = series.MakeCcFile(process_tags, cover_fname,
Chris Packhamb84fb482018-06-07 20:45:06 +1200254 not ignore_bad_tags, add_maintainers,
Simon Glass9938b7b2025-04-07 22:51:46 +1200255 None, get_maintainer_script, alias)
Simon Glass761648b2022-01-29 14:14:11 -0700256 cmd = gitutil.email_patches(
Simon Glass95745aa2020-10-29 21:46:13 -0600257 series, cover_fname, args, dry_run, not ignore_bad_tags,
Simon Glass5efa3662025-04-07 22:51:45 +1200258 cc_file, alias, in_reply_to=in_reply_to, thread=None)
Simon Glass32f12a7e2025-04-07 22:51:47 +1200259 series.ShowActions(args, cmd, process_tags, alias)
Simon Glassf544a2d2019-10-31 07:42:51 -0600260 cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600261 os.remove(cc_file)
262
Simon Glass42e3d392020-10-29 21:46:29 -0600263 lines = iter(out[0].getvalue().splitlines())
264 self.assertEqual('Cleaned %s patches' % len(series.commits),
265 next(lines))
266 self.assertEqual('Change log missing for v2', next(lines))
267 self.assertEqual('Change log missing for v3', next(lines))
268 self.assertEqual('Change log for unknown version v4', next(lines))
269 self.assertEqual("Alias 'pci' not found", next(lines))
Simon Glass620639c2023-03-08 10:52:54 -0800270 while next(lines) != 'Cc processing complete':
271 pass
Simon Glass42e3d392020-10-29 21:46:29 -0600272 self.assertIn('Dry run', next(lines))
273 self.assertEqual('', next(lines))
274 self.assertIn('Send a total of %d patches' % count, next(lines))
275 prev = next(lines)
276 for i, commit in enumerate(series.commits):
277 self.assertEqual(' %s' % args[i], prev)
278 while True:
279 prev = next(lines)
280 if 'Cc:' not in prev:
281 break
282 self.assertEqual('To: u-boot@lists.denx.de', prev)
Simon Glass9dfb3112020-11-08 20:36:18 -0700283 self.assertEqual('Cc: %s' % stefan, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600284 self.assertEqual('Version: 3', next(lines))
285 self.assertEqual('Prefix:\t RFC', next(lines))
Sean Andersondc1cd132021-10-22 19:07:04 -0400286 self.assertEqual('Postfix:\t some-branch', next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600287 self.assertEqual('Cover: 4 lines', next(lines))
288 self.assertEqual(' Cc: %s' % self.fred, next(lines))
Sean Anderson25978092024-04-18 22:36:31 -0400289 self.assertEqual(' Cc: %s' % self.joe, next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700290 self.assertEqual(' Cc: %s' % self.leb,
Simon Glass42e3d392020-10-29 21:46:29 -0600291 next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700292 self.assertEqual(' Cc: %s' % mel, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600293 self.assertEqual(' Cc: %s' % rick, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600294 expected = ('Git command: git send-email --annotate '
295 '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
Simon Glass1ee91c12020-11-03 13:54:10 -0700296 '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600297 % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
Simon Glass4f817892019-05-14 15:53:53 -0600298 ' '.join(args)))
Simon Glass9dfb3112020-11-08 20:36:18 -0700299 self.assertEqual(expected, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600300
Simon Glass9dfb3112020-11-08 20:36:18 -0700301 self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0])
Simon Glass95745aa2020-10-29 21:46:13 -0600302 self.assertEqual(
Sean Anderson25978092024-04-18 22:36:31 -0400303 '%s %s\0%s\0%s\0%s\0%s' % (args[1], self.fred, self.joe, self.leb,
304 rick, stefan),
Simon Glass9dfb3112020-11-08 20:36:18 -0700305 cc_lines[1])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600306
307 expected = '''
308This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400309letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600310works
311
312some notes
313about some things
314from the first commit
315
316Changes in v4:
Sean Andersoncf13b862020-05-04 16:28:36 -0400317- Multi
318 line
319 change
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600320- Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400321- Some notes for the cover letter
Sean Andersone45678c2024-04-18 22:36:32 -0400322- fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600323
324Simon Glass (2):
325 pci: Correct cast for sandbox
Siva Durga Prasad Paladugub3d55ea2018-07-16 15:56:11 +0530326 fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600327
328 cmd/pci.c | 3 ++-
329 fs/fat/fat.c | 1 +
330 lib/efi_loader/efi_memory.c | 1 +
331 lib/fdtdec.c | 3 ++-
332 4 files changed, 6 insertions(+), 2 deletions(-)
333
334--\x20
3352.7.4
336
Simon Glass414f1e02025-02-27 12:27:30 -0700337base-commit: 1a44532
338branch: mybranch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600339'''
Simon Glassf544a2d2019-10-31 07:42:51 -0600340 lines = open(cover_fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600341 self.assertEqual(
Sean Andersondc1cd132021-10-22 19:07:04 -0400342 'Subject: [RFC PATCH some-branch v3 0/2] test: A test patch series',
Simon Glass95745aa2020-10-29 21:46:13 -0600343 lines[3])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600344 self.assertEqual(expected.splitlines(), lines[7:])
345
346 for i, fname in enumerate(args):
Simon Glassf544a2d2019-10-31 07:42:51 -0600347 lines = open(fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600348 subject = [line for line in lines if line.startswith('Subject')]
349 self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
350 subject[0][:18])
Sean Andersoncf13b862020-05-04 16:28:36 -0400351
352 # Check that we got our commit notes
353 start = 0
354 expected = ''
355
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600356 if i == 0:
Sean Andersoncf13b862020-05-04 16:28:36 -0400357 start = 17
358 expected = '''---
359Some notes about
360the first commit
361
362(no changes since v2)
363
364Changes in v2:
365- second revision change'''
366 elif i == 1:
367 start = 17
368 expected = '''---
369
370Changes in v4:
371- Multi
372 line
373 change
Sean Andersone45678c2024-04-18 22:36:32 -0400374- New
Sean Andersoncf13b862020-05-04 16:28:36 -0400375- Some changes
376
377Changes in v2:
378- Changes only for this commit'''
379
380 if expected:
381 expected = expected.splitlines()
382 self.assertEqual(expected, lines[start:(start+len(expected))])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600383
Simon Glassda1a6ec2025-03-28 07:02:20 -0600384 def test_base_commit(self):
385 """Test adding a base commit with no cover letter"""
386 orig_text = self._get_text('test01.txt')
387 pos = orig_text.index('commit 5ab48490f03051875ab13d288a4bf32b507d76fd')
388 text = orig_text[:pos]
389 series = patchstream.get_metadata_for_test(text)
390 series.base_commit = Commit('1a44532')
391 series.branch = 'mybranch'
392 cover_fname, args = self._create_patches_for_test(series)
393 self.assertFalse(cover_fname)
Simon Glass14d64e32025-04-29 07:21:59 -0600394 with terminal.capture() as out:
Simon Glassda1a6ec2025-03-28 07:02:20 -0600395 patchstream.fix_patches(series, args, insert_base_commit=True)
396 self.assertEqual('Cleaned 1 patch\n', out[0].getvalue())
397 lines = tools.read_file(args[0], binary=False).splitlines()
398 pos = lines.index('-- ')
399
400 # We expect these lines at the end:
401 # -- (with trailing space)
402 # 2.7.4
403 # (empty)
404 # base-commit: xxx
405 # branch: xxx
406 self.assertEqual('base-commit: 1a44532', lines[pos + 3])
407 self.assertEqual('branch: mybranch', lines[pos + 4])
408
Simon Glass54f1c5b2020-07-05 21:41:50 -0600409 def make_commit_with_file(self, subject, body, fname, text):
410 """Create a file and add it to the git repo with a new commit
411
412 Args:
413 subject (str): Subject for the commit
414 body (str): Body text of the commit
415 fname (str): Filename of file to create
416 text (str): Text to put into the file
417 """
418 path = os.path.join(self.gitdir, fname)
Simon Glass80025522022-01-29 14:14:04 -0700419 tools.write_file(path, text, binary=False)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600420 index = self.repo.index
421 index.add(fname)
Simon Glass547cba62022-02-11 13:23:18 -0700422 # pylint doesn't seem to find this
423 # pylint: disable=E1101
Simon Glass95745aa2020-10-29 21:46:13 -0600424 author = pygit2.Signature('Test user', 'test@email.com')
Simon Glass54f1c5b2020-07-05 21:41:50 -0600425 committer = author
426 tree = index.write_tree()
427 message = subject + '\n' + body
428 self.repo.create_commit('HEAD', author, committer, message, tree,
429 [self.repo.head.target])
430
431 def make_git_tree(self):
432 """Make a simple git tree suitable for testing
433
434 It has three branches:
435 'base' has two commits: PCI, main
436 'first' has base as upstream and two more commits: I2C, SPI
437 'second' has base as upstream and three more: video, serial, bootm
438
439 Returns:
Simon Glasseb209e52020-10-29 21:46:15 -0600440 pygit2.Repository: repository
Simon Glass54f1c5b2020-07-05 21:41:50 -0600441 """
442 repo = pygit2.init_repository(self.gitdir)
443 self.repo = repo
444 new_tree = repo.TreeBuilder().write()
445
Simon Glass547cba62022-02-11 13:23:18 -0700446 # pylint doesn't seem to find this
447 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600448 author = pygit2.Signature('Test user', 'test@email.com')
449 committer = author
Simon Glasseb209e52020-10-29 21:46:15 -0600450 _ = repo.create_commit('HEAD', author, committer, 'Created master',
451 new_tree, [])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600452
453 self.make_commit_with_file('Initial commit', '''
454Add a README
455
456''', 'README', '''This is the README file
457describing this project
458in very little detail''')
459
460 self.make_commit_with_file('pci: PCI implementation', '''
461Here is a basic PCI implementation
462
463''', 'pci.c', '''This is a file
464it has some contents
465and some more things''')
466 self.make_commit_with_file('main: Main program', '''
467Hello here is the second commit.
468''', 'main.c', '''This is the main file
469there is very little here
470but we can always add more later
471if we want to
472
473Series-to: u-boot
474Series-cc: Barry Crump <bcrump@whataroa.nz>
475''')
476 base_target = repo.revparse_single('HEAD')
477 self.make_commit_with_file('i2c: I2C things', '''
478This has some stuff to do with I2C
479''', 'i2c.c', '''And this is the file contents
480with some I2C-related things in it''')
481 self.make_commit_with_file('spi: SPI fixes', '''
482SPI needs some fixes
483and here they are
Simon Glassd0a0a582020-10-29 21:46:36 -0600484
485Signed-off-by: %s
486
487Series-to: u-boot
488Commit-notes:
489title of the series
490This is the cover letter for the series
491with various details
492END
493''' % self.leb, 'spi.c', '''Some fixes for SPI in this
Simon Glass54f1c5b2020-07-05 21:41:50 -0600494file to make SPI work
495better than before''')
496 first_target = repo.revparse_single('HEAD')
497
498 target = repo.revparse_single('HEAD~2')
Simon Glass547cba62022-02-11 13:23:18 -0700499 # pylint doesn't seem to find this
500 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600501 repo.reset(target.oid, pygit2.GIT_CHECKOUT_FORCE)
502 self.make_commit_with_file('video: Some video improvements', '''
503Fix up the video so that
504it looks more purple. Purple is
505a very nice colour.
506''', 'video.c', '''More purple here
507Purple and purple
508Even more purple
509Could not be any more purple''')
510 self.make_commit_with_file('serial: Add a serial driver', '''
511Here is the serial driver
512for my chip.
513
514Cover-letter:
515Series for my board
516This series implements support
517for my glorious board.
518END
Simon Glassa80986c2020-10-29 21:46:16 -0600519Series-links: 183237
Simon Glass54f1c5b2020-07-05 21:41:50 -0600520''', 'serial.c', '''The code for the
521serial driver is here''')
522 self.make_commit_with_file('bootm: Make it boot', '''
523This makes my board boot
524with a fix to the bootm
525command
526''', 'bootm.c', '''Fix up the bootm
527command to make the code as
528complicated as possible''')
529 second_target = repo.revparse_single('HEAD')
530
531 repo.branches.local.create('first', first_target)
532 repo.config.set_multivar('branch.first.remote', '', '.')
533 repo.config.set_multivar('branch.first.merge', '', 'refs/heads/base')
534
535 repo.branches.local.create('second', second_target)
536 repo.config.set_multivar('branch.second.remote', '', '.')
537 repo.config.set_multivar('branch.second.merge', '', 'refs/heads/base')
538
539 repo.branches.local.create('base', base_target)
540 return repo
541
Simon Glassd85bb8f2022-01-29 14:14:09 -0700542 def test_branch(self):
Simon Glass54f1c5b2020-07-05 21:41:50 -0600543 """Test creating patches from a branch"""
544 repo = self.make_git_tree()
545 target = repo.lookup_reference('refs/heads/first')
Simon Glass547cba62022-02-11 13:23:18 -0700546 # pylint doesn't seem to find this
547 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600548 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
549 control.setup()
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200550 orig_dir = os.getcwd()
Simon Glass54f1c5b2020-07-05 21:41:50 -0600551 try:
Simon Glass54f1c5b2020-07-05 21:41:50 -0600552 os.chdir(self.gitdir)
553
554 # Check that it can detect the current branch
Simon Glass761648b2022-01-29 14:14:11 -0700555 self.assertEqual(2, gitutil.count_commits_to_branch(None))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600556 col = terminal.Color()
Simon Glass14d64e32025-04-29 07:21:59 -0600557 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600558 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600559 col, branch=None, count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100560 ignore_binary=False, signoff=True)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600561 self.assertIsNone(cover_fname)
562 self.assertEqual(2, len(patch_files))
Simon Glass2eb4da72020-07-05 21:41:51 -0600563
564 # Check that it can detect a different branch
Simon Glass761648b2022-01-29 14:14:11 -0700565 self.assertEqual(3, gitutil.count_commits_to_branch('second'))
Simon Glass14d64e32025-04-29 07:21:59 -0600566 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600567 series, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600568 col, branch='second', count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100569 ignore_binary=False, signoff=True)
Simon Glass2eb4da72020-07-05 21:41:51 -0600570 self.assertIsNotNone(cover_fname)
571 self.assertEqual(3, len(patch_files))
Simon Glassb3bf4e12020-07-05 21:41:52 -0600572
Simon Glass414f1e02025-02-27 12:27:30 -0700573 cover = tools.read_file(cover_fname, binary=False)
574 lines = cover.splitlines()[-2:]
575 base = repo.lookup_reference('refs/heads/base').target
576 self.assertEqual(f'base-commit: {base}', lines[0])
577 self.assertEqual('branch: second', lines[1])
578
Simon Glassda1a6ec2025-03-28 07:02:20 -0600579 # Make sure that the base-commit is not present when it is in the
580 # cover letter
581 for fname in patch_files:
582 self.assertNotIn(b'base-commit:', tools.read_file(fname))
583
Simon Glassb3bf4e12020-07-05 21:41:52 -0600584 # Check that it can skip patches at the end
Simon Glass14d64e32025-04-29 07:21:59 -0600585 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600586 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600587 col, branch='second', count=-1, start=0, end=1,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100588 ignore_binary=False, signoff=True)
Simon Glassb3bf4e12020-07-05 21:41:52 -0600589 self.assertIsNotNone(cover_fname)
590 self.assertEqual(2, len(patch_files))
Simon Glass414f1e02025-02-27 12:27:30 -0700591
592 cover = tools.read_file(cover_fname, binary=False)
593 lines = cover.splitlines()[-2:]
594 base2 = repo.lookup_reference('refs/heads/second')
595 ref = base2.peel(pygit2.GIT_OBJ_COMMIT).parents[0].parents[0].id
596 self.assertEqual(f'base-commit: {ref}', lines[0])
597 self.assertEqual('branch: second', lines[1])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600598 finally:
599 os.chdir(orig_dir)
Simon Glass06202d62020-10-29 21:46:27 -0600600
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500601 def test_custom_get_maintainer_script(self):
602 """Validate that a custom get_maintainer script gets used."""
603 self.make_git_tree()
604 with directory_excursion(self.gitdir):
605 # Setup git.
606 os.environ['GIT_CONFIG_GLOBAL'] = '/dev/null'
607 os.environ['GIT_CONFIG_SYSTEM'] = '/dev/null'
608 tools.run('git', 'config', 'user.name', 'Dummy')
609 tools.run('git', 'config', 'user.email', 'dumdum@dummy.com')
610 tools.run('git', 'branch', 'upstream')
611 tools.run('git', 'branch', '--set-upstream-to=upstream')
612 tools.run('git', 'add', '.')
613 tools.run('git', 'commit', '-m', 'new commit')
614
615 # Setup patman configuration.
616 with open('.patman', 'w', buffering=1) as f:
617 f.write('[settings]\n'
618 'get_maintainer_script: dummy-script.sh\n'
Sean Andersona06df742024-04-18 22:36:30 -0400619 'check_patch: False\n'
620 'add_maintainers: True\n')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500621 with open('dummy-script.sh', 'w', buffering=1) as f:
622 f.write('#!/usr/bin/env python\n'
623 'print("hello@there.com")\n')
624 os.chmod('dummy-script.sh', 0x555)
625
626 # Finally, do the test
Simon Glass14d64e32025-04-29 07:21:59 -0600627 with terminal.capture():
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500628 output = tools.run(PATMAN_DIR / 'patman', '--dry-run')
629 # Assert the email address is part of the dry-run
630 # output.
631 self.assertIn('hello@there.com', output)
632
Simon Glassd85bb8f2022-01-29 14:14:09 -0700633 def test_tags(self):
Simon Glass06202d62020-10-29 21:46:27 -0600634 """Test collection of tags in a patchstream"""
635 text = '''This is a patch
636
637Signed-off-by: Terminator
Simon Glass3b762cc2020-10-29 21:46:28 -0600638Reviewed-by: %s
639Reviewed-by: %s
Simon Glass06202d62020-10-29 21:46:27 -0600640Tested-by: %s
Simon Glass3b762cc2020-10-29 21:46:28 -0600641''' % (self.joe, self.mary, self.leb)
Simon Glass06202d62020-10-29 21:46:27 -0600642 pstrm = PatchStream.process_text(text)
643 self.assertEqual(pstrm.commit.rtags, {
Simon Glass3b762cc2020-10-29 21:46:28 -0600644 'Reviewed-by': {self.joe, self.mary},
Simon Glass06202d62020-10-29 21:46:27 -0600645 'Tested-by': {self.leb}})
Simon Glass3b762cc2020-10-29 21:46:28 -0600646
Simon Glassd85bb8f2022-01-29 14:14:09 -0700647 def test_invalid_tag(self):
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200648 """Test invalid tag in a patchstream"""
649 text = '''This is a patch
650
651Serie-version: 2
652'''
653 with self.assertRaises(ValueError) as exc:
654 pstrm = PatchStream.process_text(text)
655 self.assertEqual("Line 3: Invalid tag = 'Serie-version: 2'",
656 str(exc.exception))
657
Simon Glassd85bb8f2022-01-29 14:14:09 -0700658 def test_missing_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600659 """Test a missing END tag"""
660 text = '''This is a patch
661
662Cover-letter:
663This is the title
664missing END after this line
665Signed-off-by: Fred
666'''
667 pstrm = PatchStream.process_text(text)
668 self.assertEqual(["Missing 'END' in section 'cover'"],
669 pstrm.commit.warn)
670
Simon Glassd85bb8f2022-01-29 14:14:09 -0700671 def test_missing_blank_line(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600672 """Test a missing blank line after a tag"""
673 text = '''This is a patch
674
675Series-changes: 2
676- First line of changes
677- Missing blank line after this line
678Signed-off-by: Fred
679'''
680 pstrm = PatchStream.process_text(text)
681 self.assertEqual(["Missing 'blank line' in section 'Series-changes'"],
682 pstrm.commit.warn)
683
Simon Glassd85bb8f2022-01-29 14:14:09 -0700684 def test_invalid_commit_tag(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600685 """Test an invalid Commit-xxx tag"""
686 text = '''This is a patch
687
688Commit-fred: testing
689'''
690 pstrm = PatchStream.process_text(text)
691 self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn)
692
Simon Glassd85bb8f2022-01-29 14:14:09 -0700693 def test_self_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600694 """Test a tested by tag by this user"""
695 test_line = 'Tested-by: %s@napier.com' % os.getenv('USER')
696 text = '''This is a patch
697
698%s
699''' % test_line
700 pstrm = PatchStream.process_text(text)
701 self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn)
702
Simon Glassd85bb8f2022-01-29 14:14:09 -0700703 def test_space_before_tab(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600704 """Test a space before a tab"""
705 text = '''This is a patch
706
707+ \tSomething
708'''
709 pstrm = PatchStream.process_text(text)
710 self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn)
711
Simon Glassd85bb8f2022-01-29 14:14:09 -0700712 def test_lines_after_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600713 """Test detecting lines after TEST= line"""
714 text = '''This is a patch
715
716TEST=sometest
717more lines
718here
719'''
720 pstrm = PatchStream.process_text(text)
721 self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn)
722
Simon Glassd85bb8f2022-01-29 14:14:09 -0700723 def test_blank_line_at_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600724 """Test detecting a blank line at the end of a file"""
725 text = '''This is a patch
726
727diff --git a/lib/fdtdec.c b/lib/fdtdec.c
728index c072e54..942244f 100644
729--- a/lib/fdtdec.c
730+++ b/lib/fdtdec.c
731@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void)
732 }
733
734 gd->ram_size = (phys_size_t)(res.end - res.start + 1);
735- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size);
736+ debug("%s: Initial DRAM size %llx\n", __func__,
737+ (unsigned long long)gd->ram_size);
738+
739diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
740
741--
7422.7.4
743
744 '''
745 pstrm = PatchStream.process_text(text)
746 self.assertEqual(
747 ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"],
748 pstrm.commit.warn)
Simon Glass1c1f2072020-10-29 21:46:34 -0600749
Simon Glassd85bb8f2022-01-29 14:14:09 -0700750 def test_no_upstream(self):
Simon Glass1c1f2072020-10-29 21:46:34 -0600751 """Test CountCommitsToBranch when there is no upstream"""
752 repo = self.make_git_tree()
753 target = repo.lookup_reference('refs/heads/base')
Simon Glass547cba62022-02-11 13:23:18 -0700754 # pylint doesn't seem to find this
755 # pylint: disable=E1101
Simon Glass1c1f2072020-10-29 21:46:34 -0600756 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
757
758 # Check that it can detect the current branch
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200759 orig_dir = os.getcwd()
Simon Glass1c1f2072020-10-29 21:46:34 -0600760 try:
Simon Glass1c1f2072020-10-29 21:46:34 -0600761 os.chdir(self.gitdir)
762 with self.assertRaises(ValueError) as exc:
Simon Glass761648b2022-01-29 14:14:11 -0700763 gitutil.count_commits_to_branch(None)
Simon Glass1c1f2072020-10-29 21:46:34 -0600764 self.assertIn(
765 "Failed to determine upstream: fatal: no upstream configured for branch 'base'",
766 str(exc.exception))
767 finally:
768 os.chdir(orig_dir)
Simon Glass3db916d2020-10-29 21:46:35 -0600769
770 @staticmethod
Simon Glass25b91c12025-04-29 07:22:19 -0600771 def _fake_patchwork(subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600772 """Fake Patchwork server for the function below
773
774 This handles accessing a series, providing a list consisting of a
775 single patch
Simon Glassf9b03cf2020-11-03 13:54:14 -0700776
777 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -0700778 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"""
Simon Glass25b91c12025-04-29 07:22:19 -0600790 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork)
Simon Glass14d64e32025-04-29 07:21:59 -0600791 with terminal.capture() as (_, err):
Simon Glass3729b8b2025-04-29 07:22:24 -0600792 patches = asyncio.run(status.check_status(1234, pwork))
Simon Glass27280f42025-04-29 07:22:17 -0600793 status.check_patch_count(0, len(patches))
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"""
Simon Glass25b91c12025-04-29 07:22:19 -0600799 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork)
Simon Glass3729b8b2025-04-29 07:22:24 -0600800 patches = asyncio.run(status.check_status(1234, pwork))
Simon Glass3db916d2020-10-29 21:46:35 -0600801 self.assertEqual(1, len(patches))
802 patch = patches[0]
803 self.assertEqual('1', patch.id)
804 self.assertEqual('Some patch', patch.raw_subject)
805
Simon Glassd85bb8f2022-01-29 14:14:09 -0700806 def test_parse_subject(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600807 """Test parsing of the patch subject"""
Simon Glass232eefd2025-04-29 07:22:14 -0600808 patch = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600809
810 # Simple patch not in a series
811 patch.parse_subject('Testing')
812 self.assertEqual('Testing', patch.raw_subject)
813 self.assertEqual('Testing', patch.subject)
814 self.assertEqual(1, patch.seq)
815 self.assertEqual(1, patch.count)
816 self.assertEqual(None, patch.prefix)
817 self.assertEqual(None, patch.version)
818
819 # First patch in a series
820 patch.parse_subject('[1/2] Testing')
821 self.assertEqual('[1/2] Testing', patch.raw_subject)
822 self.assertEqual('Testing', patch.subject)
823 self.assertEqual(1, patch.seq)
824 self.assertEqual(2, patch.count)
825 self.assertEqual(None, patch.prefix)
826 self.assertEqual(None, patch.version)
827
828 # Second patch in a series
829 patch.parse_subject('[2/2] Testing')
830 self.assertEqual('Testing', patch.subject)
831 self.assertEqual(2, patch.seq)
832 self.assertEqual(2, patch.count)
833 self.assertEqual(None, patch.prefix)
834 self.assertEqual(None, patch.version)
835
836 # RFC patch
837 patch.parse_subject('[RFC,3/7] Testing')
838 self.assertEqual('Testing', patch.subject)
839 self.assertEqual(3, patch.seq)
840 self.assertEqual(7, patch.count)
841 self.assertEqual('RFC', patch.prefix)
842 self.assertEqual(None, patch.version)
843
844 # Version patch
845 patch.parse_subject('[v2,3/7] Testing')
846 self.assertEqual('Testing', patch.subject)
847 self.assertEqual(3, patch.seq)
848 self.assertEqual(7, patch.count)
849 self.assertEqual(None, patch.prefix)
850 self.assertEqual('v2', patch.version)
851
852 # All fields
853 patch.parse_subject('[RESEND,v2,3/7] Testing')
854 self.assertEqual('Testing', patch.subject)
855 self.assertEqual(3, patch.seq)
856 self.assertEqual(7, patch.count)
857 self.assertEqual('RESEND', patch.prefix)
858 self.assertEqual('v2', patch.version)
859
860 # RFC only
861 patch.parse_subject('[RESEND] Testing')
862 self.assertEqual('Testing', patch.subject)
863 self.assertEqual(1, patch.seq)
864 self.assertEqual(1, patch.count)
865 self.assertEqual('RESEND', patch.prefix)
866 self.assertEqual(None, patch.version)
867
Simon Glassd85bb8f2022-01-29 14:14:09 -0700868 def test_compare_series(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600869 """Test operation of compare_with_series()"""
870 commit1 = Commit('abcd')
871 commit1.subject = 'Subject 1'
872 commit2 = Commit('ef12')
873 commit2.subject = 'Subject 2'
874 commit3 = Commit('3456')
875 commit3.subject = 'Subject 2'
876
Simon Glass232eefd2025-04-29 07:22:14 -0600877 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600878 patch1.subject = 'Subject 1'
Simon Glass232eefd2025-04-29 07:22:14 -0600879 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600880 patch2.subject = 'Subject 2'
Simon Glass232eefd2025-04-29 07:22:14 -0600881 patch3 = patchwork.Patch('3')
Simon Glass3db916d2020-10-29 21:46:35 -0600882 patch3.subject = 'Subject 2'
883
884 series = Series()
885 series.commits = [commit1]
886 patches = [patch1]
887 patch_for_commit, commit_for_patch, warnings = (
888 status.compare_with_series(series, patches))
889 self.assertEqual(1, len(patch_for_commit))
890 self.assertEqual(patch1, patch_for_commit[0])
891 self.assertEqual(1, len(commit_for_patch))
892 self.assertEqual(commit1, commit_for_patch[0])
893
894 series.commits = [commit1]
895 patches = [patch1, patch2]
896 patch_for_commit, commit_for_patch, warnings = (
897 status.compare_with_series(series, patches))
898 self.assertEqual(1, len(patch_for_commit))
899 self.assertEqual(patch1, patch_for_commit[0])
900 self.assertEqual(1, len(commit_for_patch))
901 self.assertEqual(commit1, commit_for_patch[0])
902 self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"],
903 warnings)
904
905 series.commits = [commit1, commit2]
906 patches = [patch1]
907 patch_for_commit, commit_for_patch, warnings = (
908 status.compare_with_series(series, patches))
909 self.assertEqual(1, len(patch_for_commit))
910 self.assertEqual(patch1, patch_for_commit[0])
911 self.assertEqual(1, len(commit_for_patch))
912 self.assertEqual(commit1, commit_for_patch[0])
913 self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"],
914 warnings)
915
916 series.commits = [commit1, commit2, commit3]
917 patches = [patch1, patch2]
918 patch_for_commit, commit_for_patch, warnings = (
919 status.compare_with_series(series, patches))
920 self.assertEqual(2, len(patch_for_commit))
921 self.assertEqual(patch1, patch_for_commit[0])
922 self.assertEqual(patch2, patch_for_commit[1])
923 self.assertEqual(1, len(commit_for_patch))
924 self.assertEqual(commit1, commit_for_patch[0])
925 self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')",
926 "Multiple commits match patch 2 ('Subject 2'):\n"
927 ' Subject 2\n Subject 2'],
928 warnings)
929
930 series.commits = [commit1, commit2]
931 patches = [patch1, patch2, patch3]
932 patch_for_commit, commit_for_patch, warnings = (
933 status.compare_with_series(series, patches))
934 self.assertEqual(1, len(patch_for_commit))
935 self.assertEqual(patch1, patch_for_commit[0])
936 self.assertEqual(2, len(commit_for_patch))
937 self.assertEqual(commit1, commit_for_patch[0])
938 self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n"
939 ' Subject 2\n Subject 2',
940 "Cannot find commit for patch 3 ('Subject 2')"],
941 warnings)
942
Simon Glass25b91c12025-04-29 07:22:19 -0600943 def _fake_patchwork2(self, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600944 """Fake Patchwork server for the function below
945
946 This handles accessing series, patches and comments, providing the data
947 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700948
949 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -0700950 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600951 """
952 re_series = re.match(r'series/(\d*)/$', subpath)
953 re_patch = re.match(r'patches/(\d*)/$', subpath)
954 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
955 if re_series:
956 series_num = re_series.group(1)
957 if series_num == '1234':
958 return {'patches': self.patches}
959 elif re_patch:
960 patch_num = int(re_patch.group(1))
961 patch = self.patches[patch_num - 1]
962 return patch
963 elif re_comments:
964 patch_num = int(re_comments.group(1))
965 patch = self.patches[patch_num - 1]
966 return patch.comments
967 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
968
Simon Glassd85bb8f2022-01-29 14:14:09 -0700969 def test_find_new_responses(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600970 """Test operation of find_new_responses()"""
971 commit1 = Commit('abcd')
972 commit1.subject = 'Subject 1'
973 commit2 = Commit('ef12')
974 commit2.subject = 'Subject 2'
975
Simon Glass232eefd2025-04-29 07:22:14 -0600976 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600977 patch1.parse_subject('[1/2] Subject 1')
978 patch1.name = patch1.raw_subject
979 patch1.content = 'This is my patch content'
980 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
981
982 patch1.comments = [comment1a]
983
Simon Glass232eefd2025-04-29 07:22:14 -0600984 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600985 patch2.parse_subject('[2/2] Subject 2')
986 patch2.name = patch2.raw_subject
987 patch2.content = 'Some other patch content'
988 comment2a = {
989 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
990 (self.mary, self.leb)}
991 comment2b = {'content': 'Reviewed-by: %s' % self.fred}
992 patch2.comments = [comment2a, comment2b]
993
994 # This test works by setting up commits and patch for use by the fake
995 # Rest API function _fake_patchwork2(). It calls various functions in
996 # the status module after setting up tags in the commits, checking that
997 # things behaves as expected
998 self.commits = [commit1, commit2]
999 self.patches = [patch1, patch2]
1000 count = 2
Simon Glass3db916d2020-10-29 21:46:35 -06001001
1002 # Check that the tags are picked up on the first patch
Simon Glass29771962025-04-29 07:22:20 -06001003 new_rtags, _ = status.process_reviews(patch1.content, patch1.comments,
1004 commit1.rtags)
1005 self.assertEqual(new_rtags, {'Reviewed-by': {self.joe}})
Simon Glass3db916d2020-10-29 21:46:35 -06001006
1007 # Now the second patch
Simon Glass29771962025-04-29 07:22:20 -06001008 new_rtags, _ = status.process_reviews(patch2.content, patch2.comments,
1009 commit2.rtags)
1010 self.assertEqual(new_rtags, {
Simon Glass3db916d2020-10-29 21:46:35 -06001011 'Reviewed-by': {self.mary, self.fred},
1012 'Tested-by': {self.leb}})
1013
1014 # Now add some tags to the commit, which means they should not appear as
1015 # 'new' tags when scanning comments
Simon Glass3db916d2020-10-29 21:46:35 -06001016 commit1.rtags = {'Reviewed-by': {self.joe}}
Simon Glass29771962025-04-29 07:22:20 -06001017 new_rtags, _ = status.process_reviews(patch1.content, patch1.comments,
1018 commit1.rtags)
1019 self.assertEqual(new_rtags, {})
Simon Glass3db916d2020-10-29 21:46:35 -06001020
1021 # For the second commit, add Ed and Fred, so only Mary should be left
1022 commit2.rtags = {
1023 'Tested-by': {self.leb},
1024 'Reviewed-by': {self.fred}}
Simon Glass29771962025-04-29 07:22:20 -06001025 new_rtags, _ = status.process_reviews(patch2.content, patch2.comments,
1026 commit2.rtags)
1027 self.assertEqual(new_rtags, {'Reviewed-by': {self.mary}})
Simon Glass3db916d2020-10-29 21:46:35 -06001028
1029 # Check that the output patches expectations:
1030 # 1 Subject 1
1031 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1032 # 2 Subject 2
1033 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1034 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1035 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1036 # 1 new response available in patchwork
1037
1038 series = Series()
1039 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001040 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001041 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2)
Simon Glassc100b262025-04-29 07:22:16 -06001042 status.check_and_show_status(series, '1234', None, None, False, False,
Simon Glass25b91c12025-04-29 07:22:19 -06001043 pwork)
Simon Glass02811582022-01-29 14:14:18 -07001044 lines = iter(terminal.get_print_test_lines())
Simon Glass3db916d2020-10-29 21:46:35 -06001045 col = terminal.Color()
Simon Glassd4d3fb42025-04-29 07:22:21 -06001046 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.YELLOW),
Simon Glass3db916d2020-10-29 21:46:35 -06001047 next(lines))
1048 self.assertEqual(
1049 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
1050 bright=False),
1051 next(lines))
1052 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False),
1053 next(lines))
1054
Simon Glassd4d3fb42025-04-29 07:22:21 -06001055 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.YELLOW),
Simon Glass3db916d2020-10-29 21:46:35 -06001056 next(lines))
1057 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001058 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001059 bright=False),
1060 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -06001061 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -06001062 next(lines))
1063 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001064 terminal.PrintLine(' Tested-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.leb, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -06001068 next(lines))
1069 self.assertEqual(
1070 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1071 next(lines))
1072 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1073 next(lines))
1074 self.assertEqual(terminal.PrintLine(
Simon Glassd0a0a582020-10-29 21:46:36 -06001075 '1 new response available in patchwork (use -d to write them to a new branch)',
1076 None), next(lines))
1077
Simon Glass25b91c12025-04-29 07:22:19 -06001078 def _fake_patchwork3(self, subpath):
Simon Glassd0a0a582020-10-29 21:46:36 -06001079 """Fake Patchwork server for the function below
1080
1081 This handles accessing series, patches and comments, providing the data
1082 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -07001083
1084 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -07001085 subpath (str): URL subpath to use
Simon Glassd0a0a582020-10-29 21:46:36 -06001086 """
1087 re_series = re.match(r'series/(\d*)/$', subpath)
1088 re_patch = re.match(r'patches/(\d*)/$', subpath)
1089 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
1090 if re_series:
1091 series_num = re_series.group(1)
1092 if series_num == '1234':
1093 return {'patches': self.patches}
1094 elif re_patch:
1095 patch_num = int(re_patch.group(1))
1096 patch = self.patches[patch_num - 1]
1097 return patch
1098 elif re_comments:
1099 patch_num = int(re_comments.group(1))
1100 patch = self.patches[patch_num - 1]
1101 return patch.comments
1102 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
1103
Simon Glassd85bb8f2022-01-29 14:14:09 -07001104 def test_create_branch(self):
Simon Glassd0a0a582020-10-29 21:46:36 -06001105 """Test operation of create_branch()"""
1106 repo = self.make_git_tree()
1107 branch = 'first'
1108 dest_branch = 'first2'
1109 count = 2
1110 gitdir = os.path.join(self.gitdir, '.git')
1111
1112 # Set up the test git tree. We use branch 'first' which has two commits
1113 # in it
1114 series = patchstream.get_metadata_for_list(branch, gitdir, count)
1115 self.assertEqual(2, len(series.commits))
1116
Simon Glass232eefd2025-04-29 07:22:14 -06001117 patch1 = patchwork.Patch('1')
Simon Glassd0a0a582020-10-29 21:46:36 -06001118 patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
1119 patch1.name = patch1.raw_subject
1120 patch1.content = 'This is my patch content'
1121 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1122
1123 patch1.comments = [comment1a]
1124
Simon Glass232eefd2025-04-29 07:22:14 -06001125 patch2 = patchwork.Patch('2')
Simon Glassd0a0a582020-10-29 21:46:36 -06001126 patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
1127 patch2.name = patch2.raw_subject
1128 patch2.content = 'Some other patch content'
1129 comment2a = {
1130 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1131 (self.mary, self.leb)}
1132 comment2b = {
1133 'content': 'Reviewed-by: %s' % self.fred}
1134 patch2.comments = [comment2a, comment2b]
1135
1136 # This test works by setting up patches for use by the fake Rest API
1137 # function _fake_patchwork3(). The fake patch comments above should
1138 # result in new review tags that are collected and added to the commits
1139 # created in the destination branch.
1140 self.patches = [patch1, patch2]
1141 count = 2
1142
1143 # Expected output:
1144 # 1 i2c: I2C things
1145 # + Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1146 # 2 spi: SPI fixes
1147 # + Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1148 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1149 # + Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1150 # 4 new responses available in patchwork
1151 # 4 responses added from patchwork into new branch 'first2'
1152 # <unittest.result.TestResult run=8 errors=0 failures=0>
1153
Simon Glass02811582022-01-29 14:14:18 -07001154 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001155 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork3)
Simon Glassc100b262025-04-29 07:22:16 -06001156 status.check_and_show_status(series, '1234', branch, dest_branch,
Simon Glass25b91c12025-04-29 07:22:19 -06001157 False, False, pwork, repo)
Simon Glass02811582022-01-29 14:14:18 -07001158 lines = terminal.get_print_test_lines()
Simon Glassd0a0a582020-10-29 21:46:36 -06001159 self.assertEqual(12, len(lines))
1160 self.assertEqual(
1161 "4 responses added from patchwork into new branch 'first2'",
1162 lines[11].text)
1163
1164 # Check that the destination branch has the new tags
1165 new_series = patchstream.get_metadata_for_list(dest_branch, gitdir,
1166 count)
1167 self.assertEqual(
1168 {'Reviewed-by': {self.joe}},
1169 new_series.commits[0].rtags)
1170 self.assertEqual(
1171 {'Tested-by': {self.leb},
1172 'Reviewed-by': {self.fred, self.mary}},
1173 new_series.commits[1].rtags)
1174
1175 # Now check the actual test of the first commit message. We expect to
1176 # see the new tags immediately below the old ones.
1177 stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir)
1178 lines = iter([line.strip() for line in stdout.splitlines()
1179 if '-by:' in line])
1180
1181 # First patch should have the review tag
1182 self.assertEqual('Reviewed-by: %s' % self.joe, next(lines))
1183
1184 # Second patch should have the sign-off then the tested-by and two
1185 # reviewed-by tags
1186 self.assertEqual('Signed-off-by: %s' % self.leb, next(lines))
1187 self.assertEqual('Reviewed-by: %s' % self.fred, next(lines))
1188 self.assertEqual('Reviewed-by: %s' % self.mary, next(lines))
1189 self.assertEqual('Tested-by: %s' % self.leb, next(lines))
Simon Glassda8a2922020-10-29 21:46:37 -06001190
Simon Glassd85bb8f2022-01-29 14:14:09 -07001191 def test_parse_snippets(self):
Simon Glassda8a2922020-10-29 21:46:37 -06001192 """Test parsing of review snippets"""
1193 text = '''Hi Fred,
1194
1195This is a comment from someone.
1196
1197Something else
1198
1199On some recent date, Fred wrote:
1200> This is why I wrote the patch
1201> so here it is
1202
1203Now a comment about the commit message
1204A little more to say
1205
1206Even more
1207
1208> diff --git a/file.c b/file.c
1209> Some more code
1210> Code line 2
1211> Code line 3
1212> Code line 4
1213> Code line 5
1214> Code line 6
1215> Code line 7
1216> Code line 8
1217> Code line 9
1218
1219And another comment
1220
Simon Glassd85bb8f2022-01-29 14:14:09 -07001221> @@ -153,8 +143,13 @@ def check_patch(fname, show_types=False):
Simon Glassda8a2922020-10-29 21:46:37 -06001222> further down on the file
1223> and more code
1224> +Addition here
1225> +Another addition here
1226> codey
1227> more codey
1228
1229and another thing in same file
1230
1231> @@ -253,8 +243,13 @@
1232> with no function context
1233
1234one more thing
1235
1236> diff --git a/tools/patman/main.py b/tools/patman/main.py
1237> +line of code
1238now a very long comment in a different file
1239line2
1240line3
1241line4
1242line5
1243line6
1244line7
1245line8
1246'''
1247 pstrm = PatchStream.process_text(text, True)
1248 self.assertEqual([], pstrm.commit.warn)
1249
1250 # We expect to the filename and up to 5 lines of code context before
1251 # each comment. The 'On xxx wrote:' bit should be removed.
1252 self.assertEqual(
1253 [['Hi Fred,',
1254 'This is a comment from someone.',
1255 'Something else'],
1256 ['> This is why I wrote the patch',
1257 '> so here it is',
1258 'Now a comment about the commit message',
1259 'A little more to say', 'Even more'],
1260 ['> File: file.c', '> Code line 5', '> Code line 6',
1261 '> Code line 7', '> Code line 8', '> Code line 9',
1262 'And another comment'],
1263 ['> File: file.c',
Simon Glassd85bb8f2022-01-29 14:14:09 -07001264 '> Line: 153 / 143: def check_patch(fname, show_types=False):',
Simon Glassda8a2922020-10-29 21:46:37 -06001265 '> and more code', '> +Addition here', '> +Another addition here',
1266 '> codey', '> more codey', 'and another thing in same file'],
1267 ['> File: file.c', '> Line: 253 / 243',
1268 '> with no function context', 'one more thing'],
1269 ['> File: tools/patman/main.py', '> +line of code',
1270 'now a very long comment in a different file',
1271 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']],
1272 pstrm.snippets)
Simon Glass2112d072020-10-29 21:46:38 -06001273
Simon Glassd85bb8f2022-01-29 14:14:09 -07001274 def test_review_snippets(self):
Simon Glass2112d072020-10-29 21:46:38 -06001275 """Test showing of review snippets"""
1276 def _to_submitter(who):
1277 m_who = re.match('(.*) <(.*)>', who)
1278 return {
1279 'name': m_who.group(1),
1280 'email': m_who.group(2)
1281 }
1282
1283 commit1 = Commit('abcd')
1284 commit1.subject = 'Subject 1'
1285 commit2 = Commit('ef12')
1286 commit2.subject = 'Subject 2'
1287
Simon Glass232eefd2025-04-29 07:22:14 -06001288 patch1 = patchwork.Patch('1')
Simon Glass2112d072020-10-29 21:46:38 -06001289 patch1.parse_subject('[1/2] Subject 1')
1290 patch1.name = patch1.raw_subject
1291 patch1.content = 'This is my patch content'
1292 comment1a = {'submitter': _to_submitter(self.joe),
1293 'content': '''Hi Fred,
1294
1295On some date Fred wrote:
1296
1297> diff --git a/file.c b/file.c
1298> Some code
1299> and more code
1300
1301Here is my comment above the above...
1302
1303
1304Reviewed-by: %s
1305''' % self.joe}
1306
1307 patch1.comments = [comment1a]
1308
Simon Glass232eefd2025-04-29 07:22:14 -06001309 patch2 = patchwork.Patch('2')
Simon Glass2112d072020-10-29 21:46:38 -06001310 patch2.parse_subject('[2/2] Subject 2')
1311 patch2.name = patch2.raw_subject
1312 patch2.content = 'Some other patch content'
1313 comment2a = {
1314 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1315 (self.mary, self.leb)}
1316 comment2b = {'submitter': _to_submitter(self.fred),
1317 'content': '''Hi Fred,
1318
1319On some date Fred wrote:
1320
1321> diff --git a/tools/patman/commit.py b/tools/patman/commit.py
1322> @@ -41,6 +41,9 @@ class Commit:
1323> self.rtags = collections.defaultdict(set)
1324> self.warn = []
1325>
1326> + def __str__(self):
1327> + return self.subject
1328> +
Simon Glassd85bb8f2022-01-29 14:14:09 -07001329> def add_change(self, version, info):
Simon Glass2112d072020-10-29 21:46:38 -06001330> """Add a new change line to the change list for a version.
1331>
1332A comment
1333
1334Reviewed-by: %s
1335''' % self.fred}
1336 patch2.comments = [comment2a, comment2b]
1337
1338 # This test works by setting up commits and patch for use by the fake
1339 # Rest API function _fake_patchwork2(). It calls various functions in
1340 # the status module after setting up tags in the commits, checking that
1341 # things behaves as expected
1342 self.commits = [commit1, commit2]
1343 self.patches = [patch1, patch2]
1344
1345 # Check that the output patches expectations:
1346 # 1 Subject 1
1347 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1348 # 2 Subject 2
1349 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1350 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1351 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1352 # 1 new response available in patchwork
1353
1354 series = Series()
1355 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001356 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001357 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2)
Simon Glassc100b262025-04-29 07:22:16 -06001358 status.check_and_show_status(series, '1234', None, None, False, True,
Simon Glass25b91c12025-04-29 07:22:19 -06001359 pwork)
Simon Glass02811582022-01-29 14:14:18 -07001360 lines = iter(terminal.get_print_test_lines())
Simon Glass2112d072020-10-29 21:46:38 -06001361 col = terminal.Color()
Simon Glassd4d3fb42025-04-29 07:22:21 -06001362 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.YELLOW),
Simon Glass2112d072020-10-29 21:46:38 -06001363 next(lines))
1364 self.assertEqual(
1365 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1366 next(lines))
1367 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(lines))
1368
1369 self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED),
1370 next(lines))
1371 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1372 self.assertEqual(terminal.PrintLine('', None), next(lines))
1373 self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA),
1374 next(lines))
1375 self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA),
1376 next(lines))
1377 self.assertEqual(terminal.PrintLine(' > and more code', col.MAGENTA),
1378 next(lines))
1379 self.assertEqual(terminal.PrintLine(
1380 ' Here is my comment above the above...', None), next(lines))
1381 self.assertEqual(terminal.PrintLine('', None), next(lines))
1382
Simon Glassd4d3fb42025-04-29 07:22:21 -06001383 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.YELLOW),
Simon Glass2112d072020-10-29 21:46:38 -06001384 next(lines))
1385 self.assertEqual(
1386 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1387 next(lines))
1388 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE),
1389 next(lines))
1390 self.assertEqual(
1391 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1392 next(lines))
1393 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1394 next(lines))
1395 self.assertEqual(
1396 terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False),
1397 next(lines))
1398 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE),
1399 next(lines))
1400
1401 self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED),
1402 next(lines))
1403 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1404 self.assertEqual(terminal.PrintLine('', None), next(lines))
1405 self.assertEqual(terminal.PrintLine(
1406 ' > File: tools/patman/commit.py', col.MAGENTA), next(lines))
1407 self.assertEqual(terminal.PrintLine(
1408 ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(lines))
1409 self.assertEqual(terminal.PrintLine(
1410 ' > + return self.subject', col.MAGENTA), next(lines))
1411 self.assertEqual(terminal.PrintLine(
1412 ' > +', col.MAGENTA), next(lines))
1413 self.assertEqual(
Simon Glassd85bb8f2022-01-29 14:14:09 -07001414 terminal.PrintLine(' > def add_change(self, version, info):',
Simon Glass2112d072020-10-29 21:46:38 -06001415 col.MAGENTA),
1416 next(lines))
1417 self.assertEqual(terminal.PrintLine(
1418 ' > """Add a new change line to the change list for a version.',
1419 col.MAGENTA), next(lines))
1420 self.assertEqual(terminal.PrintLine(
1421 ' >', col.MAGENTA), next(lines))
1422 self.assertEqual(terminal.PrintLine(
1423 ' A comment', None), next(lines))
1424 self.assertEqual(terminal.PrintLine('', None), next(lines))
1425
1426 self.assertEqual(terminal.PrintLine(
1427 '4 new responses available in patchwork (use -d to write them to a new branch)',
1428 None), next(lines))
Simon Glass6a222e62021-08-01 16:02:39 -06001429
Simon Glassd85bb8f2022-01-29 14:14:09 -07001430 def test_insert_tags(self):
Simon Glass6a222e62021-08-01 16:02:39 -06001431 """Test inserting of review tags"""
1432 msg = '''first line
1433second line.'''
1434 tags = [
1435 'Reviewed-by: Bin Meng <bmeng.cn@gmail.com>',
1436 'Tested-by: Bin Meng <bmeng.cn@gmail.com>'
1437 ]
1438 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.com>'
1439 tag_str = '\n'.join(tags)
1440
1441 new_msg = patchstream.insert_tags(msg, tags)
1442 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1443
1444 new_msg = patchstream.insert_tags(msg + '\n', tags)
1445 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1446
1447 msg += '\n\n' + signoff
1448 new_msg = patchstream.insert_tags(msg, tags)
1449 self.assertEqual(msg + '\n' + tag_str, new_msg)