blob: 61df82312ba759297cff5df781b45ebd53550b47 [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
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 Glassdf1bc5c2017-05-29 15:31:31 -060087
88 def tearDown(self):
Simon Glassed831d12025-04-29 07:22:10 -060089 if self.preserve_outdirs:
90 print(f'Output dir: {self.tmpdir}')
91 else:
92 shutil.rmtree(self.tmpdir)
Simon Glass02811582022-01-29 14:14:18 -070093 terminal.set_print_test_mode(False)
Simon Glassdf1bc5c2017-05-29 15:31:31 -060094
95 @staticmethod
Simon Glasseb209e52020-10-29 21:46:15 -060096 def _get_path(fname):
97 """Get the path to a test file
98
99 Args:
100 fname (str): Filename to obtain
101
102 Returns:
103 str: Full path to file in the test directory
104 """
Maxim Cournoyer0331edb2022-12-19 17:32:39 -0500105 return TEST_DATA_DIR / fname
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600106
107 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600108 def _get_text(cls, fname):
109 """Read a file as text
110
111 Args:
112 fname (str): Filename to read
113
114 Returns:
115 str: Contents of file
116 """
117 return open(cls._get_path(fname), encoding='utf-8').read()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600118
119 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -0600120 def _get_patch_name(cls, subject):
121 """Get the filename of a patch given its subject
122
123 Args:
124 subject (str): Patch subject
125
126 Returns:
127 str: Filename for that patch
128 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600129 fname = re.sub('[ :]', '-', subject)
130 return fname.replace('--', '-')
131
Simon Glasseb209e52020-10-29 21:46:15 -0600132 def _create_patches_for_test(self, series):
133 """Create patch files for use by tests
134
135 This copies patch files from the test directory as needed by the series
136
137 Args:
138 series (Series): Series containing commits to convert
139
140 Returns:
141 tuple:
142 str: Cover-letter filename, or None if none
143 fname_list: list of str, each a patch filename
144 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600145 cover_fname = None
146 fname_list = []
147 for i, commit in enumerate(series.commits):
Simon Glasseb209e52020-10-29 21:46:15 -0600148 clean_subject = self._get_patch_name(commit.subject)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600149 src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
150 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600151 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600152 fname_list.append(fname)
153 if series.get('cover'):
154 src_fname = '0000-cover-letter.patch'
155 cover_fname = os.path.join(self.tmpdir, src_fname)
156 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600157 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600158
159 return cover_fname, fname_list
160
Simon Glassd85bb8f2022-01-29 14:14:09 -0700161 def test_basic(self):
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600162 """Tests the basic flow of patman
163
164 This creates a series from some hard-coded patches build from a simple
165 tree with the following metadata in the top commit:
166
167 Series-to: u-boot
168 Series-prefix: RFC
Sean Andersondc1cd132021-10-22 19:07:04 -0400169 Series-postfix: some-branch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600170 Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
171 Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Sean Andersoncf13b862020-05-04 16:28:36 -0400172 Series-version: 3
173 Patch-cc: fred
174 Series-process-log: sort, uniq
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600175 Series-changes: 4
176 - Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400177 - Multi
178 line
179 change
180
181 Commit-changes: 2
182 - Changes only for this commit
183
Simon Glassf1aab6f2025-04-29 07:22:07 -0600184 Cover-changes: 4
Sean Andersoncf13b862020-05-04 16:28:36 -0400185 - Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600186
187 Cover-letter:
188 test: A test patch series
189 This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400190 letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600191 works
192 END
193
194 and this in the first commit:
195
Sean Andersoncf13b862020-05-04 16:28:36 -0400196 Commit-changes: 2
197 - second revision change
198
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600199 Series-notes:
200 some notes
201 about some things
202 from the first commit
203 END
204
205 Commit-notes:
206 Some notes about
207 the first commit
208 END
209
210 with the following commands:
211
212 git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
213 git format-patch --subject-prefix RFC --cover-letter HEAD~2
214 mv 00* /path/to/tools/patman/test
215
216 It checks these aspects:
217 - git log can be processed by patchstream
218 - emailing patches uses the correct command
219 - CC file has information on each commit
220 - cover letter has the expected text and subject
221 - each patch has the correct subject
222 - dry-run information prints out correctly
223 - unicode is handled correctly
Sean Andersondc1cd132021-10-22 19:07:04 -0400224 - Series-to, Series-cc, Series-prefix, Series-postfix, Cover-letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600225 - Cover-letter-cc, Series-version, Series-changes, Series-notes
226 - Commit-notes
227 """
228 process_tags = True
Simon Glass1f975b92021-01-23 08:56:15 -0700229 ignore_bad_tags = False
Simon Glassb3080ec2025-05-08 04:58:49 +0200230 stefan = (b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'
231 .decode('utf-8'))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600232 rick = 'Richard III <richard@palace.gov>'
Simon Glass4f817892019-05-14 15:53:53 -0600233 mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600234 add_maintainers = [stefan, rick]
235 dry_run = True
236 in_reply_to = mel
237 count = 2
Simon Glass5efa3662025-04-07 22:51:45 +1200238 alias = {
Simon Glass95745aa2020-10-29 21:46:13 -0600239 'fdt': ['simon'],
240 'u-boot': ['u-boot@lists.denx.de'],
Simon Glass06202d62020-10-29 21:46:27 -0600241 'simon': [self.leb],
Simon Glass3b762cc2020-10-29 21:46:28 -0600242 'fred': [self.fred],
Sean Anderson25978092024-04-18 22:36:31 -0400243 'joe': [self.joe],
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600244 }
245
Simon Glasseb209e52020-10-29 21:46:15 -0600246 text = self._get_text('test01.txt')
Simon Glass93f61c02020-10-29 21:46:19 -0600247 series = patchstream.get_metadata_for_test(text)
Simon Glass414f1e02025-02-27 12:27:30 -0700248 series.base_commit = Commit('1a44532')
249 series.branch = 'mybranch'
Simon Glasseb209e52020-10-29 21:46:15 -0600250 cover_fname, args = self._create_patches_for_test(series)
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500251 get_maintainer_script = str(pathlib.Path(__file__).parent.parent.parent
252 / 'get_maintainer.pl') + ' --norolestats'
Simon Glass14d64e32025-04-29 07:21:59 -0600253 with terminal.capture() as out:
Simon Glass93f61c02020-10-29 21:46:19 -0600254 patchstream.fix_patches(series, args)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600255 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -0600256 patchstream.insert_cover_letter(cover_fname, series, count)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600257 series.DoChecks()
258 cc_file = series.MakeCcFile(process_tags, cover_fname,
Chris Packhamb84fb482018-06-07 20:45:06 +1200259 not ignore_bad_tags, add_maintainers,
Simon Glass9938b7b2025-04-07 22:51:46 +1200260 None, get_maintainer_script, alias)
Simon Glass761648b2022-01-29 14:14:11 -0700261 cmd = gitutil.email_patches(
Simon Glass95745aa2020-10-29 21:46:13 -0600262 series, cover_fname, args, dry_run, not ignore_bad_tags,
Simon Glass5efa3662025-04-07 22:51:45 +1200263 cc_file, alias, in_reply_to=in_reply_to, thread=None)
Simon Glass32f12a7e2025-04-07 22:51:47 +1200264 series.ShowActions(args, cmd, process_tags, alias)
Simon Glassf544a2d2019-10-31 07:42:51 -0600265 cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600266 os.remove(cc_file)
267
Simon Glassb3080ec2025-05-08 04:58:49 +0200268 itr = iter(out[0].getvalue().splitlines())
Simon Glass42e3d392020-10-29 21:46:29 -0600269 self.assertEqual('Cleaned %s patches' % len(series.commits),
Simon Glassb3080ec2025-05-08 04:58:49 +0200270 next(itr))
271 self.assertEqual('Change log missing for v2', next(itr))
272 self.assertEqual('Change log missing for v3', next(itr))
273 self.assertEqual('Change log for unknown version v4', next(itr))
274 self.assertEqual("Alias 'pci' not found", next(itr))
275 while next(itr) != 'Cc processing complete':
Simon Glass620639c2023-03-08 10:52:54 -0800276 pass
Simon Glassb3080ec2025-05-08 04:58:49 +0200277 self.assertIn('Dry run', next(itr))
278 self.assertEqual('', next(itr))
279 self.assertIn('Send a total of %d patches' % count, next(itr))
280 prev = next(itr)
Simon Glass42e3d392020-10-29 21:46:29 -0600281 for i, commit in enumerate(series.commits):
282 self.assertEqual(' %s' % args[i], prev)
283 while True:
Simon Glassb3080ec2025-05-08 04:58:49 +0200284 prev = next(itr)
Simon Glass42e3d392020-10-29 21:46:29 -0600285 if 'Cc:' not in prev:
286 break
287 self.assertEqual('To: u-boot@lists.denx.de', prev)
Simon Glassb3080ec2025-05-08 04:58:49 +0200288 self.assertEqual('Cc: %s' % stefan, next(itr))
289 self.assertEqual('Version: 3', next(itr))
290 self.assertEqual('Prefix:\t RFC', next(itr))
291 self.assertEqual('Postfix:\t some-branch', next(itr))
292 self.assertEqual('Cover: 4 lines', next(itr))
293 self.assertEqual(' Cc: %s' % self.fred, next(itr))
294 self.assertEqual(' Cc: %s' % self.joe, next(itr))
Simon Glass9dfb3112020-11-08 20:36:18 -0700295 self.assertEqual(' Cc: %s' % self.leb,
Simon Glassb3080ec2025-05-08 04:58:49 +0200296 next(itr))
297 self.assertEqual(' Cc: %s' % mel, next(itr))
298 self.assertEqual(' Cc: %s' % rick, next(itr))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600299 expected = ('Git command: git send-email --annotate '
Simon Glassa8ba0792025-05-08 04:38:30 +0200300 '--in-reply-to="%s" --to u-boot@lists.denx.de '
Simon Glass1ee91c12020-11-03 13:54:10 -0700301 '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600302 % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
Simon Glass4f817892019-05-14 15:53:53 -0600303 ' '.join(args)))
Simon Glassb3080ec2025-05-08 04:58:49 +0200304 self.assertEqual(expected, next(itr))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600305
Simon Glass9dfb3112020-11-08 20:36:18 -0700306 self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0])
Simon Glass95745aa2020-10-29 21:46:13 -0600307 self.assertEqual(
Sean Anderson25978092024-04-18 22:36:31 -0400308 '%s %s\0%s\0%s\0%s\0%s' % (args[1], self.fred, self.joe, self.leb,
309 rick, stefan),
Simon Glass9dfb3112020-11-08 20:36:18 -0700310 cc_lines[1])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600311
312 expected = '''
313This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400314letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600315works
316
317some notes
318about some things
319from the first commit
320
321Changes in v4:
Sean Andersoncf13b862020-05-04 16:28:36 -0400322- Multi
323 line
324 change
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600325- Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400326- Some notes for the cover letter
Sean Andersone45678c2024-04-18 22:36:32 -0400327- fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600328
329Simon Glass (2):
330 pci: Correct cast for sandbox
Siva Durga Prasad Paladugub3d55ea2018-07-16 15:56:11 +0530331 fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600332
333 cmd/pci.c | 3 ++-
334 fs/fat/fat.c | 1 +
335 lib/efi_loader/efi_memory.c | 1 +
336 lib/fdtdec.c | 3 ++-
337 4 files changed, 6 insertions(+), 2 deletions(-)
338
339--\x20
3402.7.4
341
Simon Glass414f1e02025-02-27 12:27:30 -0700342base-commit: 1a44532
343branch: mybranch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600344'''
Simon Glassf544a2d2019-10-31 07:42:51 -0600345 lines = open(cover_fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600346 self.assertEqual(
Sean Andersondc1cd132021-10-22 19:07:04 -0400347 'Subject: [RFC PATCH some-branch v3 0/2] test: A test patch series',
Simon Glass95745aa2020-10-29 21:46:13 -0600348 lines[3])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600349 self.assertEqual(expected.splitlines(), lines[7:])
350
351 for i, fname in enumerate(args):
Simon Glassf544a2d2019-10-31 07:42:51 -0600352 lines = open(fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600353 subject = [line for line in lines if line.startswith('Subject')]
354 self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
355 subject[0][:18])
Sean Andersoncf13b862020-05-04 16:28:36 -0400356
357 # Check that we got our commit notes
358 start = 0
359 expected = ''
360
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600361 if i == 0:
Sean Andersoncf13b862020-05-04 16:28:36 -0400362 start = 17
363 expected = '''---
364Some notes about
365the first commit
366
367(no changes since v2)
368
369Changes in v2:
370- second revision change'''
371 elif i == 1:
372 start = 17
373 expected = '''---
374
375Changes in v4:
376- Multi
377 line
378 change
Sean Andersone45678c2024-04-18 22:36:32 -0400379- New
Sean Andersoncf13b862020-05-04 16:28:36 -0400380- Some changes
381
382Changes in v2:
383- Changes only for this commit'''
384
385 if expected:
386 expected = expected.splitlines()
387 self.assertEqual(expected, lines[start:(start+len(expected))])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600388
Simon Glassda1a6ec2025-03-28 07:02:20 -0600389 def test_base_commit(self):
390 """Test adding a base commit with no cover letter"""
391 orig_text = self._get_text('test01.txt')
392 pos = orig_text.index('commit 5ab48490f03051875ab13d288a4bf32b507d76fd')
393 text = orig_text[:pos]
394 series = patchstream.get_metadata_for_test(text)
395 series.base_commit = Commit('1a44532')
396 series.branch = 'mybranch'
397 cover_fname, args = self._create_patches_for_test(series)
398 self.assertFalse(cover_fname)
Simon Glass14d64e32025-04-29 07:21:59 -0600399 with terminal.capture() as out:
Simon Glassda1a6ec2025-03-28 07:02:20 -0600400 patchstream.fix_patches(series, args, insert_base_commit=True)
401 self.assertEqual('Cleaned 1 patch\n', out[0].getvalue())
402 lines = tools.read_file(args[0], binary=False).splitlines()
403 pos = lines.index('-- ')
404
405 # We expect these lines at the end:
406 # -- (with trailing space)
407 # 2.7.4
408 # (empty)
409 # base-commit: xxx
410 # branch: xxx
411 self.assertEqual('base-commit: 1a44532', lines[pos + 3])
412 self.assertEqual('branch: mybranch', lines[pos + 4])
413
Simon Glass54f1c5b2020-07-05 21:41:50 -0600414 def make_commit_with_file(self, subject, body, fname, text):
415 """Create a file and add it to the git repo with a new commit
416
417 Args:
418 subject (str): Subject for the commit
419 body (str): Body text of the commit
420 fname (str): Filename of file to create
421 text (str): Text to put into the file
422 """
Simon Glass41dfb6e2025-05-08 05:13:35 +0200423 path = os.path.join(self.tmpdir, fname)
Simon Glass80025522022-01-29 14:14:04 -0700424 tools.write_file(path, text, binary=False)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600425 index = self.repo.index
426 index.add(fname)
Simon Glass547cba62022-02-11 13:23:18 -0700427 # pylint doesn't seem to find this
428 # pylint: disable=E1101
Simon Glass95745aa2020-10-29 21:46:13 -0600429 author = pygit2.Signature('Test user', 'test@email.com')
Simon Glass54f1c5b2020-07-05 21:41:50 -0600430 committer = author
431 tree = index.write_tree()
432 message = subject + '\n' + body
433 self.repo.create_commit('HEAD', author, committer, message, tree,
434 [self.repo.head.target])
435
436 def make_git_tree(self):
437 """Make a simple git tree suitable for testing
438
439 It has three branches:
440 'base' has two commits: PCI, main
441 'first' has base as upstream and two more commits: I2C, SPI
442 'second' has base as upstream and three more: video, serial, bootm
443
444 Returns:
Simon Glasseb209e52020-10-29 21:46:15 -0600445 pygit2.Repository: repository
Simon Glass54f1c5b2020-07-05 21:41:50 -0600446 """
447 repo = pygit2.init_repository(self.gitdir)
448 self.repo = repo
449 new_tree = repo.TreeBuilder().write()
450
Simon Glass547cba62022-02-11 13:23:18 -0700451 # pylint doesn't seem to find this
452 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600453 author = pygit2.Signature('Test user', 'test@email.com')
454 committer = author
Simon Glasseb209e52020-10-29 21:46:15 -0600455 _ = repo.create_commit('HEAD', author, committer, 'Created master',
456 new_tree, [])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600457
458 self.make_commit_with_file('Initial commit', '''
459Add a README
460
461''', 'README', '''This is the README file
462describing this project
463in very little detail''')
464
465 self.make_commit_with_file('pci: PCI implementation', '''
466Here is a basic PCI implementation
467
468''', 'pci.c', '''This is a file
469it has some contents
470and some more things''')
471 self.make_commit_with_file('main: Main program', '''
472Hello here is the second commit.
473''', 'main.c', '''This is the main file
474there is very little here
475but we can always add more later
476if we want to
477
478Series-to: u-boot
479Series-cc: Barry Crump <bcrump@whataroa.nz>
480''')
481 base_target = repo.revparse_single('HEAD')
482 self.make_commit_with_file('i2c: I2C things', '''
483This has some stuff to do with I2C
484''', 'i2c.c', '''And this is the file contents
485with some I2C-related things in it''')
486 self.make_commit_with_file('spi: SPI fixes', '''
487SPI needs some fixes
488and here they are
Simon Glassd0a0a582020-10-29 21:46:36 -0600489
490Signed-off-by: %s
491
492Series-to: u-boot
493Commit-notes:
494title of the series
495This is the cover letter for the series
496with various details
497END
498''' % self.leb, 'spi.c', '''Some fixes for SPI in this
Simon Glass54f1c5b2020-07-05 21:41:50 -0600499file to make SPI work
500better than before''')
501 first_target = repo.revparse_single('HEAD')
502
503 target = repo.revparse_single('HEAD~2')
Simon Glass547cba62022-02-11 13:23:18 -0700504 # pylint doesn't seem to find this
505 # pylint: disable=E1101
Simon Glass573abf82025-05-08 05:23:41 +0200506 repo.reset(target.oid, pygit2.enums.ResetMode.HARD)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600507 self.make_commit_with_file('video: Some video improvements', '''
508Fix up the video so that
509it looks more purple. Purple is
510a very nice colour.
511''', 'video.c', '''More purple here
512Purple and purple
513Even more purple
514Could not be any more purple''')
Simon Glassb8ca4692025-05-08 05:26:16 +0200515 self.make_commit_with_file('serial: Add a serial driver', f'''
Simon Glass54f1c5b2020-07-05 21:41:50 -0600516Here is the serial driver
517for my chip.
518
519Cover-letter:
Simon Glassb8ca4692025-05-08 05:26:16 +0200520{self.TITLE_SECOND}
Simon Glass54f1c5b2020-07-05 21:41:50 -0600521This series implements support
522for my glorious board.
523END
Simon Glassb8ca4692025-05-08 05:26:16 +0200524Series-to: u-boot
525Series-links: {self.SERIES_ID_SECOND_V1}
Simon Glass54f1c5b2020-07-05 21:41:50 -0600526''', 'serial.c', '''The code for the
527serial driver is here''')
528 self.make_commit_with_file('bootm: Make it boot', '''
529This makes my board boot
530with a fix to the bootm
531command
532''', 'bootm.c', '''Fix up the bootm
533command to make the code as
534complicated as possible''')
535 second_target = repo.revparse_single('HEAD')
536
537 repo.branches.local.create('first', first_target)
538 repo.config.set_multivar('branch.first.remote', '', '.')
539 repo.config.set_multivar('branch.first.merge', '', 'refs/heads/base')
540
541 repo.branches.local.create('second', second_target)
542 repo.config.set_multivar('branch.second.remote', '', '.')
543 repo.config.set_multivar('branch.second.merge', '', 'refs/heads/base')
544
545 repo.branches.local.create('base', base_target)
Simon Glass573abf82025-05-08 05:23:41 +0200546
547 target = repo.lookup_reference('refs/heads/first')
548 repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
549 target = repo.revparse_single('HEAD')
550 repo.reset(target.oid, pygit2.enums.ResetMode.HARD)
551
552 self.assertFalse(gitutil.check_dirty(self.gitdir, self.tmpdir))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600553 return repo
554
Simon Glassd85bb8f2022-01-29 14:14:09 -0700555 def test_branch(self):
Simon Glass54f1c5b2020-07-05 21:41:50 -0600556 """Test creating patches from a branch"""
557 repo = self.make_git_tree()
558 target = repo.lookup_reference('refs/heads/first')
Simon Glass547cba62022-02-11 13:23:18 -0700559 # pylint doesn't seem to find this
560 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600561 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
562 control.setup()
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200563 orig_dir = os.getcwd()
Simon Glass54f1c5b2020-07-05 21:41:50 -0600564 try:
Simon Glass41dfb6e2025-05-08 05:13:35 +0200565 os.chdir(self.tmpdir)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600566
567 # Check that it can detect the current branch
Simon Glass761648b2022-01-29 14:14:11 -0700568 self.assertEqual(2, gitutil.count_commits_to_branch(None))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600569 col = terminal.Color()
Simon Glass14d64e32025-04-29 07:21:59 -0600570 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600571 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600572 col, branch=None, count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100573 ignore_binary=False, signoff=True)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600574 self.assertIsNone(cover_fname)
575 self.assertEqual(2, len(patch_files))
Simon Glass2eb4da72020-07-05 21:41:51 -0600576
577 # Check that it can detect a different branch
Simon Glass761648b2022-01-29 14:14:11 -0700578 self.assertEqual(3, gitutil.count_commits_to_branch('second'))
Simon Glass14d64e32025-04-29 07:21:59 -0600579 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600580 series, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600581 col, branch='second', count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100582 ignore_binary=False, signoff=True)
Simon Glass2eb4da72020-07-05 21:41:51 -0600583 self.assertIsNotNone(cover_fname)
584 self.assertEqual(3, len(patch_files))
Simon Glassb3bf4e12020-07-05 21:41:52 -0600585
Simon Glass414f1e02025-02-27 12:27:30 -0700586 cover = tools.read_file(cover_fname, binary=False)
587 lines = cover.splitlines()[-2:]
588 base = repo.lookup_reference('refs/heads/base').target
589 self.assertEqual(f'base-commit: {base}', lines[0])
590 self.assertEqual('branch: second', lines[1])
591
Simon Glassda1a6ec2025-03-28 07:02:20 -0600592 # Make sure that the base-commit is not present when it is in the
593 # cover letter
594 for fname in patch_files:
595 self.assertNotIn(b'base-commit:', tools.read_file(fname))
596
Simon Glassb3bf4e12020-07-05 21:41:52 -0600597 # Check that it can skip patches at the end
Simon Glass14d64e32025-04-29 07:21:59 -0600598 with terminal.capture() as _:
Simon Glassc0257982025-04-29 07:22:11 -0600599 _, cover_fname, patch_files = send.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600600 col, branch='second', count=-1, start=0, end=1,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100601 ignore_binary=False, signoff=True)
Simon Glassb3bf4e12020-07-05 21:41:52 -0600602 self.assertIsNotNone(cover_fname)
603 self.assertEqual(2, len(patch_files))
Simon Glass414f1e02025-02-27 12:27:30 -0700604
605 cover = tools.read_file(cover_fname, binary=False)
606 lines = cover.splitlines()[-2:]
607 base2 = repo.lookup_reference('refs/heads/second')
608 ref = base2.peel(pygit2.GIT_OBJ_COMMIT).parents[0].parents[0].id
609 self.assertEqual(f'base-commit: {ref}', lines[0])
610 self.assertEqual('branch: second', lines[1])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600611 finally:
612 os.chdir(orig_dir)
Simon Glass06202d62020-10-29 21:46:27 -0600613
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500614 def test_custom_get_maintainer_script(self):
615 """Validate that a custom get_maintainer script gets used."""
616 self.make_git_tree()
Simon Glass41dfb6e2025-05-08 05:13:35 +0200617 with directory_excursion(self.tmpdir):
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500618 # Setup git.
619 os.environ['GIT_CONFIG_GLOBAL'] = '/dev/null'
620 os.environ['GIT_CONFIG_SYSTEM'] = '/dev/null'
621 tools.run('git', 'config', 'user.name', 'Dummy')
622 tools.run('git', 'config', 'user.email', 'dumdum@dummy.com')
623 tools.run('git', 'branch', 'upstream')
624 tools.run('git', 'branch', '--set-upstream-to=upstream')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500625
626 # Setup patman configuration.
627 with open('.patman', 'w', buffering=1) as f:
628 f.write('[settings]\n'
629 'get_maintainer_script: dummy-script.sh\n'
Sean Andersona06df742024-04-18 22:36:30 -0400630 'check_patch: False\n'
631 'add_maintainers: True\n')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500632 with open('dummy-script.sh', 'w', buffering=1) as f:
633 f.write('#!/usr/bin/env python\n'
634 'print("hello@there.com")\n')
635 os.chmod('dummy-script.sh', 0x555)
Simon Glass41dfb6e2025-05-08 05:13:35 +0200636 tools.run('git', 'add', '.')
637 tools.run('git', 'commit', '-m', 'new commit')
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500638
639 # Finally, do the test
Simon Glass14d64e32025-04-29 07:21:59 -0600640 with terminal.capture():
Maxim Cournoyer3ef23e92022-12-20 00:28:46 -0500641 output = tools.run(PATMAN_DIR / 'patman', '--dry-run')
642 # Assert the email address is part of the dry-run
643 # output.
644 self.assertIn('hello@there.com', output)
645
Simon Glassd85bb8f2022-01-29 14:14:09 -0700646 def test_tags(self):
Simon Glass06202d62020-10-29 21:46:27 -0600647 """Test collection of tags in a patchstream"""
648 text = '''This is a patch
649
650Signed-off-by: Terminator
Simon Glass3b762cc2020-10-29 21:46:28 -0600651Reviewed-by: %s
652Reviewed-by: %s
Simon Glass06202d62020-10-29 21:46:27 -0600653Tested-by: %s
Simon Glass3b762cc2020-10-29 21:46:28 -0600654''' % (self.joe, self.mary, self.leb)
Simon Glass06202d62020-10-29 21:46:27 -0600655 pstrm = PatchStream.process_text(text)
656 self.assertEqual(pstrm.commit.rtags, {
Simon Glass3b762cc2020-10-29 21:46:28 -0600657 'Reviewed-by': {self.joe, self.mary},
Simon Glass06202d62020-10-29 21:46:27 -0600658 'Tested-by': {self.leb}})
Simon Glass3b762cc2020-10-29 21:46:28 -0600659
Simon Glassd85bb8f2022-01-29 14:14:09 -0700660 def test_invalid_tag(self):
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200661 """Test invalid tag in a patchstream"""
662 text = '''This is a patch
663
664Serie-version: 2
665'''
666 with self.assertRaises(ValueError) as exc:
667 pstrm = PatchStream.process_text(text)
668 self.assertEqual("Line 3: Invalid tag = 'Serie-version: 2'",
669 str(exc.exception))
670
Simon Glassd85bb8f2022-01-29 14:14:09 -0700671 def test_missing_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600672 """Test a missing END tag"""
673 text = '''This is a patch
674
675Cover-letter:
676This is the title
677missing END after this line
678Signed-off-by: Fred
679'''
680 pstrm = PatchStream.process_text(text)
681 self.assertEqual(["Missing 'END' in section 'cover'"],
682 pstrm.commit.warn)
683
Simon Glassd85bb8f2022-01-29 14:14:09 -0700684 def test_missing_blank_line(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600685 """Test a missing blank line after a tag"""
686 text = '''This is a patch
687
688Series-changes: 2
689- First line of changes
690- Missing blank line after this line
691Signed-off-by: Fred
692'''
693 pstrm = PatchStream.process_text(text)
694 self.assertEqual(["Missing 'blank line' in section 'Series-changes'"],
695 pstrm.commit.warn)
696
Simon Glassd85bb8f2022-01-29 14:14:09 -0700697 def test_invalid_commit_tag(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600698 """Test an invalid Commit-xxx tag"""
699 text = '''This is a patch
700
701Commit-fred: testing
702'''
703 pstrm = PatchStream.process_text(text)
704 self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn)
705
Simon Glassd85bb8f2022-01-29 14:14:09 -0700706 def test_self_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600707 """Test a tested by tag by this user"""
708 test_line = 'Tested-by: %s@napier.com' % os.getenv('USER')
709 text = '''This is a patch
710
711%s
712''' % test_line
713 pstrm = PatchStream.process_text(text)
714 self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn)
715
Simon Glassd85bb8f2022-01-29 14:14:09 -0700716 def test_space_before_tab(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600717 """Test a space before a tab"""
718 text = '''This is a patch
719
720+ \tSomething
721'''
722 pstrm = PatchStream.process_text(text)
723 self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn)
724
Simon Glassd85bb8f2022-01-29 14:14:09 -0700725 def test_lines_after_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600726 """Test detecting lines after TEST= line"""
727 text = '''This is a patch
728
729TEST=sometest
730more lines
731here
732'''
733 pstrm = PatchStream.process_text(text)
734 self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn)
735
Simon Glassd85bb8f2022-01-29 14:14:09 -0700736 def test_blank_line_at_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600737 """Test detecting a blank line at the end of a file"""
738 text = '''This is a patch
739
740diff --git a/lib/fdtdec.c b/lib/fdtdec.c
741index c072e54..942244f 100644
742--- a/lib/fdtdec.c
743+++ b/lib/fdtdec.c
744@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void)
745 }
746
747 gd->ram_size = (phys_size_t)(res.end - res.start + 1);
748- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size);
749+ debug("%s: Initial DRAM size %llx\n", __func__,
750+ (unsigned long long)gd->ram_size);
751+
752diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
753
754--
7552.7.4
756
757 '''
758 pstrm = PatchStream.process_text(text)
759 self.assertEqual(
760 ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"],
761 pstrm.commit.warn)
Simon Glass1c1f2072020-10-29 21:46:34 -0600762
Simon Glassd85bb8f2022-01-29 14:14:09 -0700763 def test_no_upstream(self):
Simon Glass1c1f2072020-10-29 21:46:34 -0600764 """Test CountCommitsToBranch when there is no upstream"""
765 repo = self.make_git_tree()
766 target = repo.lookup_reference('refs/heads/base')
Simon Glass547cba62022-02-11 13:23:18 -0700767 # pylint doesn't seem to find this
768 # pylint: disable=E1101
Simon Glass1c1f2072020-10-29 21:46:34 -0600769 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
770
771 # Check that it can detect the current branch
Heinrich Schuchardtd01d6672023-04-20 20:07:29 +0200772 orig_dir = os.getcwd()
Simon Glass1c1f2072020-10-29 21:46:34 -0600773 try:
Simon Glass1c1f2072020-10-29 21:46:34 -0600774 os.chdir(self.gitdir)
775 with self.assertRaises(ValueError) as exc:
Simon Glass761648b2022-01-29 14:14:11 -0700776 gitutil.count_commits_to_branch(None)
Simon Glass1c1f2072020-10-29 21:46:34 -0600777 self.assertIn(
778 "Failed to determine upstream: fatal: no upstream configured for branch 'base'",
779 str(exc.exception))
780 finally:
781 os.chdir(orig_dir)
Simon Glass3db916d2020-10-29 21:46:35 -0600782
783 @staticmethod
Simon Glass25b91c12025-04-29 07:22:19 -0600784 def _fake_patchwork(subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600785 """Fake Patchwork server for the function below
786
787 This handles accessing a series, providing a list consisting of a
788 single patch
Simon Glassf9b03cf2020-11-03 13:54:14 -0700789
790 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -0700791 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600792 """
793 re_series = re.match(r'series/(\d*)/$', subpath)
794 if re_series:
795 series_num = re_series.group(1)
796 if series_num == '1234':
797 return {'patches': [
798 {'id': '1', 'name': 'Some patch'}]}
799 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
800
Simon Glassd85bb8f2022-01-29 14:14:09 -0700801 def test_status_mismatch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600802 """Test Patchwork patches not matching the series"""
Simon Glass25b91c12025-04-29 07:22:19 -0600803 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork)
Simon Glass14d64e32025-04-29 07:21:59 -0600804 with terminal.capture() as (_, err):
Simon Glass3729b8b2025-04-29 07:22:24 -0600805 patches = asyncio.run(status.check_status(1234, pwork))
Simon Glass27280f42025-04-29 07:22:17 -0600806 status.check_patch_count(0, len(patches))
Simon Glass3db916d2020-10-29 21:46:35 -0600807 self.assertIn('Warning: Patchwork reports 1 patches, series has 0',
808 err.getvalue())
809
Simon Glassd85bb8f2022-01-29 14:14:09 -0700810 def test_status_read_patch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600811 """Test handling a single patch in Patchwork"""
Simon Glass25b91c12025-04-29 07:22:19 -0600812 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork)
Simon Glass3729b8b2025-04-29 07:22:24 -0600813 patches = asyncio.run(status.check_status(1234, pwork))
Simon Glass3db916d2020-10-29 21:46:35 -0600814 self.assertEqual(1, len(patches))
815 patch = patches[0]
816 self.assertEqual('1', patch.id)
817 self.assertEqual('Some patch', patch.raw_subject)
818
Simon Glassd85bb8f2022-01-29 14:14:09 -0700819 def test_parse_subject(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600820 """Test parsing of the patch subject"""
Simon Glass232eefd2025-04-29 07:22:14 -0600821 patch = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600822
823 # Simple patch not in a series
824 patch.parse_subject('Testing')
825 self.assertEqual('Testing', patch.raw_subject)
826 self.assertEqual('Testing', patch.subject)
827 self.assertEqual(1, patch.seq)
828 self.assertEqual(1, patch.count)
829 self.assertEqual(None, patch.prefix)
830 self.assertEqual(None, patch.version)
831
832 # First patch in a series
833 patch.parse_subject('[1/2] Testing')
834 self.assertEqual('[1/2] Testing', patch.raw_subject)
835 self.assertEqual('Testing', patch.subject)
836 self.assertEqual(1, patch.seq)
837 self.assertEqual(2, patch.count)
838 self.assertEqual(None, patch.prefix)
839 self.assertEqual(None, patch.version)
840
841 # Second patch in a series
842 patch.parse_subject('[2/2] Testing')
843 self.assertEqual('Testing', patch.subject)
844 self.assertEqual(2, patch.seq)
845 self.assertEqual(2, patch.count)
846 self.assertEqual(None, patch.prefix)
847 self.assertEqual(None, patch.version)
848
849 # RFC patch
850 patch.parse_subject('[RFC,3/7] Testing')
851 self.assertEqual('Testing', patch.subject)
852 self.assertEqual(3, patch.seq)
853 self.assertEqual(7, patch.count)
854 self.assertEqual('RFC', patch.prefix)
855 self.assertEqual(None, patch.version)
856
857 # Version patch
858 patch.parse_subject('[v2,3/7] Testing')
859 self.assertEqual('Testing', patch.subject)
860 self.assertEqual(3, patch.seq)
861 self.assertEqual(7, patch.count)
862 self.assertEqual(None, patch.prefix)
863 self.assertEqual('v2', patch.version)
864
865 # All fields
866 patch.parse_subject('[RESEND,v2,3/7] Testing')
867 self.assertEqual('Testing', patch.subject)
868 self.assertEqual(3, patch.seq)
869 self.assertEqual(7, patch.count)
870 self.assertEqual('RESEND', patch.prefix)
871 self.assertEqual('v2', patch.version)
872
873 # RFC only
874 patch.parse_subject('[RESEND] Testing')
875 self.assertEqual('Testing', patch.subject)
876 self.assertEqual(1, patch.seq)
877 self.assertEqual(1, patch.count)
878 self.assertEqual('RESEND', patch.prefix)
879 self.assertEqual(None, patch.version)
880
Simon Glassd85bb8f2022-01-29 14:14:09 -0700881 def test_compare_series(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600882 """Test operation of compare_with_series()"""
883 commit1 = Commit('abcd')
884 commit1.subject = 'Subject 1'
885 commit2 = Commit('ef12')
886 commit2.subject = 'Subject 2'
887 commit3 = Commit('3456')
888 commit3.subject = 'Subject 2'
889
Simon Glass232eefd2025-04-29 07:22:14 -0600890 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600891 patch1.subject = 'Subject 1'
Simon Glass232eefd2025-04-29 07:22:14 -0600892 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600893 patch2.subject = 'Subject 2'
Simon Glass232eefd2025-04-29 07:22:14 -0600894 patch3 = patchwork.Patch('3')
Simon Glass3db916d2020-10-29 21:46:35 -0600895 patch3.subject = 'Subject 2'
896
897 series = Series()
898 series.commits = [commit1]
899 patches = [patch1]
900 patch_for_commit, commit_for_patch, warnings = (
901 status.compare_with_series(series, patches))
902 self.assertEqual(1, len(patch_for_commit))
903 self.assertEqual(patch1, patch_for_commit[0])
904 self.assertEqual(1, len(commit_for_patch))
905 self.assertEqual(commit1, commit_for_patch[0])
906
907 series.commits = [commit1]
908 patches = [patch1, patch2]
909 patch_for_commit, commit_for_patch, warnings = (
910 status.compare_with_series(series, patches))
911 self.assertEqual(1, len(patch_for_commit))
912 self.assertEqual(patch1, patch_for_commit[0])
913 self.assertEqual(1, len(commit_for_patch))
914 self.assertEqual(commit1, commit_for_patch[0])
915 self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"],
916 warnings)
917
918 series.commits = [commit1, commit2]
919 patches = [patch1]
920 patch_for_commit, commit_for_patch, warnings = (
921 status.compare_with_series(series, patches))
922 self.assertEqual(1, len(patch_for_commit))
923 self.assertEqual(patch1, patch_for_commit[0])
924 self.assertEqual(1, len(commit_for_patch))
925 self.assertEqual(commit1, commit_for_patch[0])
926 self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"],
927 warnings)
928
929 series.commits = [commit1, commit2, commit3]
930 patches = [patch1, patch2]
931 patch_for_commit, commit_for_patch, warnings = (
932 status.compare_with_series(series, patches))
933 self.assertEqual(2, len(patch_for_commit))
934 self.assertEqual(patch1, patch_for_commit[0])
935 self.assertEqual(patch2, patch_for_commit[1])
936 self.assertEqual(1, len(commit_for_patch))
937 self.assertEqual(commit1, commit_for_patch[0])
938 self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')",
939 "Multiple commits match patch 2 ('Subject 2'):\n"
940 ' Subject 2\n Subject 2'],
941 warnings)
942
943 series.commits = [commit1, commit2]
944 patches = [patch1, patch2, patch3]
945 patch_for_commit, commit_for_patch, warnings = (
946 status.compare_with_series(series, patches))
947 self.assertEqual(1, len(patch_for_commit))
948 self.assertEqual(patch1, patch_for_commit[0])
949 self.assertEqual(2, len(commit_for_patch))
950 self.assertEqual(commit1, commit_for_patch[0])
951 self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n"
952 ' Subject 2\n Subject 2',
953 "Cannot find commit for patch 3 ('Subject 2')"],
954 warnings)
955
Simon Glass25b91c12025-04-29 07:22:19 -0600956 def _fake_patchwork2(self, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600957 """Fake Patchwork server for the function below
958
959 This handles accessing series, patches and comments, providing the data
960 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700961
962 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -0700963 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600964 """
965 re_series = re.match(r'series/(\d*)/$', subpath)
966 re_patch = re.match(r'patches/(\d*)/$', subpath)
967 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
968 if re_series:
969 series_num = re_series.group(1)
970 if series_num == '1234':
971 return {'patches': self.patches}
972 elif re_patch:
973 patch_num = int(re_patch.group(1))
974 patch = self.patches[patch_num - 1]
975 return patch
976 elif re_comments:
977 patch_num = int(re_comments.group(1))
978 patch = self.patches[patch_num - 1]
979 return patch.comments
980 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
981
Simon Glassd85bb8f2022-01-29 14:14:09 -0700982 def test_find_new_responses(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600983 """Test operation of find_new_responses()"""
984 commit1 = Commit('abcd')
985 commit1.subject = 'Subject 1'
986 commit2 = Commit('ef12')
987 commit2.subject = 'Subject 2'
988
Simon Glass232eefd2025-04-29 07:22:14 -0600989 patch1 = patchwork.Patch('1')
Simon Glass3db916d2020-10-29 21:46:35 -0600990 patch1.parse_subject('[1/2] Subject 1')
991 patch1.name = patch1.raw_subject
992 patch1.content = 'This is my patch content'
993 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
994
995 patch1.comments = [comment1a]
996
Simon Glass232eefd2025-04-29 07:22:14 -0600997 patch2 = patchwork.Patch('2')
Simon Glass3db916d2020-10-29 21:46:35 -0600998 patch2.parse_subject('[2/2] Subject 2')
999 patch2.name = patch2.raw_subject
1000 patch2.content = 'Some other patch content'
1001 comment2a = {
1002 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1003 (self.mary, self.leb)}
1004 comment2b = {'content': 'Reviewed-by: %s' % self.fred}
1005 patch2.comments = [comment2a, comment2b]
1006
1007 # This test works by setting up commits and patch for use by the fake
1008 # Rest API function _fake_patchwork2(). It calls various functions in
1009 # the status module after setting up tags in the commits, checking that
1010 # things behaves as expected
1011 self.commits = [commit1, commit2]
1012 self.patches = [patch1, patch2]
1013 count = 2
Simon Glass3db916d2020-10-29 21:46:35 -06001014
1015 # Check that the tags are picked up on the first patch
Simon Glass29771962025-04-29 07:22:20 -06001016 new_rtags, _ = status.process_reviews(patch1.content, patch1.comments,
1017 commit1.rtags)
1018 self.assertEqual(new_rtags, {'Reviewed-by': {self.joe}})
Simon Glass3db916d2020-10-29 21:46:35 -06001019
1020 # Now the second patch
Simon Glass29771962025-04-29 07:22:20 -06001021 new_rtags, _ = status.process_reviews(patch2.content, patch2.comments,
1022 commit2.rtags)
1023 self.assertEqual(new_rtags, {
Simon Glass3db916d2020-10-29 21:46:35 -06001024 'Reviewed-by': {self.mary, self.fred},
1025 'Tested-by': {self.leb}})
1026
1027 # Now add some tags to the commit, which means they should not appear as
1028 # 'new' tags when scanning comments
Simon Glass3db916d2020-10-29 21:46:35 -06001029 commit1.rtags = {'Reviewed-by': {self.joe}}
Simon Glass29771962025-04-29 07:22:20 -06001030 new_rtags, _ = status.process_reviews(patch1.content, patch1.comments,
1031 commit1.rtags)
1032 self.assertEqual(new_rtags, {})
Simon Glass3db916d2020-10-29 21:46:35 -06001033
1034 # For the second commit, add Ed and Fred, so only Mary should be left
1035 commit2.rtags = {
1036 'Tested-by': {self.leb},
1037 'Reviewed-by': {self.fred}}
Simon Glass29771962025-04-29 07:22:20 -06001038 new_rtags, _ = status.process_reviews(patch2.content, patch2.comments,
1039 commit2.rtags)
1040 self.assertEqual(new_rtags, {'Reviewed-by': {self.mary}})
Simon Glass3db916d2020-10-29 21:46:35 -06001041
1042 # Check that the output patches expectations:
1043 # 1 Subject 1
1044 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1045 # 2 Subject 2
1046 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1047 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1048 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1049 # 1 new response available in patchwork
1050
1051 series = Series()
1052 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001053 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001054 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2)
Simon Glassc100b262025-04-29 07:22:16 -06001055 status.check_and_show_status(series, '1234', None, None, False, False,
Simon Glass25b91c12025-04-29 07:22:19 -06001056 pwork)
Simon Glassb3080ec2025-05-08 04:58:49 +02001057 itr = iter(terminal.get_print_test_lines())
Simon Glass3db916d2020-10-29 21:46:35 -06001058 col = terminal.Color()
Simon Glassd4d3fb42025-04-29 07:22:21 -06001059 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001060 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001061 self.assertEqual(
1062 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
1063 bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001064 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001065 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001066 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001067
Simon Glassd4d3fb42025-04-29 07:22:21 -06001068 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001069 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001070 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001071 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001072 bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001073 next(itr))
1074 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE,
1075 bright=False), next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001076 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -06001077 terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -06001078 bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001079 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001080 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001081 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001082 self.assertEqual(
1083 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001084 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001085 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001086 next(itr))
Simon Glass3db916d2020-10-29 21:46:35 -06001087 self.assertEqual(terminal.PrintLine(
Simon Glassd0a0a582020-10-29 21:46:36 -06001088 '1 new response available in patchwork (use -d to write them to a new branch)',
Simon Glassb3080ec2025-05-08 04:58:49 +02001089 None), next(itr))
Simon Glassd0a0a582020-10-29 21:46:36 -06001090
Simon Glass25b91c12025-04-29 07:22:19 -06001091 def _fake_patchwork3(self, subpath):
Simon Glassd0a0a582020-10-29 21:46:36 -06001092 """Fake Patchwork server for the function below
1093
1094 This handles accessing series, patches and comments, providing the data
1095 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -07001096
1097 Args:
Simon Glassf9b03cf2020-11-03 13:54:14 -07001098 subpath (str): URL subpath to use
Simon Glassd0a0a582020-10-29 21:46:36 -06001099 """
1100 re_series = re.match(r'series/(\d*)/$', subpath)
1101 re_patch = re.match(r'patches/(\d*)/$', subpath)
1102 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
1103 if re_series:
1104 series_num = re_series.group(1)
1105 if series_num == '1234':
1106 return {'patches': self.patches}
1107 elif re_patch:
1108 patch_num = int(re_patch.group(1))
1109 patch = self.patches[patch_num - 1]
1110 return patch
1111 elif re_comments:
1112 patch_num = int(re_comments.group(1))
1113 patch = self.patches[patch_num - 1]
1114 return patch.comments
1115 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
1116
Simon Glassd85bb8f2022-01-29 14:14:09 -07001117 def test_create_branch(self):
Simon Glassd0a0a582020-10-29 21:46:36 -06001118 """Test operation of create_branch()"""
1119 repo = self.make_git_tree()
1120 branch = 'first'
1121 dest_branch = 'first2'
1122 count = 2
Simon Glass41dfb6e2025-05-08 05:13:35 +02001123 gitdir = self.gitdir
Simon Glassd0a0a582020-10-29 21:46:36 -06001124
1125 # Set up the test git tree. We use branch 'first' which has two commits
1126 # in it
1127 series = patchstream.get_metadata_for_list(branch, gitdir, count)
1128 self.assertEqual(2, len(series.commits))
1129
Simon Glass232eefd2025-04-29 07:22:14 -06001130 patch1 = patchwork.Patch('1')
Simon Glassd0a0a582020-10-29 21:46:36 -06001131 patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
1132 patch1.name = patch1.raw_subject
1133 patch1.content = 'This is my patch content'
1134 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1135
1136 patch1.comments = [comment1a]
1137
Simon Glass232eefd2025-04-29 07:22:14 -06001138 patch2 = patchwork.Patch('2')
Simon Glassd0a0a582020-10-29 21:46:36 -06001139 patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
1140 patch2.name = patch2.raw_subject
1141 patch2.content = 'Some other patch content'
1142 comment2a = {
1143 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1144 (self.mary, self.leb)}
1145 comment2b = {
1146 'content': 'Reviewed-by: %s' % self.fred}
1147 patch2.comments = [comment2a, comment2b]
1148
1149 # This test works by setting up patches for use by the fake Rest API
1150 # function _fake_patchwork3(). The fake patch comments above should
1151 # result in new review tags that are collected and added to the commits
1152 # created in the destination branch.
1153 self.patches = [patch1, patch2]
1154 count = 2
1155
1156 # Expected output:
1157 # 1 i2c: I2C things
1158 # + Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1159 # 2 spi: SPI fixes
1160 # + Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1161 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1162 # + Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1163 # 4 new responses available in patchwork
1164 # 4 responses added from patchwork into new branch 'first2'
1165 # <unittest.result.TestResult run=8 errors=0 failures=0>
1166
Simon Glass02811582022-01-29 14:14:18 -07001167 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001168 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork3)
Simon Glassc100b262025-04-29 07:22:16 -06001169 status.check_and_show_status(series, '1234', branch, dest_branch,
Simon Glass25b91c12025-04-29 07:22:19 -06001170 False, False, pwork, repo)
Simon Glass02811582022-01-29 14:14:18 -07001171 lines = terminal.get_print_test_lines()
Simon Glassd0a0a582020-10-29 21:46:36 -06001172 self.assertEqual(12, len(lines))
1173 self.assertEqual(
1174 "4 responses added from patchwork into new branch 'first2'",
1175 lines[11].text)
1176
1177 # Check that the destination branch has the new tags
1178 new_series = patchstream.get_metadata_for_list(dest_branch, gitdir,
1179 count)
1180 self.assertEqual(
1181 {'Reviewed-by': {self.joe}},
1182 new_series.commits[0].rtags)
1183 self.assertEqual(
1184 {'Tested-by': {self.leb},
1185 'Reviewed-by': {self.fred, self.mary}},
1186 new_series.commits[1].rtags)
1187
1188 # Now check the actual test of the first commit message. We expect to
1189 # see the new tags immediately below the old ones.
1190 stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir)
Simon Glassb3080ec2025-05-08 04:58:49 +02001191 itr = iter([line.strip() for line in stdout.splitlines()
1192 if '-by:' in line])
Simon Glassd0a0a582020-10-29 21:46:36 -06001193
1194 # First patch should have the review tag
Simon Glassb3080ec2025-05-08 04:58:49 +02001195 self.assertEqual('Reviewed-by: %s' % self.joe, next(itr))
Simon Glassd0a0a582020-10-29 21:46:36 -06001196
1197 # Second patch should have the sign-off then the tested-by and two
1198 # reviewed-by tags
Simon Glassb3080ec2025-05-08 04:58:49 +02001199 self.assertEqual('Signed-off-by: %s' % self.leb, next(itr))
1200 self.assertEqual('Reviewed-by: %s' % self.fred, next(itr))
1201 self.assertEqual('Reviewed-by: %s' % self.mary, next(itr))
1202 self.assertEqual('Tested-by: %s' % self.leb, next(itr))
Simon Glassda8a2922020-10-29 21:46:37 -06001203
Simon Glassd85bb8f2022-01-29 14:14:09 -07001204 def test_parse_snippets(self):
Simon Glassda8a2922020-10-29 21:46:37 -06001205 """Test parsing of review snippets"""
1206 text = '''Hi Fred,
1207
1208This is a comment from someone.
1209
1210Something else
1211
1212On some recent date, Fred wrote:
1213> This is why I wrote the patch
1214> so here it is
1215
1216Now a comment about the commit message
1217A little more to say
1218
1219Even more
1220
1221> diff --git a/file.c b/file.c
1222> Some more code
1223> Code line 2
1224> Code line 3
1225> Code line 4
1226> Code line 5
1227> Code line 6
1228> Code line 7
1229> Code line 8
1230> Code line 9
1231
1232And another comment
1233
Simon Glassd85bb8f2022-01-29 14:14:09 -07001234> @@ -153,8 +143,13 @@ def check_patch(fname, show_types=False):
Simon Glassda8a2922020-10-29 21:46:37 -06001235> further down on the file
1236> and more code
1237> +Addition here
1238> +Another addition here
1239> codey
1240> more codey
1241
1242and another thing in same file
1243
1244> @@ -253,8 +243,13 @@
1245> with no function context
1246
1247one more thing
1248
1249> diff --git a/tools/patman/main.py b/tools/patman/main.py
1250> +line of code
1251now a very long comment in a different file
1252line2
1253line3
1254line4
1255line5
1256line6
1257line7
1258line8
1259'''
1260 pstrm = PatchStream.process_text(text, True)
1261 self.assertEqual([], pstrm.commit.warn)
1262
1263 # We expect to the filename and up to 5 lines of code context before
1264 # each comment. The 'On xxx wrote:' bit should be removed.
1265 self.assertEqual(
1266 [['Hi Fred,',
1267 'This is a comment from someone.',
1268 'Something else'],
1269 ['> This is why I wrote the patch',
1270 '> so here it is',
1271 'Now a comment about the commit message',
1272 'A little more to say', 'Even more'],
1273 ['> File: file.c', '> Code line 5', '> Code line 6',
1274 '> Code line 7', '> Code line 8', '> Code line 9',
1275 'And another comment'],
1276 ['> File: file.c',
Simon Glassd85bb8f2022-01-29 14:14:09 -07001277 '> Line: 153 / 143: def check_patch(fname, show_types=False):',
Simon Glassda8a2922020-10-29 21:46:37 -06001278 '> and more code', '> +Addition here', '> +Another addition here',
1279 '> codey', '> more codey', 'and another thing in same file'],
1280 ['> File: file.c', '> Line: 253 / 243',
1281 '> with no function context', 'one more thing'],
1282 ['> File: tools/patman/main.py', '> +line of code',
1283 'now a very long comment in a different file',
1284 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']],
1285 pstrm.snippets)
Simon Glass2112d072020-10-29 21:46:38 -06001286
Simon Glassd85bb8f2022-01-29 14:14:09 -07001287 def test_review_snippets(self):
Simon Glass2112d072020-10-29 21:46:38 -06001288 """Test showing of review snippets"""
1289 def _to_submitter(who):
1290 m_who = re.match('(.*) <(.*)>', who)
1291 return {
1292 'name': m_who.group(1),
1293 'email': m_who.group(2)
1294 }
1295
1296 commit1 = Commit('abcd')
1297 commit1.subject = 'Subject 1'
1298 commit2 = Commit('ef12')
1299 commit2.subject = 'Subject 2'
1300
Simon Glass232eefd2025-04-29 07:22:14 -06001301 patch1 = patchwork.Patch('1')
Simon Glass2112d072020-10-29 21:46:38 -06001302 patch1.parse_subject('[1/2] Subject 1')
1303 patch1.name = patch1.raw_subject
1304 patch1.content = 'This is my patch content'
1305 comment1a = {'submitter': _to_submitter(self.joe),
1306 'content': '''Hi Fred,
1307
1308On some date Fred wrote:
1309
1310> diff --git a/file.c b/file.c
1311> Some code
1312> and more code
1313
1314Here is my comment above the above...
1315
1316
1317Reviewed-by: %s
1318''' % self.joe}
1319
1320 patch1.comments = [comment1a]
1321
Simon Glass232eefd2025-04-29 07:22:14 -06001322 patch2 = patchwork.Patch('2')
Simon Glass2112d072020-10-29 21:46:38 -06001323 patch2.parse_subject('[2/2] Subject 2')
1324 patch2.name = patch2.raw_subject
1325 patch2.content = 'Some other patch content'
1326 comment2a = {
1327 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1328 (self.mary, self.leb)}
1329 comment2b = {'submitter': _to_submitter(self.fred),
1330 'content': '''Hi Fred,
1331
1332On some date Fred wrote:
1333
1334> diff --git a/tools/patman/commit.py b/tools/patman/commit.py
1335> @@ -41,6 +41,9 @@ class Commit:
1336> self.rtags = collections.defaultdict(set)
1337> self.warn = []
1338>
1339> + def __str__(self):
1340> + return self.subject
1341> +
Simon Glassd85bb8f2022-01-29 14:14:09 -07001342> def add_change(self, version, info):
Simon Glass2112d072020-10-29 21:46:38 -06001343> """Add a new change line to the change list for a version.
1344>
1345A comment
1346
1347Reviewed-by: %s
1348''' % self.fred}
1349 patch2.comments = [comment2a, comment2b]
1350
1351 # This test works by setting up commits and patch for use by the fake
1352 # Rest API function _fake_patchwork2(). It calls various functions in
1353 # the status module after setting up tags in the commits, checking that
1354 # things behaves as expected
1355 self.commits = [commit1, commit2]
1356 self.patches = [patch1, patch2]
1357
1358 # Check that the output patches expectations:
1359 # 1 Subject 1
1360 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1361 # 2 Subject 2
1362 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1363 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1364 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1365 # 1 new response available in patchwork
1366
1367 series = Series()
1368 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001369 terminal.set_print_test_mode()
Simon Glass25b91c12025-04-29 07:22:19 -06001370 pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2)
Simon Glassc100b262025-04-29 07:22:16 -06001371 status.check_and_show_status(series, '1234', None, None, False, True,
Simon Glass25b91c12025-04-29 07:22:19 -06001372 pwork)
Simon Glassb3080ec2025-05-08 04:58:49 +02001373 itr = iter(terminal.get_print_test_lines())
Simon Glass2112d072020-10-29 21:46:38 -06001374 col = terminal.Color()
Simon Glassd4d3fb42025-04-29 07:22:21 -06001375 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001376 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001377 self.assertEqual(
1378 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001379 next(itr))
1380 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001381
1382 self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED),
Simon Glassb3080ec2025-05-08 04:58:49 +02001383 next(itr))
1384 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(itr))
1385 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001386 self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA),
Simon Glassb3080ec2025-05-08 04:58:49 +02001387 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001388 self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA),
Simon Glassb3080ec2025-05-08 04:58:49 +02001389 next(itr))
1390 self.assertEqual(terminal.PrintLine(' > and more code',
1391 col.MAGENTA),
1392 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001393 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001394 ' Here is my comment above the above...', None), next(itr))
1395 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001396
Simon Glassd4d3fb42025-04-29 07:22:21 -06001397 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.YELLOW),
Simon Glassb3080ec2025-05-08 04:58:49 +02001398 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001399 self.assertEqual(
1400 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001401 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001402 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001403 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001404 self.assertEqual(
1405 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001406 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001407 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001408 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001409 self.assertEqual(
1410 terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False),
Simon Glassb3080ec2025-05-08 04:58:49 +02001411 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001412 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE),
Simon Glassb3080ec2025-05-08 04:58:49 +02001413 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001414
1415 self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED),
Simon Glassb3080ec2025-05-08 04:58:49 +02001416 next(itr))
1417 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(itr))
1418 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001419 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001420 ' > File: tools/patman/commit.py', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001421 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001422 ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001423 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001424 ' > + return self.subject', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001425 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001426 ' > +', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001427 self.assertEqual(
Simon Glassb3080ec2025-05-08 04:58:49 +02001428 terminal.PrintLine(
1429 ' > def add_change(self, version, info):',
1430 col.MAGENTA),
1431 next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001432 self.assertEqual(terminal.PrintLine(
1433 ' > """Add a new change line to the change list for a version.',
Simon Glassb3080ec2025-05-08 04:58:49 +02001434 col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001435 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001436 ' >', col.MAGENTA), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001437 self.assertEqual(terminal.PrintLine(
Simon Glassb3080ec2025-05-08 04:58:49 +02001438 ' A comment', None), next(itr))
1439 self.assertEqual(terminal.PrintLine('', None), next(itr))
Simon Glass2112d072020-10-29 21:46:38 -06001440
1441 self.assertEqual(terminal.PrintLine(
1442 '4 new responses available in patchwork (use -d to write them to a new branch)',
Simon Glassb3080ec2025-05-08 04:58:49 +02001443 None), next(itr))
Simon Glass6a222e62021-08-01 16:02:39 -06001444
Simon Glassd85bb8f2022-01-29 14:14:09 -07001445 def test_insert_tags(self):
Simon Glass6a222e62021-08-01 16:02:39 -06001446 """Test inserting of review tags"""
1447 msg = '''first line
1448second line.'''
1449 tags = [
1450 'Reviewed-by: Bin Meng <bmeng.cn@gmail.com>',
1451 'Tested-by: Bin Meng <bmeng.cn@gmail.com>'
1452 ]
1453 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.com>'
1454 tag_str = '\n'.join(tags)
1455
1456 new_msg = patchstream.insert_tags(msg, tags)
1457 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1458
1459 new_msg = patchstream.insert_tags(msg + '\n', tags)
1460 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1461
1462 msg += '\n\n' + signoff
1463 new_msg = patchstream.insert_tags(msg, tags)
1464 self.assertEqual(msg + '\n' + tag_str, new_msg)