blob: 00aed8786e8157501200b481ecd8fe8a4fdad15d [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 Glass30b21792025-05-08 05:36:20 +020019import pygit2
20
Simon Glass2584cc52025-05-08 06:01:28 +020021from u_boot_pylib import command
Simon Glass30b21792025-05-08 05:36:20 +020022from u_boot_pylib import gitutil
23from u_boot_pylib import terminal
24from u_boot_pylib import tools
Simon Glass3db916d2020-10-29 21:46:35 -060025
26from patman.commit import Commit
Simon Glass54f1c5b2020-07-05 21:41:50 -060027from patman import control
Simon Glassa997ea52020-04-17 18:09:04 -060028from patman import patchstream
Simon Glassa7fadab2020-10-29 21:46:26 -060029from patman.patchstream import PatchStream
Simon Glass232eefd2025-04-29 07:22:14 -060030from patman import patchwork
Simon Glassc0257982025-04-29 07:22:11 -060031from patman import send
Simon Glass3db916d2020-10-29 21:46:35 -060032from patman.series import Series
Tom Rini488ea972021-02-26 07:52:31 -050033from 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
Simon Glassb8ca4692025-05-08 05:26:16 +020062 # Fake patchwork info for testing
63 SERIES_ID_SECOND_V1 = 456
64 TITLE_SECOND = 'Series for my board'
65
Simon Glassed831d12025-04-29 07:22:10 -060066 @classmethod
67 def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
68 toolpath=None, verbosity=None, no_capture=False):
69 """Accept arguments controlling test execution
70
71 Args:
72 preserve_indir: not used
73 preserve_outdir: Preserve the output directories used by tests.
74 Each test has its own, so this is normally only useful when
75 running a single test.
76 toolpath: not used
77 """
78 cls.preserve_outdirs = preserve_outdirs
79 cls.toolpath = toolpath
80 cls.verbosity = verbosity
81 cls.no_capture = no_capture
Simon Glass06202d62020-10-29 21:46:27 -060082
Simon Glassdf1bc5c2017-05-29 15:31:31 -060083 def setUp(self):
84 self.tmpdir = tempfile.mkdtemp(prefix='patman.')
Simon Glass41dfb6e2025-05-08 05:13:35 +020085 self.gitdir = os.path.join(self.tmpdir, '.git')
Simon Glass54f1c5b2020-07-05 21:41:50 -060086 self.repo = None
Simon Glass2584cc52025-05-08 06:01:28 +020087 self._patman_pathname = sys.argv[0]
88 self._patman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glassdf1bc5c2017-05-29 15:31:31 -060089
90 def tearDown(self):
Simon Glassed831d12025-04-29 07:22:10 -060091 if self.preserve_outdirs:
92 print(f'Output dir: {self.tmpdir}')
93 else:
94 shutil.rmtree(self.tmpdir)
Simon Glass02811582022-01-29 14:14:18 -070095 terminal.set_print_test_mode(False)
Simon Glassdf1bc5c2017-05-29 15:31:31 -060096
97 @staticmethod
Simon Glasseb209e52020-10-29 21:46:15 -060098 def _get_path(fname):
99 """Get the path to a test file
100
101 Args:
102 fname (str): Filename to obtain
103
104 Returns:
105 str: Full path to file in the test directory
106 """
Maxim Cournoyer0331edb2022-12-19 17:32:39 -0500107 return TEST_DATA_DIR / fname
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600108
109 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600110 def _get_text(cls, fname):
111 """Read a file as text
112
113 Args:
114 fname (str): Filename to read
115
116 Returns:
117 str: Contents of file
118 """
119 return open(cls._get_path(fname), encoding='utf-8').read()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600120
121 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600122 def _get_patch_name(cls, subject):
123 """Get the filename of a patch given its subject
124
125 Args:
126 subject (str): Patch subject
127
128 Returns:
129 str: Filename for that patch
130 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600131 fname = re.sub('[ :]', '-', subject)
132 return fname.replace('--', '-')
133
Simon Glasseb209e52020-10-29 21:46:15 -0600134 def _create_patches_for_test(self, series):
135 """Create patch files for use by tests
136
137 This copies patch files from the test directory as needed by the series
138
139 Args:
140 series (Series): Series containing commits to convert
141
142 Returns:
143 tuple:
144 str: Cover-letter filename, or None if none
145 fname_list: list of str, each a patch filename
146 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600147 cover_fname = None
148 fname_list = []
149 for i, commit in enumerate(series.commits):
Simon Glasseb209e52020-10-29 21:46:15 -0600150 clean_subject = self._get_patch_name(commit.subject)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600151 src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
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 fname_list.append(fname)
155 if series.get('cover'):
156 src_fname = '0000-cover-letter.patch'
157 cover_fname = os.path.join(self.tmpdir, src_fname)
158 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600159 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600160
161 return cover_fname, fname_list
162
Simon Glassd85bb8f2022-01-29 14:14:09 -0700163 def test_basic(self):
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600164 """Tests the basic flow of patman
165
166 This creates a series from some hard-coded patches build from a simple
167 tree with the following metadata in the top commit:
168
169 Series-to: u-boot
170 Series-prefix: RFC
Sean Andersondc1cd132021-10-22 19:07:04 -0400171 Series-postfix: some-branch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600172 Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
173 Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Sean Andersoncf13b862020-05-04 16:28:36 -0400174 Series-version: 3
175 Patch-cc: fred
176 Series-process-log: sort, uniq
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600177 Series-changes: 4
178 - Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400179 - Multi
180 line
181 change
182
183 Commit-changes: 2
184 - Changes only for this commit
185
Simon Glassf1aab6f2025-04-29 07:22:07 -0600186 Cover-changes: 4
Sean Andersoncf13b862020-05-04 16:28:36 -0400187 - Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600188
189 Cover-letter:
190 test: A test patch series
191 This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400192 letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600193 works
194 END
195
196 and this in the first commit:
197
Sean Andersoncf13b862020-05-04 16:28:36 -0400198 Commit-changes: 2
199 - second revision change
200
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600201 Series-notes:
202 some notes
203 about some things
204 from the first commit
205 END
206
207 Commit-notes:
208 Some notes about
209 the first commit
210 END
211
212 with the following commands:
213
214 git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
215 git format-patch --subject-prefix RFC --cover-letter HEAD~2
216 mv 00* /path/to/tools/patman/test
217
218 It checks these aspects:
219 - git log can be processed by patchstream
220 - emailing patches uses the correct command
221 - CC file has information on each commit
222 - cover letter has the expected text and subject
223 - each patch has the correct subject
224 - dry-run information prints out correctly
225 - unicode is handled correctly
Sean Andersondc1cd132021-10-22 19:07:04 -0400226 - Series-to, Series-cc, Series-prefix, Series-postfix, Cover-letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600227 - Cover-letter-cc, Series-version, Series-changes, Series-notes
228 - Commit-notes
229 """
230 process_tags = True
Simon Glass1f975b92021-01-23 08:56:15 -0700231 ignore_bad_tags = False
Simon Glassb3080ec2025-05-08 04:58:49 +0200232 stefan = (b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'
233 .decode('utf-8'))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600234 rick = 'Richard III <richard@palace.gov>'
Simon Glass4f817892019-05-14 15:53:53 -0600235 mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600236 add_maintainers = [stefan, rick]
237 dry_run = True
238 in_reply_to = mel
239 count = 2
Simon Glass5efa3662025-04-07 22:51:45 +1200240 alias = {
Simon Glass95745aa2020-10-29 21:46:13 -0600241 'fdt': ['simon'],
242 'u-boot': ['u-boot@lists.denx.de'],
Simon Glass06202d62020-10-29 21:46:27 -0600243 'simon': [self.leb],
Simon Glass3b762cc2020-10-29 21:46:28 -0600244 'fred': [self.fred],
Sean Anderson25978092024-04-18 22:36:31 -0400245 'joe': [self.joe],
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600246 }
247
Simon Glasseb209e52020-10-29 21:46:15 -0600248 text = self._get_text('test01.txt')
Simon Glass93f61c02020-10-29 21:46:19 -0600249 series = patchstream.get_metadata_for_test(text)
Simon Glass414f1e02025-02-27 12:27:30 -0700250 series.base_commit = Commit('1a44532')
251 series.branch = 'mybranch'
Simon Glasseb209e52020-10-29 21:46:15 -0600252 cover_fname, args = self._create_patches_for_test(series)
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500253 get_maintainer_script = str(pathlib.Path(__file__).parent.parent.parent
254 / 'get_maintainer.pl') + ' --norolestats'
Simon Glass14d64e32025-04-29 07:21:59 -0600255 with terminal.capture() as out:
Simon Glass93f61c02020-10-29 21:46:19 -0600256 patchstream.fix_patches(series, args)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600257 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -0600258 patchstream.insert_cover_letter(cover_fname, series, count)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600259 series.DoChecks()
260 cc_file = series.MakeCcFile(process_tags, cover_fname,
Chris Packhamb84fb482018-06-07 20:45:06 +1200261 not ignore_bad_tags, add_maintainers,
Simon Glass9938b7b2025-04-07 22:51:46 +1200262 None, get_maintainer_script, alias)
Simon Glass761648b2022-01-29 14:14:11 -0700263 cmd = gitutil.email_patches(
Simon Glass95745aa2020-10-29 21:46:13 -0600264 series, cover_fname, args, dry_run, not ignore_bad_tags,
Simon Glass5efa3662025-04-07 22:51:45 +1200265 cc_file, alias, in_reply_to=in_reply_to, thread=None)
Simon Glass32f12a7e2025-04-07 22:51:47 +1200266 series.ShowActions(args, cmd, process_tags, alias)
Simon Glassf544a2d2019-10-31 07:42:51 -0600267 cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600268 os.remove(cc_file)
269
Simon Glassb3080ec2025-05-08 04:58:49 +0200270 itr = iter(out[0].getvalue().splitlines())
Simon Glass42e3d392020-10-29 21:46:29 -0600271 self.assertEqual('Cleaned %s patches' % len(series.commits),
Simon Glassb3080ec2025-05-08 04:58:49 +0200272 next(itr))
273 self.assertEqual('Change log missing for v2', next(itr))
274 self.assertEqual('Change log missing for v3', next(itr))
275 self.assertEqual('Change log for unknown version v4', next(itr))
276 self.assertEqual("Alias 'pci' not found", next(itr))
277 while next(itr) != 'Cc processing complete':
Simon Glass620639c2023-03-08 10:52:54 -0800278 pass
Simon Glassb3080ec2025-05-08 04:58:49 +0200279 self.assertIn('Dry run', next(itr))
280 self.assertEqual('', next(itr))
281 self.assertIn('Send a total of %d patches' % count, next(itr))
282 prev = next(itr)
Simon Glass30b21792025-05-08 05:36:20 +0200283 for i in range(len(series.commits)):
Simon Glass42e3d392020-10-29 21:46:29 -0600284 self.assertEqual(' %s' % args[i], prev)
285 while True:
Simon Glassb3080ec2025-05-08 04:58:49 +0200286 prev = next(itr)
Simon Glass42e3d392020-10-29 21:46:29 -0600287 if 'Cc:' not in prev:
288 break
289 self.assertEqual('To: u-boot@lists.denx.de', prev)
Simon Glassb3080ec2025-05-08 04:58:49 +0200290 self.assertEqual('Cc: %s' % stefan, next(itr))
291 self.assertEqual('Version: 3', next(itr))
292 self.assertEqual('Prefix:\t RFC', next(itr))
293 self.assertEqual('Postfix:\t some-branch', next(itr))
294 self.assertEqual('Cover: 4 lines', next(itr))
295 self.assertEqual(' Cc: %s' % self.fred, next(itr))
296 self.assertEqual(' Cc: %s' % self.joe, next(itr))
Simon Glass9dfb3112020-11-08 20:36:18 -0700297 self.assertEqual(' Cc: %s' % self.leb,
Simon Glassb3080ec2025-05-08 04:58:49 +0200298 next(itr))
299 self.assertEqual(' Cc: %s' % mel, next(itr))
300 self.assertEqual(' Cc: %s' % rick, next(itr))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600301 expected = ('Git command: git send-email --annotate '
Simon Glassa8ba0792025-05-08 04:38:30 +0200302 '--in-reply-to="%s" --to u-boot@lists.denx.de '
Simon Glass1ee91c12020-11-03 13:54:10 -0700303 '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600304 % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
Simon Glass4f817892019-05-14 15:53:53 -0600305 ' '.join(args)))
Simon Glassb3080ec2025-05-08 04:58:49 +0200306 self.assertEqual(expected, next(itr))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600307
Simon Glass9dfb3112020-11-08 20:36:18 -0700308 self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0])
Simon Glass95745aa2020-10-29 21:46:13 -0600309 self.assertEqual(
Sean Anderson25978092024-04-18 22:36:31 -0400310 '%s %s\0%s\0%s\0%s\0%s' % (args[1], self.fred, self.joe, self.leb,
311 rick, stefan),
Simon Glass9dfb3112020-11-08 20:36:18 -0700312 cc_lines[1])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600313
314 expected = '''
315This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400316letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600317works
318
319some notes
320about some things
321from the first commit
322
323Changes in v4:
Sean Andersoncf13b862020-05-04 16:28:36 -0400324- Multi
325 line
326 change
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600327- Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400328- Some notes for the cover letter
Sean Andersone45678c2024-04-18 22:36:32 -0400329- fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600330
331Simon Glass (2):
332 pci: Correct cast for sandbox
Siva Durga Prasad Paladugub3d55ea2018-07-16 15:56:11 +0530333 fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600334
335 cmd/pci.c | 3 ++-
336 fs/fat/fat.c | 1 +
337 lib/efi_loader/efi_memory.c | 1 +
338 lib/fdtdec.c | 3 ++-
339 4 files changed, 6 insertions(+), 2 deletions(-)
340
341--\x20
3422.7.4
343
Simon Glass414f1e02025-02-27 12:27:30 -0700344base-commit: 1a44532
345branch: mybranch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600346'''
Simon Glassf544a2d2019-10-31 07:42:51 -0600347 lines = open(cover_fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600348 self.assertEqual(
Sean Andersondc1cd132021-10-22 19:07:04 -0400349 'Subject: [RFC PATCH some-branch v3 0/2] test: A test patch series',
Simon Glass95745aa2020-10-29 21:46:13 -0600350 lines[3])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600351 self.assertEqual(expected.splitlines(), lines[7:])
352
353 for i, fname in enumerate(args):
Simon Glassf544a2d2019-10-31 07:42:51 -0600354 lines = open(fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600355 subject = [line for line in lines if line.startswith('Subject')]
356 self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
357 subject[0][:18])
Sean Andersoncf13b862020-05-04 16:28:36 -0400358
359 # Check that we got our commit notes
360 start = 0
361 expected = ''
362
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600363 if i == 0:
Sean Andersoncf13b862020-05-04 16:28:36 -0400364 start = 17
365 expected = '''---
366Some notes about
367the first commit
368
369(no changes since v2)
370
371Changes in v2:
372- second revision change'''
373 elif i == 1:
374 start = 17
375 expected = '''---
376
377Changes in v4:
378- Multi
379 line
380 change
Sean Andersone45678c2024-04-18 22:36:32 -0400381- New
Sean Andersoncf13b862020-05-04 16:28:36 -0400382- Some changes
383
384Changes in v2:
385- Changes only for this commit'''
386
387 if expected:
388 expected = expected.splitlines()
389 self.assertEqual(expected, lines[start:(start+len(expected))])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600390
Simon Glassda1a6ec2025-03-28 07:02:20 -0600391 def test_base_commit(self):
392 """Test adding a base commit with no cover letter"""
393 orig_text = self._get_text('test01.txt')
Simon Glass30b21792025-05-08 05:36:20 +0200394 pos = orig_text.index(
395 'commit 5ab48490f03051875ab13d288a4bf32b507d76fd')
Simon Glassda1a6ec2025-03-28 07:02:20 -0600396 text = orig_text[:pos]
397 series = patchstream.get_metadata_for_test(text)
398 series.base_commit = Commit('1a44532')
399 series.branch = 'mybranch'
400 cover_fname, args = self._create_patches_for_test(series)
401 self.assertFalse(cover_fname)
Simon Glass14d64e32025-04-29 07:21:59 -0600402 with terminal.capture() as out:
Simon Glassda1a6ec2025-03-28 07:02:20 -0600403 patchstream.fix_patches(series, args, insert_base_commit=True)
404 self.assertEqual('Cleaned 1 patch\n', out[0].getvalue())
405 lines = tools.read_file(args[0], binary=False).splitlines()
406 pos = lines.index('-- ')
407
408 # We expect these lines at the end:
409 # -- (with trailing space)
410 # 2.7.4
411 # (empty)
412 # base-commit: xxx
413 # branch: xxx
414 self.assertEqual('base-commit: 1a44532', lines[pos + 3])
415 self.assertEqual('branch: mybranch', lines[pos + 4])
416
Simon Glass54f1c5b2020-07-05 21:41:50 -0600417 def make_commit_with_file(self, subject, body, fname, text):
418 """Create a file and add it to the git repo with a new commit
419
420 Args:
421 subject (str): Subject for the commit
422 body (str): Body text of the commit
423 fname (str): Filename of file to create
424 text (str): Text to put into the file
425 """
Simon Glass41dfb6e2025-05-08 05:13:35 +0200426 path = os.path.join(self.tmpdir, fname)
Simon Glass80025522022-01-29 14:14:04 -0700427 tools.write_file(path, text, binary=False)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600428 index = self.repo.index
429 index.add(fname)
Simon Glass547cba62022-02-11 13:23:18 -0700430 # pylint doesn't seem to find this
431 # pylint: disable=E1101
Simon Glass95745aa2020-10-29 21:46:13 -0600432 author = pygit2.Signature('Test user', 'test@email.com')
Simon Glass54f1c5b2020-07-05 21:41:50 -0600433 committer = author
434 tree = index.write_tree()
435 message = subject + '\n' + body
436 self.repo.create_commit('HEAD', author, committer, message, tree,
437 [self.repo.head.target])
438
439 def make_git_tree(self):
440 """Make a simple git tree suitable for testing
441
442 It has three branches:
443 'base' has two commits: PCI, main
444 'first' has base as upstream and two more commits: I2C, SPI
445 'second' has base as upstream and three more: video, serial, bootm
446
447 Returns:
Simon Glasseb209e52020-10-29 21:46:15 -0600448 pygit2.Repository: repository
Simon Glass54f1c5b2020-07-05 21:41:50 -0600449 """
450 repo = pygit2.init_repository(self.gitdir)
451 self.repo = repo
452 new_tree = repo.TreeBuilder().write()
453
Simon Glass7cb21f02025-05-08 05:02:07 +0200454 common = ['git', f'--git-dir={self.gitdir}', 'config']
455 tools.run(*(common + ['user.name', 'Dummy']), cwd=self.gitdir)
456 tools.run(*(common + ['user.email', 'dumdum@dummy.com']),
457 cwd=self.gitdir)
458
Simon Glass547cba62022-02-11 13:23:18 -0700459 # pylint doesn't seem to find this
460 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600461 author = pygit2.Signature('Test user', 'test@email.com')
462 committer = author
Simon Glasseb209e52020-10-29 21:46:15 -0600463 _ = repo.create_commit('HEAD', author, committer, 'Created master',
464 new_tree, [])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600465
466 self.make_commit_with_file('Initial commit', '''
467Add a README
468
469''', 'README', '''This is the README file
470describing this project
471in very little detail''')
472
473 self.make_commit_with_file('pci: PCI implementation', '''
474Here is a basic PCI implementation
475
476''', 'pci.c', '''This is a file
477it has some contents
478and some more things''')
479 self.make_commit_with_file('main: Main program', '''
480Hello here is the second commit.
481''', 'main.c', '''This is the main file
482there is very little here
483but we can always add more later
484if we want to
485
486Series-to: u-boot
487Series-cc: Barry Crump <bcrump@whataroa.nz>
488''')
489 base_target = repo.revparse_single('HEAD')
490 self.make_commit_with_file('i2c: I2C things', '''
491This has some stuff to do with I2C
492''', 'i2c.c', '''And this is the file contents
493with some I2C-related things in it''')
494 self.make_commit_with_file('spi: SPI fixes', '''
495SPI needs some fixes
496and here they are
Simon Glassd0a0a582020-10-29 21:46:36 -0600497
498Signed-off-by: %s
499
500Series-to: u-boot
501Commit-notes:
502title of the series
503This is the cover letter for the series
504with various details
505END
506''' % self.leb, 'spi.c', '''Some fixes for SPI in this
Simon Glass54f1c5b2020-07-05 21:41:50 -0600507file to make SPI work
508better than before''')
509 first_target = repo.revparse_single('HEAD')
510
511 target = repo.revparse_single('HEAD~2')
Simon Glass547cba62022-02-11 13:23:18 -0700512 # pylint doesn't seem to find this
513 # pylint: disable=E1101
Simon Glass573abf82025-05-08 05:23:41 +0200514 repo.reset(target.oid, pygit2.enums.ResetMode.HARD)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600515 self.make_commit_with_file('video: Some video improvements', '''
516Fix up the video so that
517it looks more purple. Purple is
518a very nice colour.
519''', 'video.c', '''More purple here
520Purple and purple
521Even more purple
522Could not be any more purple''')
Simon Glassb8ca4692025-05-08 05:26:16 +0200523 self.make_commit_with_file('serial: Add a serial driver', f'''
Simon Glass54f1c5b2020-07-05 21:41:50 -0600524Here is the serial driver
525for my chip.
526
527Cover-letter:
Simon Glassb8ca4692025-05-08 05:26:16 +0200528{self.TITLE_SECOND}
Simon Glass54f1c5b2020-07-05 21:41:50 -0600529This series implements support
530for my glorious board.
531END
Simon Glassb8ca4692025-05-08 05:26:16 +0200532Series-to: u-boot
533Series-links: {self.SERIES_ID_SECOND_V1}
Simon Glass54f1c5b2020-07-05 21:41:50 -0600534''', 'serial.c', '''The code for the
535serial driver is here''')
536 self.make_commit_with_file('bootm: Make it boot', '''
537This makes my board boot
538with a fix to the bootm
539command
540''', 'bootm.c', '''Fix up the bootm
541command to make the code as
542complicated as possible''')
543 second_target = repo.revparse_single('HEAD')
544
545 repo.branches.local.create('first', first_target)
546 repo.config.set_multivar('branch.first.remote', '', '.')
547 repo.config.set_multivar('branch.first.merge', '', 'refs/heads/base')
548
549 repo.branches.local.create('second', second_target)
550 repo.config.set_multivar('branch.second.remote', '', '.')
551 repo.config.set_multivar('branch.second.merge', '', 'refs/heads/base')
552
553 repo.branches.local.create('base', base_target)
Simon Glass573abf82025-05-08 05:23:41 +0200554
555 target = repo.lookup_reference('refs/heads/first')
556 repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
557 target = repo.revparse_single('HEAD')
558 repo.reset(target.oid, pygit2.enums.ResetMode.HARD)
559
560 self.assertFalse(gitutil.check_dirty(self.gitdir, self.tmpdir))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600561 return repo
562
Simon Glassd85bb8f2022-01-29 14:14:09 -0700563 def test_branch(self):
Simon Glass54f1c5b2020-07-05 21:41:50 -0600564 """Test creating patches from a branch"""
565 repo = self.make_git_tree()
566 target = repo.lookup_reference('refs/heads/first')
Simon Glass547cba62022-02-11 13:23:18 -0700567 # pylint doesn't seem to find this
568 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600569 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
570 control.setup()
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200571 orig_dir = os.getcwd()
Simon Glass54f1c5b2020-07-05 21:41:50 -0600572 try:
Simon Glass41dfb6e2025-05-08 05:13:35 +0200573 os.chdir(self.tmpdir)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600574
575 # Check that it can detect the current branch
Simon Glass761648b2022-01-29 14:14:11 -0700576 self.assertEqual(2, gitutil.count_commits_to_branch(None))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600577 col = terminal.Color()
Simon Glass14d64e32025-04-29 07:21:59 -0600578 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600579 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600580 col, branch=None, count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100581 ignore_binary=False, signoff=True)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600582 self.assertIsNone(cover_fname)
583 self.assertEqual(2, len(patch_files))
Simon Glass2eb4da72020-07-05 21:41:51 -0600584
585 # Check that it can detect a different branch
Simon Glass761648b2022-01-29 14:14:11 -0700586 self.assertEqual(3, gitutil.count_commits_to_branch('second'))
Simon Glass14d64e32025-04-29 07:21:59 -0600587 with terminal.capture() as _:
Simon Glass30b21792025-05-08 05:36:20 +0200588 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600589 col, branch='second', count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100590 ignore_binary=False, signoff=True)
Simon Glass2eb4da72020-07-05 21:41:51 -0600591 self.assertIsNotNone(cover_fname)
592 self.assertEqual(3, len(patch_files))
Simon Glassb3bf4e12020-07-05 21:41:52 -0600593
Simon Glass414f1e02025-02-27 12:27:30 -0700594 cover = tools.read_file(cover_fname, binary=False)
595 lines = cover.splitlines()[-2:]
596 base = repo.lookup_reference('refs/heads/base').target
597 self.assertEqual(f'base-commit: {base}', lines[0])
598 self.assertEqual('branch: second', lines[1])
599
Simon Glassda1a6ec2025-03-28 07:02:20 -0600600 # Make sure that the base-commit is not present when it is in the
601 # cover letter
602 for fname in patch_files:
603 self.assertNotIn(b'base-commit:', tools.read_file(fname))
604
Simon Glassb3bf4e12020-07-05 21:41:52 -0600605 # Check that it can skip patches at the end
Simon Glass14d64e32025-04-29 07:21:59 -0600606 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600607 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600608 col, branch='second', count=-1, start=0, end=1,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100609 ignore_binary=False, signoff=True)
Simon Glassb3bf4e12020-07-05 21:41:52 -0600610 self.assertIsNotNone(cover_fname)
611 self.assertEqual(2, len(patch_files))
Simon Glass414f1e02025-02-27 12:27:30 -0700612
613 cover = tools.read_file(cover_fname, binary=False)
614 lines = cover.splitlines()[-2:]
615 base2 = repo.lookup_reference('refs/heads/second')
616 ref = base2.peel(pygit2.GIT_OBJ_COMMIT).parents[0].parents[0].id
617 self.assertEqual(f'base-commit: {ref}', lines[0])
618 self.assertEqual('branch: second', lines[1])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600619 finally:
620 os.chdir(orig_dir)
Simon Glass06202d62020-10-29 21:46:27 -0600621
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500622 def test_custom_get_maintainer_script(self):
623 """Validate that a custom get_maintainer script gets used."""
624 self.make_git_tree()
Simon Glass41dfb6e2025-05-08 05:13:35 +0200625 with directory_excursion(self.tmpdir):
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500626 # Setup git.
627 os.environ['GIT_CONFIG_GLOBAL'] = '/dev/null'
628 os.environ['GIT_CONFIG_SYSTEM'] = '/dev/null'
629 tools.run('git', 'config', 'user.name', 'Dummy')
630 tools.run('git', 'config', 'user.email', 'dumdum@dummy.com')
631 tools.run('git', 'branch', 'upstream')
632 tools.run('git', 'branch', '--set-upstream-to=upstream')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500633
634 # Setup patman configuration.
Simon Glass30b21792025-05-08 05:36:20 +0200635 tools.write_file('.patman', '[settings]\n'
636 'get_maintainer_script: dummy-script.sh\n'
637 'check_patch: False\n'
638 'add_maintainers: True\n', binary=False)
639 tools.write_file('dummy-script.sh',
640 '#!/usr/bin/env python\n'
641 'print("hello@there.com")\n', binary=False)
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500642 os.chmod('dummy-script.sh', 0x555)
Simon Glass41dfb6e2025-05-08 05:13:35 +0200643 tools.run('git', 'add', '.')
644 tools.run('git', 'commit', '-m', 'new commit')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500645
646 # Finally, do the test
Simon Glass14d64e32025-04-29 07:21:59 -0600647 with terminal.capture():
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500648 output = tools.run(PATMAN_DIR / 'patman', '--dry-run')
649 # Assert the email address is part of the dry-run
650 # output.
651 self.assertIn('hello@there.com', output)
652
Simon Glassd85bb8f2022-01-29 14:14:09 -0700653 def test_tags(self):
Simon Glass06202d62020-10-29 21:46:27 -0600654 """Test collection of tags in a patchstream"""
655 text = '''This is a patch
656
657Signed-off-by: Terminator
Simon Glass3b762cc2020-10-29 21:46:28 -0600658Reviewed-by: %s
659Reviewed-by: %s
Simon Glass06202d62020-10-29 21:46:27 -0600660Tested-by: %s
Simon Glass3b762cc2020-10-29 21:46:28 -0600661''' % (self.joe, self.mary, self.leb)
Simon Glass06202d62020-10-29 21:46:27 -0600662 pstrm = PatchStream.process_text(text)
663 self.assertEqual(pstrm.commit.rtags, {
Simon Glass3b762cc2020-10-29 21:46:28 -0600664 'Reviewed-by': {self.joe, self.mary},
Simon Glass06202d62020-10-29 21:46:27 -0600665 'Tested-by': {self.leb}})
Simon Glass3b762cc2020-10-29 21:46:28 -0600666
Simon Glassd85bb8f2022-01-29 14:14:09 -0700667 def test_invalid_tag(self):
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200668 """Test invalid tag in a patchstream"""
669 text = '''This is a patch
670
671Serie-version: 2
672'''
673 with self.assertRaises(ValueError) as exc:
Simon Glass30b21792025-05-08 05:36:20 +0200674 PatchStream.process_text(text)
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200675 self.assertEqual("Line 3: Invalid tag = 'Serie-version: 2'",
676 str(exc.exception))
677
Simon Glassd85bb8f2022-01-29 14:14:09 -0700678 def test_missing_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600679 """Test a missing END tag"""
680 text = '''This is a patch
681
682Cover-letter:
683This is the title
684missing END after this line
685Signed-off-by: Fred
686'''
687 pstrm = PatchStream.process_text(text)
688 self.assertEqual(["Missing 'END' in section 'cover'"],
689 pstrm.commit.warn)
690
Simon Glassd85bb8f2022-01-29 14:14:09 -0700691 def test_missing_blank_line(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600692 """Test a missing blank line after a tag"""
693 text = '''This is a patch
694
695Series-changes: 2
696- First line of changes
697- Missing blank line after this line
698Signed-off-by: Fred
699'''
700 pstrm = PatchStream.process_text(text)
701 self.assertEqual(["Missing 'blank line' in section 'Series-changes'"],
702 pstrm.commit.warn)
703
Simon Glassd85bb8f2022-01-29 14:14:09 -0700704 def test_invalid_commit_tag(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600705 """Test an invalid Commit-xxx tag"""
706 text = '''This is a patch
707
708Commit-fred: testing
709'''
710 pstrm = PatchStream.process_text(text)
711 self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn)
712
Simon Glassd85bb8f2022-01-29 14:14:09 -0700713 def test_self_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600714 """Test a tested by tag by this user"""
715 test_line = 'Tested-by: %s@napier.com' % os.getenv('USER')
716 text = '''This is a patch
717
718%s
719''' % test_line
720 pstrm = PatchStream.process_text(text)
721 self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn)
722
Simon Glassd85bb8f2022-01-29 14:14:09 -0700723 def test_space_before_tab(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600724 """Test a space before a tab"""
725 text = '''This is a patch
726
727+ \tSomething
728'''
729 pstrm = PatchStream.process_text(text)
730 self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn)
731
Simon Glassd85bb8f2022-01-29 14:14:09 -0700732 def test_lines_after_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600733 """Test detecting lines after TEST= line"""
734 text = '''This is a patch
735
736TEST=sometest
737more lines
738here
739'''
740 pstrm = PatchStream.process_text(text)
741 self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn)
742
Simon Glassd85bb8f2022-01-29 14:14:09 -0700743 def test_blank_line_at_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600744 """Test detecting a blank line at the end of a file"""
745 text = '''This is a patch
746
747diff --git a/lib/fdtdec.c b/lib/fdtdec.c
748index c072e54..942244f 100644
749--- a/lib/fdtdec.c
750+++ b/lib/fdtdec.c
751@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void)
Simon Glass30b21792025-05-08 05:36:20 +0200752 \t}
Simon Glass3b762cc2020-10-29 21:46:28 -0600753
Simon Glass30b21792025-05-08 05:36:20 +0200754 \tgd->ram_size = (phys_size_t)(res.end - res.start + 1);
Simon Glass3b762cc2020-10-29 21:46:28 -0600755- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size);
756+ debug("%s: Initial DRAM size %llx\n", __func__,
757+ (unsigned long long)gd->ram_size);
758+
759diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
760
761--
7622.7.4
763
764 '''
765 pstrm = PatchStream.process_text(text)
766 self.assertEqual(
767 ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"],
768 pstrm.commit.warn)
Simon Glass1c1f2072020-10-29 21:46:34 -0600769
Simon Glassd85bb8f2022-01-29 14:14:09 -0700770 def test_no_upstream(self):
Simon Glass1c1f2072020-10-29 21:46:34 -0600771 """Test CountCommitsToBranch when there is no upstream"""
772 repo = self.make_git_tree()
773 target = repo.lookup_reference('refs/heads/base')
Simon Glass547cba62022-02-11 13:23:18 -0700774 # pylint doesn't seem to find this
775 # pylint: disable=E1101
Simon Glass1c1f2072020-10-29 21:46:34 -0600776 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
777
778 # Check that it can detect the current branch
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200779 orig_dir = os.getcwd()
Simon Glass1c1f2072020-10-29 21:46:34 -0600780 try:
Simon Glass1c1f2072020-10-29 21:46:34 -0600781 os.chdir(self.gitdir)
782 with self.assertRaises(ValueError) as exc:
Simon Glass761648b2022-01-29 14:14:11 -0700783 gitutil.count_commits_to_branch(None)
Simon Glass1c1f2072020-10-29 21:46:34 -0600784 self.assertIn(
785 "Failed to determine upstream: fatal: no upstream configured for branch 'base'",
786 str(exc.exception))
787 finally:
788 os.chdir(orig_dir)
Simon Glass3db916d2020-10-29 21:46:35 -0600789
Simon Glass2584cc52025-05-08 06:01:28 +0200790 def _RunPatman(self, *args):
791 all_args = [self._patman_pathname] + list(args)
792 return command.run_one(*all_args, capture=True, capture_stderr=True)
793
794 def testFullHelp(self):
795 command.TEST_RESULT = None
796 result = self._RunPatman('-H')
797 help_file = os.path.join(self._patman_dir, 'README.rst')
798 # Remove possible extraneous strings
799 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
800 gothelp = result.stdout.replace(extra, '')
801 self.assertEqual(len(gothelp), os.path.getsize(help_file))
802 self.assertEqual(0, len(result.stderr))
803 self.assertEqual(0, result.return_code)
804
805 def testHelp(self):
806 command.TEST_RESULT = None
807 result = self._RunPatman('-h')
808 self.assertTrue(len(result.stdout) > 1000)
809 self.assertEqual(0, len(result.stderr))
810 self.assertEqual(0, result.return_code)
811
Simon Glass3db916d2020-10-29 21:46:35 -0600812 @staticmethod
Simon Glass25b91c12025-04-29 07:22:19 -0600813 def _fake_patchwork(subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600814 """Fake Patchwork server for the function below
815
816 This handles accessing a series, providing a list consisting of a
817 single patch
Simon Glassf9b03cf2020-11-03 13:54:14 -0700818
819 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -0700820 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600821 """
822 re_series = re.match(r'series/(\d*)/$', subpath)
823 if re_series:
824 series_num = re_series.group(1)
825 if series_num == '1234':
826 return {'patches': [
827 {'id': '1', 'name': 'Some patch'}]}
828 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
829
Simon Glassd85bb8f2022-01-29 14:14:09 -0700830 def test_status_mismatch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600831 """Test Patchwork patches not matching the series"""
Simon Glass25b91c12025-04-29 07:22:19 -0600832 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork)
Simon Glass14d64e32025-04-29 07:21:59 -0600833 with terminal.capture() as (_, err):
Simon Glass30b21792025-05-08 05:36:20 +0200834 loop = asyncio.get_event_loop()
835 patches = loop.run_until_complete(status.check_status(1234, pwork))
Simon Glass27280f42025-04-29 07:22:17 -0600836 status.check_patch_count(0, len(patches))
Simon Glass3db916d2020-10-29 21:46:35 -0600837 self.assertIn('Warning: Patchwork reports 1 patches, series has 0',
838 err.getvalue())
839
Simon Glassd85bb8f2022-01-29 14:14:09 -0700840 def test_status_read_patch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600841 """Test handling a single patch in Patchwork"""
Simon Glass25b91c12025-04-29 07:22:19 -0600842 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork)
Simon Glass30b21792025-05-08 05:36:20 +0200843 loop = asyncio.get_event_loop()
844 patches = loop.run_until_complete(status.check_status(1234, pwork))
Simon Glass3db916d2020-10-29 21:46:35 -0600845 self.assertEqual(1, len(patches))
846 patch = patches[0]
847 self.assertEqual('1', patch.id)
848 self.assertEqual('Some patch', patch.raw_subject)
849
Simon Glassd85bb8f2022-01-29 14:14:09 -0700850 def test_parse_subject(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600851 """Test parsing of the patch subject"""
Simon Glass232eefd2025-04-29 07:22:14 -0600852 patch = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600853
854 # Simple patch not in a series
855 patch.parse_subject('Testing')
856 self.assertEqual('Testing', patch.raw_subject)
857 self.assertEqual('Testing', patch.subject)
858 self.assertEqual(1, patch.seq)
859 self.assertEqual(1, patch.count)
860 self.assertEqual(None, patch.prefix)
861 self.assertEqual(None, patch.version)
862
863 # First patch in a series
864 patch.parse_subject('[1/2] Testing')
865 self.assertEqual('[1/2] Testing', patch.raw_subject)
866 self.assertEqual('Testing', patch.subject)
867 self.assertEqual(1, patch.seq)
868 self.assertEqual(2, patch.count)
869 self.assertEqual(None, patch.prefix)
870 self.assertEqual(None, patch.version)
871
872 # Second patch in a series
873 patch.parse_subject('[2/2] Testing')
874 self.assertEqual('Testing', patch.subject)
875 self.assertEqual(2, patch.seq)
876 self.assertEqual(2, patch.count)
877 self.assertEqual(None, patch.prefix)
878 self.assertEqual(None, patch.version)
879
880 # RFC patch
881 patch.parse_subject('[RFC,3/7] Testing')
882 self.assertEqual('Testing', patch.subject)
883 self.assertEqual(3, patch.seq)
884 self.assertEqual(7, patch.count)
885 self.assertEqual('RFC', patch.prefix)
886 self.assertEqual(None, patch.version)
887
888 # Version patch
889 patch.parse_subject('[v2,3/7] Testing')
890 self.assertEqual('Testing', patch.subject)
891 self.assertEqual(3, patch.seq)
892 self.assertEqual(7, patch.count)
893 self.assertEqual(None, patch.prefix)
894 self.assertEqual('v2', patch.version)
895
896 # All fields
897 patch.parse_subject('[RESEND,v2,3/7] Testing')
898 self.assertEqual('Testing', patch.subject)
899 self.assertEqual(3, patch.seq)
900 self.assertEqual(7, patch.count)
901 self.assertEqual('RESEND', patch.prefix)
902 self.assertEqual('v2', patch.version)
903
904 # RFC only
905 patch.parse_subject('[RESEND] Testing')
906 self.assertEqual('Testing', patch.subject)
907 self.assertEqual(1, patch.seq)
908 self.assertEqual(1, patch.count)
909 self.assertEqual('RESEND', patch.prefix)
910 self.assertEqual(None, patch.version)
911
Simon Glassd85bb8f2022-01-29 14:14:09 -0700912 def test_compare_series(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600913 """Test operation of compare_with_series()"""
914 commit1 = Commit('abcd')
915 commit1.subject = 'Subject 1'
916 commit2 = Commit('ef12')
917 commit2.subject = 'Subject 2'
918 commit3 = Commit('3456')
919 commit3.subject = 'Subject 2'
920
Simon Glass232eefd2025-04-29 07:22:14 -0600921 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600922 patch1.subject = 'Subject 1'
Simon Glass232eefd2025-04-29 07:22:14 -0600923 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600924 patch2.subject = 'Subject 2'
Simon Glass232eefd2025-04-29 07:22:14 -0600925 patch3 = patchwork.Patch('3')
Simon Glass3db916d2020-10-29 21:46:35 -0600926 patch3.subject = 'Subject 2'
927
928 series = Series()
929 series.commits = [commit1]
930 patches = [patch1]
931 patch_for_commit, commit_for_patch, warnings = (
932 status.compare_with_series(series, patches))
933 self.assertEqual(1, len(patch_for_commit))
934 self.assertEqual(patch1, patch_for_commit[0])
935 self.assertEqual(1, len(commit_for_patch))
936 self.assertEqual(commit1, commit_for_patch[0])
937
938 series.commits = [commit1]
939 patches = [patch1, patch2]
940 patch_for_commit, commit_for_patch, warnings = (
941 status.compare_with_series(series, patches))
942 self.assertEqual(1, len(patch_for_commit))
943 self.assertEqual(patch1, patch_for_commit[0])
944 self.assertEqual(1, len(commit_for_patch))
945 self.assertEqual(commit1, commit_for_patch[0])
946 self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"],
947 warnings)
948
949 series.commits = [commit1, commit2]
950 patches = [patch1]
951 patch_for_commit, commit_for_patch, warnings = (
952 status.compare_with_series(series, patches))
953 self.assertEqual(1, len(patch_for_commit))
954 self.assertEqual(patch1, patch_for_commit[0])
955 self.assertEqual(1, len(commit_for_patch))
956 self.assertEqual(commit1, commit_for_patch[0])
957 self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"],
958 warnings)
959
960 series.commits = [commit1, commit2, commit3]
961 patches = [patch1, patch2]
962 patch_for_commit, commit_for_patch, warnings = (
963 status.compare_with_series(series, patches))
964 self.assertEqual(2, len(patch_for_commit))
965 self.assertEqual(patch1, patch_for_commit[0])
966 self.assertEqual(patch2, patch_for_commit[1])
967 self.assertEqual(1, len(commit_for_patch))
968 self.assertEqual(commit1, commit_for_patch[0])
969 self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')",
970 "Multiple commits match patch 2 ('Subject 2'):\n"
971 ' Subject 2\n Subject 2'],
972 warnings)
973
974 series.commits = [commit1, commit2]
975 patches = [patch1, patch2, patch3]
976 patch_for_commit, commit_for_patch, warnings = (
977 status.compare_with_series(series, patches))
978 self.assertEqual(1, len(patch_for_commit))
979 self.assertEqual(patch1, patch_for_commit[0])
980 self.assertEqual(2, len(commit_for_patch))
981 self.assertEqual(commit1, commit_for_patch[0])
982 self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n"
983 ' Subject 2\n Subject 2',
984 "Cannot find commit for patch 3 ('Subject 2')"],
985 warnings)
986
Simon Glass25b91c12025-04-29 07:22:19 -0600987 def _fake_patchwork2(self, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600988 """Fake Patchwork server for the function below
989
990 This handles accessing series, patches and comments, providing the data
991 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700992
993 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -0700994 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600995 """
996 re_series = re.match(r'series/(\d*)/$', subpath)
997 re_patch = re.match(r'patches/(\d*)/$', subpath)
998 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
999 if re_series:
1000 series_num = re_series.group(1)
1001 if series_num == '1234':
1002 return {'patches': self.patches}
1003 elif re_patch:
1004 patch_num = int(re_patch.group(1))
1005 patch = self.patches[patch_num - 1]
1006 return patch
1007 elif re_comments:
1008 patch_num = int(re_comments.group(1))
1009 patch = self.patches[patch_num - 1]
1010 return patch.comments
1011 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
1012
Simon Glassd85bb8f2022-01-29 14:14:09 -07001013 def test_find_new_responses(self):
Simon Glass3db916d2020-10-29 21:46:35 -06001014 """Test operation of find_new_responses()"""
1015 commit1 = Commit('abcd')
1016 commit1.subject = 'Subject 1'
1017 commit2 = Commit('ef12')
1018 commit2.subject = 'Subject 2'
1019
Simon Glass232eefd2025-04-29 07:22:14 -06001020 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -06001021 patch1.parse_subject('[1/2] Subject 1')
1022 patch1.name = patch1.raw_subject
1023 patch1.content = 'This is my patch content'
1024 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1025
1026 patch1.comments = [comment1a]
1027
Simon Glass232eefd2025-04-29 07:22:14 -06001028 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -06001029 patch2.parse_subject('[2/2] Subject 2')
1030 patch2.name = patch2.raw_subject
1031 patch2.content = 'Some other patch content'
1032 comment2a = {
1033 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1034 (self.mary, self.leb)}
1035 comment2b = {'content': 'Reviewed-by: %s' % self.fred}
1036 patch2.comments = [comment2a, comment2b]
1037
1038 # This test works by setting up commits and patch for use by the fake
1039 # Rest API function _fake_patchwork2(). It calls various functions in
1040 # the status module after setting up tags in the commits, checking that
1041 # things behaves as expected
1042 self.commits = [commit1, commit2]
1043 self.patches = [patch1, patch2]
Simon Glass3db916d2020-10-29 21:46:35 -06001044
1045 # Check that the tags are picked up on the first patch
Simon Glass29771962025-04-29 07:22:20 -06001046 new_rtags, _ = status.process_reviews(patch1.content, patch1.comments,
1047 commit1.rtags)
1048 self.assertEqual(new_rtags, {'Reviewed-by': {self.joe}})
Simon Glass3db916d2020-10-29 21:46:35 -06001049
1050 # Now the second patch
Simon Glass29771962025-04-29 07:22:20 -06001051 new_rtags, _ = status.process_reviews(patch2.content, patch2.comments,
1052 commit2.rtags)
1053 self.assertEqual(new_rtags, {
Simon Glass3db916d2020-10-29 21:46:35 -06001054 'Reviewed-by': {self.mary, self.fred},
1055 'Tested-by': {self.leb}})
1056
1057 # Now add some tags to the commit, which means they should not appear as
1058 # 'new' tags when scanning comments
Simon Glass3db916d2020-10-29 21:46:35 -06001059 commit1.rtags = {'Reviewed-by': {self.joe}}
Simon Glass29771962025-04-29 07:22:20 -06001060 new_rtags, _ = status.process_reviews(patch1.content, patch1.comments,
1061 commit1.rtags)
1062 self.assertEqual(new_rtags, {})
Simon Glass3db916d2020-10-29 21:46:35 -06001063
1064 # For the second commit, add Ed and Fred, so only Mary should be left
1065 commit2.rtags = {
1066 'Tested-by': {self.leb},
1067 'Reviewed-by': {self.fred}}
Simon Glass29771962025-04-29 07:22:20 -06001068 new_rtags, _ = status.process_reviews(patch2.content, patch2.comments,
1069 commit2.rtags)
1070 self.assertEqual(new_rtags, {'Reviewed-by': {self.mary}})
Simon Glass3db916d2020-10-29 21:46:35 -06001071
1072 # Check that the output patches expectations:
1073 # 1 Subject 1
1074 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1075 # 2 Subject 2
1076 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1077 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1078 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1079 # 1 new response available in patchwork
1080
1081 series = Series()
1082 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001083 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001084 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2)
Simon Glassc100b262025-04-29 07:22:16 -06001085 status.check_and_show_status(series, '1234', None, None, False, False,
Simon Glass25b91c12025-04-29 07:22:19 -06001086 pwork)
Simon Glassb3080ec2025-05-08 04:58:49 +02001087 itr = iter(terminal.get_print_test_lines())
Simon Glass3db916d2020-10-29 21:46:35 -06001088 col = terminal.Color()
Simon Glassd4d3fb42025-04-29 07:22:21 -06001089 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001090 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001091 self.assertEqual(
1092 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
1093 bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001094 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001095 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001096 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001097
Simon Glassd4d3fb42025-04-29 07:22:21 -06001098 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001099 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001100 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001101 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001102 bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001103 next(itr))
1104 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE,
1105 bright=False), next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001106 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001107 terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001108 bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001109 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001110 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001111 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001112 self.assertEqual(
1113 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001114 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001115 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001116 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001117 self.assertEqual(terminal.PrintLine(
Simon Glassd0a0a582020-10-29 21:46:36 -06001118 '1 new response available in patchwork (use -d to write them to a new branch)',
Simon Glassb3080ec2025-05-08 04:58:49 +02001119 None), next(itr))
Simon Glassd0a0a582020-10-29 21:46:36 -06001120
Simon Glass25b91c12025-04-29 07:22:19 -06001121 def _fake_patchwork3(self, subpath):
Simon Glassd0a0a582020-10-29 21:46:36 -06001122 """Fake Patchwork server for the function below
1123
1124 This handles accessing series, patches and comments, providing the data
1125 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -07001126
1127 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -07001128 subpath (str): URL subpath to use
Simon Glassd0a0a582020-10-29 21:46:36 -06001129 """
1130 re_series = re.match(r'series/(\d*)/$', subpath)
1131 re_patch = re.match(r'patches/(\d*)/$', subpath)
1132 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
1133 if re_series:
1134 series_num = re_series.group(1)
1135 if series_num == '1234':
1136 return {'patches': self.patches}
1137 elif re_patch:
1138 patch_num = int(re_patch.group(1))
1139 patch = self.patches[patch_num - 1]
1140 return patch
1141 elif re_comments:
1142 patch_num = int(re_comments.group(1))
1143 patch = self.patches[patch_num - 1]
1144 return patch.comments
1145 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
1146
Simon Glassd85bb8f2022-01-29 14:14:09 -07001147 def test_create_branch(self):
Simon Glassd0a0a582020-10-29 21:46:36 -06001148 """Test operation of create_branch()"""
1149 repo = self.make_git_tree()
1150 branch = 'first'
1151 dest_branch = 'first2'
1152 count = 2
Simon Glass41dfb6e2025-05-08 05:13:35 +02001153 gitdir = self.gitdir
Simon Glassd0a0a582020-10-29 21:46:36 -06001154
1155 # Set up the test git tree. We use branch 'first' which has two commits
1156 # in it
1157 series = patchstream.get_metadata_for_list(branch, gitdir, count)
1158 self.assertEqual(2, len(series.commits))
1159
Simon Glass232eefd2025-04-29 07:22:14 -06001160 patch1 = patchwork.Patch('1')
Simon Glassd0a0a582020-10-29 21:46:36 -06001161 patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
1162 patch1.name = patch1.raw_subject
1163 patch1.content = 'This is my patch content'
1164 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1165
1166 patch1.comments = [comment1a]
1167
Simon Glass232eefd2025-04-29 07:22:14 -06001168 patch2 = patchwork.Patch('2')
Simon Glassd0a0a582020-10-29 21:46:36 -06001169 patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
1170 patch2.name = patch2.raw_subject
1171 patch2.content = 'Some other patch content'
1172 comment2a = {
1173 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1174 (self.mary, self.leb)}
1175 comment2b = {
1176 'content': 'Reviewed-by: %s' % self.fred}
1177 patch2.comments = [comment2a, comment2b]
1178
1179 # This test works by setting up patches for use by the fake Rest API
1180 # function _fake_patchwork3(). The fake patch comments above should
1181 # result in new review tags that are collected and added to the commits
1182 # created in the destination branch.
1183 self.patches = [patch1, patch2]
1184 count = 2
1185
1186 # Expected output:
1187 # 1 i2c: I2C things
1188 # + Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1189 # 2 spi: SPI fixes
1190 # + Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1191 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1192 # + Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1193 # 4 new responses available in patchwork
1194 # 4 responses added from patchwork into new branch 'first2'
1195 # <unittest.result.TestResult run=8 errors=0 failures=0>
1196
Simon Glass02811582022-01-29 14:14:18 -07001197 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001198 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork3)
Simon Glassc100b262025-04-29 07:22:16 -06001199 status.check_and_show_status(series, '1234', branch, dest_branch,
Simon Glass25b91c12025-04-29 07:22:19 -06001200 False, False, pwork, repo)
Simon Glass02811582022-01-29 14:14:18 -07001201 lines = terminal.get_print_test_lines()
Simon Glassd0a0a582020-10-29 21:46:36 -06001202 self.assertEqual(12, len(lines))
1203 self.assertEqual(
1204 "4 responses added from patchwork into new branch 'first2'",
1205 lines[11].text)
1206
1207 # Check that the destination branch has the new tags
1208 new_series = patchstream.get_metadata_for_list(dest_branch, gitdir,
1209 count)
1210 self.assertEqual(
1211 {'Reviewed-by': {self.joe}},
1212 new_series.commits[0].rtags)
1213 self.assertEqual(
1214 {'Tested-by': {self.leb},
1215 'Reviewed-by': {self.fred, self.mary}},
1216 new_series.commits[1].rtags)
1217
1218 # Now check the actual test of the first commit message. We expect to
1219 # see the new tags immediately below the old ones.
1220 stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir)
Simon Glassb3080ec2025-05-08 04:58:49 +02001221 itr = iter([line.strip() for line in stdout.splitlines()
1222 if '-by:' in line])
Simon Glassd0a0a582020-10-29 21:46:36 -06001223
1224 # First patch should have the review tag
Simon Glassb3080ec2025-05-08 04:58:49 +02001225 self.assertEqual('Reviewed-by: %s' % self.joe, next(itr))
Simon Glassd0a0a582020-10-29 21:46:36 -06001226
1227 # Second patch should have the sign-off then the tested-by and two
1228 # reviewed-by tags
Simon Glassb3080ec2025-05-08 04:58:49 +02001229 self.assertEqual('Signed-off-by: %s' % self.leb, next(itr))
1230 self.assertEqual('Reviewed-by: %s' % self.fred, next(itr))
1231 self.assertEqual('Reviewed-by: %s' % self.mary, next(itr))
1232 self.assertEqual('Tested-by: %s' % self.leb, next(itr))
Simon Glassda8a2922020-10-29 21:46:37 -06001233
Simon Glassd85bb8f2022-01-29 14:14:09 -07001234 def test_parse_snippets(self):
Simon Glassda8a2922020-10-29 21:46:37 -06001235 """Test parsing of review snippets"""
1236 text = '''Hi Fred,
1237
1238This is a comment from someone.
1239
1240Something else
1241
1242On some recent date, Fred wrote:
1243> This is why I wrote the patch
1244> so here it is
1245
1246Now a comment about the commit message
1247A little more to say
1248
1249Even more
1250
1251> diff --git a/file.c b/file.c
1252> Some more code
1253> Code line 2
1254> Code line 3
1255> Code line 4
1256> Code line 5
1257> Code line 6
1258> Code line 7
1259> Code line 8
1260> Code line 9
1261
1262And another comment
1263
Simon Glassd85bb8f2022-01-29 14:14:09 -07001264> @@ -153,8 +143,13 @@ def check_patch(fname, show_types=False):
Simon Glassda8a2922020-10-29 21:46:37 -06001265> further down on the file
1266> and more code
1267> +Addition here
1268> +Another addition here
1269> codey
1270> more codey
1271
1272and another thing in same file
1273
1274> @@ -253,8 +243,13 @@
1275> with no function context
1276
1277one more thing
1278
1279> diff --git a/tools/patman/main.py b/tools/patman/main.py
1280> +line of code
1281now a very long comment in a different file
1282line2
1283line3
1284line4
1285line5
1286line6
1287line7
1288line8
1289'''
1290 pstrm = PatchStream.process_text(text, True)
1291 self.assertEqual([], pstrm.commit.warn)
1292
1293 # We expect to the filename and up to 5 lines of code context before
1294 # each comment. The 'On xxx wrote:' bit should be removed.
1295 self.assertEqual(
1296 [['Hi Fred,',
1297 'This is a comment from someone.',
1298 'Something else'],
1299 ['> This is why I wrote the patch',
1300 '> so here it is',
1301 'Now a comment about the commit message',
1302 'A little more to say', 'Even more'],
1303 ['> File: file.c', '> Code line 5', '> Code line 6',
1304 '> Code line 7', '> Code line 8', '> Code line 9',
1305 'And another comment'],
1306 ['> File: file.c',
Simon Glassd85bb8f2022-01-29 14:14:09 -07001307 '> Line: 153 / 143: def check_patch(fname, show_types=False):',
Simon Glass30b21792025-05-08 05:36:20 +02001308 '> and more code', '> +Addition here',
1309 '> +Another addition here', '> codey', '> more codey',
1310 'and another thing in same file'],
Simon Glassda8a2922020-10-29 21:46:37 -06001311 ['> File: file.c', '> Line: 253 / 243',
1312 '> with no function context', 'one more thing'],
1313 ['> File: tools/patman/main.py', '> +line of code',
1314 'now a very long comment in a different file',
1315 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']],
1316 pstrm.snippets)
Simon Glass2112d072020-10-29 21:46:38 -06001317
Simon Glassd85bb8f2022-01-29 14:14:09 -07001318 def test_review_snippets(self):
Simon Glass2112d072020-10-29 21:46:38 -06001319 """Test showing of review snippets"""
1320 def _to_submitter(who):
1321 m_who = re.match('(.*) <(.*)>', who)
1322 return {
1323 'name': m_who.group(1),
1324 'email': m_who.group(2)
1325 }
1326
1327 commit1 = Commit('abcd')
1328 commit1.subject = 'Subject 1'
1329 commit2 = Commit('ef12')
1330 commit2.subject = 'Subject 2'
1331
Simon Glass232eefd2025-04-29 07:22:14 -06001332 patch1 = patchwork.Patch('1')
Simon Glass2112d072020-10-29 21:46:38 -06001333 patch1.parse_subject('[1/2] Subject 1')
1334 patch1.name = patch1.raw_subject
1335 patch1.content = 'This is my patch content'
1336 comment1a = {'submitter': _to_submitter(self.joe),
1337 'content': '''Hi Fred,
1338
1339On some date Fred wrote:
1340
1341> diff --git a/file.c b/file.c
1342> Some code
1343> and more code
1344
1345Here is my comment above the above...
1346
1347
1348Reviewed-by: %s
1349''' % self.joe}
1350
1351 patch1.comments = [comment1a]
1352
Simon Glass232eefd2025-04-29 07:22:14 -06001353 patch2 = patchwork.Patch('2')
Simon Glass2112d072020-10-29 21:46:38 -06001354 patch2.parse_subject('[2/2] Subject 2')
1355 patch2.name = patch2.raw_subject
1356 patch2.content = 'Some other patch content'
1357 comment2a = {
1358 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1359 (self.mary, self.leb)}
1360 comment2b = {'submitter': _to_submitter(self.fred),
1361 'content': '''Hi Fred,
1362
1363On some date Fred wrote:
1364
1365> diff --git a/tools/patman/commit.py b/tools/patman/commit.py
1366> @@ -41,6 +41,9 @@ class Commit:
1367> self.rtags = collections.defaultdict(set)
1368> self.warn = []
1369>
1370> + def __str__(self):
1371> + return self.subject
1372> +
Simon Glassd85bb8f2022-01-29 14:14:09 -07001373> def add_change(self, version, info):
Simon Glass2112d072020-10-29 21:46:38 -06001374> """Add a new change line to the change list for a version.
1375>
1376A comment
1377
1378Reviewed-by: %s
1379''' % self.fred}
1380 patch2.comments = [comment2a, comment2b]
1381
1382 # This test works by setting up commits and patch for use by the fake
1383 # Rest API function _fake_patchwork2(). It calls various functions in
1384 # the status module after setting up tags in the commits, checking that
1385 # things behaves as expected
1386 self.commits = [commit1, commit2]
1387 self.patches = [patch1, patch2]
1388
1389 # Check that the output patches expectations:
1390 # 1 Subject 1
1391 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1392 # 2 Subject 2
1393 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1394 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1395 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1396 # 1 new response available in patchwork
1397
1398 series = Series()
1399 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001400 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001401 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2)
Simon Glassc100b262025-04-29 07:22:16 -06001402 status.check_and_show_status(series, '1234', None, None, False, True,
Simon Glass25b91c12025-04-29 07:22:19 -06001403 pwork)
Simon Glassb3080ec2025-05-08 04:58:49 +02001404 itr = iter(terminal.get_print_test_lines())
Simon Glass2112d072020-10-29 21:46:38 -06001405 col = terminal.Color()
Simon Glassd4d3fb42025-04-29 07:22:21 -06001406 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001407 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001408 self.assertEqual(
1409 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001410 next(itr))
1411 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001412
1413 self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED),
Simon Glassb3080ec2025-05-08 04:58:49 +02001414 next(itr))
1415 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(itr))
1416 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001417 self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA),
Simon Glassb3080ec2025-05-08 04:58:49 +02001418 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001419 self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA),
Simon Glassb3080ec2025-05-08 04:58:49 +02001420 next(itr))
1421 self.assertEqual(terminal.PrintLine(' > and more code',
1422 col.MAGENTA),
1423 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001424 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001425 ' Here is my comment above the above...', None), next(itr))
1426 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001427
Simon Glassd4d3fb42025-04-29 07:22:21 -06001428 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001429 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001430 self.assertEqual(
1431 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001432 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001433 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001434 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001435 self.assertEqual(
1436 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001437 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001438 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001439 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001440 self.assertEqual(
1441 terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001442 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001443 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001444 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001445
1446 self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED),
Simon Glassb3080ec2025-05-08 04:58:49 +02001447 next(itr))
1448 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(itr))
1449 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001450 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001451 ' > File: tools/patman/commit.py', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001452 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001453 ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001454 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001455 ' > + return self.subject', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001456 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001457 ' > +', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001458 self.assertEqual(
Simon Glassb3080ec2025-05-08 04:58:49 +02001459 terminal.PrintLine(
1460 ' > def add_change(self, version, info):',
1461 col.MAGENTA),
1462 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001463 self.assertEqual(terminal.PrintLine(
1464 ' > """Add a new change line to the change list for a version.',
Simon Glassb3080ec2025-05-08 04:58:49 +02001465 col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001466 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001467 ' >', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001468 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001469 ' A comment', None), next(itr))
1470 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001471
1472 self.assertEqual(terminal.PrintLine(
1473 '4 new responses available in patchwork (use -d to write them to a new branch)',
Simon Glassb3080ec2025-05-08 04:58:49 +02001474 None), next(itr))
Simon Glass6a222e62021-08-01 16:02:39 -06001475
Simon Glassd85bb8f2022-01-29 14:14:09 -07001476 def test_insert_tags(self):
Simon Glass6a222e62021-08-01 16:02:39 -06001477 """Test inserting of review tags"""
1478 msg = '''first line
1479second line.'''
1480 tags = [
1481 'Reviewed-by: Bin Meng <bmeng.cn@gmail.com>',
1482 'Tested-by: Bin Meng <bmeng.cn@gmail.com>'
1483 ]
1484 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.com>'
1485 tag_str = '\n'.join(tags)
1486
1487 new_msg = patchstream.insert_tags(msg, tags)
1488 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1489
1490 new_msg = patchstream.insert_tags(msg + '\n', tags)
1491 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1492
1493 msg += '\n\n' + signoff
1494 new_msg = patchstream.insert_tags(msg, tags)
1495 self.assertEqual(msg + '\n' + tag_str, new_msg)