blob: 7b92bc67be1eff37134d58fe72fccfd8c992d8dd [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 Glassdf1bc5c2017-05-29 15:31:31 -06009import os
10import re
11import shutil
12import sys
13import tempfile
14import unittest
15
Simon Glass3db916d2020-10-29 21:46:35 -060016
17from patman.commit import Commit
Simon Glass54f1c5b2020-07-05 21:41:50 -060018from patman import control
Simon Glassa997ea52020-04-17 18:09:04 -060019from patman import gitutil
20from patman import patchstream
Simon Glassa7fadab2020-10-29 21:46:26 -060021from patman.patchstream import PatchStream
Simon Glass3db916d2020-10-29 21:46:35 -060022from patman.series import Series
Simon Glassa997ea52020-04-17 18:09:04 -060023from patman import settings
Simon Glass54f1c5b2020-07-05 21:41:50 -060024from patman import terminal
Simon Glassa997ea52020-04-17 18:09:04 -060025from patman import tools
Simon Glass54f1c5b2020-07-05 21:41:50 -060026from patman.test_util import capture_sys_output
Simon Glassdf1bc5c2017-05-29 15:31:31 -060027
Tom Rini488ea972021-02-26 07:52:31 -050028import pygit2
29from patman import status
Simon Glassdf1bc5c2017-05-29 15:31:31 -060030
31class TestFunctional(unittest.TestCase):
Simon Glasseb209e52020-10-29 21:46:15 -060032 """Functional tests for checking that patman behaves correctly"""
Simon Glass06202d62020-10-29 21:46:27 -060033 leb = (b'Lord Edmund Blackadd\xc3\xabr <weasel@blackadder.org>'.
34 decode('utf-8'))
Simon Glass3b762cc2020-10-29 21:46:28 -060035 fred = 'Fred Bloggs <f.bloggs@napier.net>'
36 joe = 'Joe Bloggs <joe@napierwallies.co.nz>'
37 mary = 'Mary Bloggs <mary@napierwallies.co.nz>'
Simon Glass3db916d2020-10-29 21:46:35 -060038 commits = None
39 patches = None
Simon Glass06202d62020-10-29 21:46:27 -060040
Simon Glassdf1bc5c2017-05-29 15:31:31 -060041 def setUp(self):
42 self.tmpdir = tempfile.mkdtemp(prefix='patman.')
Simon Glass54f1c5b2020-07-05 21:41:50 -060043 self.gitdir = os.path.join(self.tmpdir, 'git')
44 self.repo = None
Simon Glassdf1bc5c2017-05-29 15:31:31 -060045
46 def tearDown(self):
47 shutil.rmtree(self.tmpdir)
Simon Glass02811582022-01-29 14:14:18 -070048 terminal.set_print_test_mode(False)
Simon Glassdf1bc5c2017-05-29 15:31:31 -060049
50 @staticmethod
Simon Glasseb209e52020-10-29 21:46:15 -060051 def _get_path(fname):
52 """Get the path to a test file
53
54 Args:
55 fname (str): Filename to obtain
56
57 Returns:
58 str: Full path to file in the test directory
59 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -060060 return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
61 'test', fname)
62
63 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -060064 def _get_text(cls, fname):
65 """Read a file as text
66
67 Args:
68 fname (str): Filename to read
69
70 Returns:
71 str: Contents of file
72 """
73 return open(cls._get_path(fname), encoding='utf-8').read()
Simon Glassdf1bc5c2017-05-29 15:31:31 -060074
75 @classmethod
Simon Glasseb209e52020-10-29 21:46:15 -060076 def _get_patch_name(cls, subject):
77 """Get the filename of a patch given its subject
78
79 Args:
80 subject (str): Patch subject
81
82 Returns:
83 str: Filename for that patch
84 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -060085 fname = re.sub('[ :]', '-', subject)
86 return fname.replace('--', '-')
87
Simon Glasseb209e52020-10-29 21:46:15 -060088 def _create_patches_for_test(self, series):
89 """Create patch files for use by tests
90
91 This copies patch files from the test directory as needed by the series
92
93 Args:
94 series (Series): Series containing commits to convert
95
96 Returns:
97 tuple:
98 str: Cover-letter filename, or None if none
99 fname_list: list of str, each a patch filename
100 """
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600101 cover_fname = None
102 fname_list = []
103 for i, commit in enumerate(series.commits):
Simon Glasseb209e52020-10-29 21:46:15 -0600104 clean_subject = self._get_patch_name(commit.subject)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600105 src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
106 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600107 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600108 fname_list.append(fname)
109 if series.get('cover'):
110 src_fname = '0000-cover-letter.patch'
111 cover_fname = os.path.join(self.tmpdir, src_fname)
112 fname = os.path.join(self.tmpdir, src_fname)
Simon Glasseb209e52020-10-29 21:46:15 -0600113 shutil.copy(self._get_path(src_fname), fname)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600114
115 return cover_fname, fname_list
116
Simon Glassd85bb8f2022-01-29 14:14:09 -0700117 def test_basic(self):
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600118 """Tests the basic flow of patman
119
120 This creates a series from some hard-coded patches build from a simple
121 tree with the following metadata in the top commit:
122
123 Series-to: u-boot
124 Series-prefix: RFC
Sean Andersondc1cd132021-10-22 19:07:04 -0400125 Series-postfix: some-branch
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600126 Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
127 Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
Sean Andersoncf13b862020-05-04 16:28:36 -0400128 Series-version: 3
129 Patch-cc: fred
130 Series-process-log: sort, uniq
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600131 Series-changes: 4
132 - Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400133 - Multi
134 line
135 change
136
137 Commit-changes: 2
138 - Changes only for this commit
139
Simon Glass6a222e62021-08-01 16:02:39 -0600140' Cover-changes: 4
Sean Andersoncf13b862020-05-04 16:28:36 -0400141 - Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600142
143 Cover-letter:
144 test: A test patch series
145 This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400146 letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600147 works
148 END
149
150 and this in the first commit:
151
Sean Andersoncf13b862020-05-04 16:28:36 -0400152 Commit-changes: 2
153 - second revision change
154
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600155 Series-notes:
156 some notes
157 about some things
158 from the first commit
159 END
160
161 Commit-notes:
162 Some notes about
163 the first commit
164 END
165
166 with the following commands:
167
168 git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
169 git format-patch --subject-prefix RFC --cover-letter HEAD~2
170 mv 00* /path/to/tools/patman/test
171
172 It checks these aspects:
173 - git log can be processed by patchstream
174 - emailing patches uses the correct command
175 - CC file has information on each commit
176 - cover letter has the expected text and subject
177 - each patch has the correct subject
178 - dry-run information prints out correctly
179 - unicode is handled correctly
Sean Andersondc1cd132021-10-22 19:07:04 -0400180 - Series-to, Series-cc, Series-prefix, Series-postfix, Cover-letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600181 - Cover-letter-cc, Series-version, Series-changes, Series-notes
182 - Commit-notes
183 """
184 process_tags = True
Simon Glass1f975b92021-01-23 08:56:15 -0700185 ignore_bad_tags = False
Simon Glass4f817892019-05-14 15:53:53 -0600186 stefan = b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600187 rick = 'Richard III <richard@palace.gov>'
Simon Glass4f817892019-05-14 15:53:53 -0600188 mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600189 add_maintainers = [stefan, rick]
190 dry_run = True
191 in_reply_to = mel
192 count = 2
193 settings.alias = {
Simon Glass95745aa2020-10-29 21:46:13 -0600194 'fdt': ['simon'],
195 'u-boot': ['u-boot@lists.denx.de'],
Simon Glass06202d62020-10-29 21:46:27 -0600196 'simon': [self.leb],
Simon Glass3b762cc2020-10-29 21:46:28 -0600197 'fred': [self.fred],
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600198 }
199
Simon Glasseb209e52020-10-29 21:46:15 -0600200 text = self._get_text('test01.txt')
Simon Glass93f61c02020-10-29 21:46:19 -0600201 series = patchstream.get_metadata_for_test(text)
Simon Glasseb209e52020-10-29 21:46:15 -0600202 cover_fname, args = self._create_patches_for_test(series)
Simon Glass59a70bb2020-10-29 21:46:14 -0600203 with capture_sys_output() as out:
Simon Glass93f61c02020-10-29 21:46:19 -0600204 patchstream.fix_patches(series, args)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600205 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -0600206 patchstream.insert_cover_letter(cover_fname, series, count)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600207 series.DoChecks()
208 cc_file = series.MakeCcFile(process_tags, cover_fname,
Chris Packhamb84fb482018-06-07 20:45:06 +1200209 not ignore_bad_tags, add_maintainers,
210 None)
Simon Glass761648b2022-01-29 14:14:11 -0700211 cmd = gitutil.email_patches(
Simon Glass95745aa2020-10-29 21:46:13 -0600212 series, cover_fname, args, dry_run, not ignore_bad_tags,
213 cc_file, in_reply_to=in_reply_to, thread=None)
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600214 series.ShowActions(args, cmd, process_tags)
Simon Glassf544a2d2019-10-31 07:42:51 -0600215 cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600216 os.remove(cc_file)
217
Simon Glass42e3d392020-10-29 21:46:29 -0600218 lines = iter(out[0].getvalue().splitlines())
219 self.assertEqual('Cleaned %s patches' % len(series.commits),
220 next(lines))
221 self.assertEqual('Change log missing for v2', next(lines))
222 self.assertEqual('Change log missing for v3', next(lines))
223 self.assertEqual('Change log for unknown version v4', next(lines))
224 self.assertEqual("Alias 'pci' not found", next(lines))
225 self.assertIn('Dry run', next(lines))
226 self.assertEqual('', next(lines))
227 self.assertIn('Send a total of %d patches' % count, next(lines))
228 prev = next(lines)
229 for i, commit in enumerate(series.commits):
230 self.assertEqual(' %s' % args[i], prev)
231 while True:
232 prev = next(lines)
233 if 'Cc:' not in prev:
234 break
235 self.assertEqual('To: u-boot@lists.denx.de', prev)
Simon Glass9dfb3112020-11-08 20:36:18 -0700236 self.assertEqual('Cc: %s' % stefan, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600237 self.assertEqual('Version: 3', next(lines))
238 self.assertEqual('Prefix:\t RFC', next(lines))
Sean Andersondc1cd132021-10-22 19:07:04 -0400239 self.assertEqual('Postfix:\t some-branch', next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600240 self.assertEqual('Cover: 4 lines', next(lines))
241 self.assertEqual(' Cc: %s' % self.fred, next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700242 self.assertEqual(' Cc: %s' % self.leb,
Simon Glass42e3d392020-10-29 21:46:29 -0600243 next(lines))
Simon Glass9dfb3112020-11-08 20:36:18 -0700244 self.assertEqual(' Cc: %s' % mel, next(lines))
Simon Glass42e3d392020-10-29 21:46:29 -0600245 self.assertEqual(' Cc: %s' % rick, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600246 expected = ('Git command: git send-email --annotate '
247 '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
Simon Glass1ee91c12020-11-03 13:54:10 -0700248 '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600249 % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
Simon Glass4f817892019-05-14 15:53:53 -0600250 ' '.join(args)))
Simon Glass9dfb3112020-11-08 20:36:18 -0700251 self.assertEqual(expected, next(lines))
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600252
Simon Glass9dfb3112020-11-08 20:36:18 -0700253 self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0])
Simon Glass95745aa2020-10-29 21:46:13 -0600254 self.assertEqual(
Simon Glass3b762cc2020-10-29 21:46:28 -0600255 '%s %s\0%s\0%s\0%s' % (args[1], self.fred, self.leb, rick, stefan),
Simon Glass9dfb3112020-11-08 20:36:18 -0700256 cc_lines[1])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600257
258 expected = '''
259This is a test of how the cover
Sean Andersoncf13b862020-05-04 16:28:36 -0400260letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600261works
262
263some notes
264about some things
265from the first commit
266
267Changes in v4:
Sean Andersoncf13b862020-05-04 16:28:36 -0400268- Multi
269 line
270 change
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600271- Some changes
Sean Andersoncf13b862020-05-04 16:28:36 -0400272- Some notes for the cover letter
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600273
274Simon Glass (2):
275 pci: Correct cast for sandbox
Siva Durga Prasad Paladugub3d55ea2018-07-16 15:56:11 +0530276 fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600277
278 cmd/pci.c | 3 ++-
279 fs/fat/fat.c | 1 +
280 lib/efi_loader/efi_memory.c | 1 +
281 lib/fdtdec.c | 3 ++-
282 4 files changed, 6 insertions(+), 2 deletions(-)
283
284--\x20
2852.7.4
286
287'''
Simon Glassf544a2d2019-10-31 07:42:51 -0600288 lines = open(cover_fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600289 self.assertEqual(
Sean Andersondc1cd132021-10-22 19:07:04 -0400290 'Subject: [RFC PATCH some-branch v3 0/2] test: A test patch series',
Simon Glass95745aa2020-10-29 21:46:13 -0600291 lines[3])
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600292 self.assertEqual(expected.splitlines(), lines[7:])
293
294 for i, fname in enumerate(args):
Simon Glassf544a2d2019-10-31 07:42:51 -0600295 lines = open(fname, encoding='utf-8').read().splitlines()
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600296 subject = [line for line in lines if line.startswith('Subject')]
297 self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
298 subject[0][:18])
Sean Andersoncf13b862020-05-04 16:28:36 -0400299
300 # Check that we got our commit notes
301 start = 0
302 expected = ''
303
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600304 if i == 0:
Sean Andersoncf13b862020-05-04 16:28:36 -0400305 start = 17
306 expected = '''---
307Some notes about
308the first commit
309
310(no changes since v2)
311
312Changes in v2:
313- second revision change'''
314 elif i == 1:
315 start = 17
316 expected = '''---
317
318Changes in v4:
319- Multi
320 line
321 change
322- Some changes
323
324Changes in v2:
325- Changes only for this commit'''
326
327 if expected:
328 expected = expected.splitlines()
329 self.assertEqual(expected, lines[start:(start+len(expected))])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600330
331 def make_commit_with_file(self, subject, body, fname, text):
332 """Create a file and add it to the git repo with a new commit
333
334 Args:
335 subject (str): Subject for the commit
336 body (str): Body text of the commit
337 fname (str): Filename of file to create
338 text (str): Text to put into the file
339 """
340 path = os.path.join(self.gitdir, fname)
Simon Glass80025522022-01-29 14:14:04 -0700341 tools.write_file(path, text, binary=False)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600342 index = self.repo.index
343 index.add(fname)
Simon Glass547cba62022-02-11 13:23:18 -0700344 # pylint doesn't seem to find this
345 # pylint: disable=E1101
Simon Glass95745aa2020-10-29 21:46:13 -0600346 author = pygit2.Signature('Test user', 'test@email.com')
Simon Glass54f1c5b2020-07-05 21:41:50 -0600347 committer = author
348 tree = index.write_tree()
349 message = subject + '\n' + body
350 self.repo.create_commit('HEAD', author, committer, message, tree,
351 [self.repo.head.target])
352
353 def make_git_tree(self):
354 """Make a simple git tree suitable for testing
355
356 It has three branches:
357 'base' has two commits: PCI, main
358 'first' has base as upstream and two more commits: I2C, SPI
359 'second' has base as upstream and three more: video, serial, bootm
360
361 Returns:
Simon Glasseb209e52020-10-29 21:46:15 -0600362 pygit2.Repository: repository
Simon Glass54f1c5b2020-07-05 21:41:50 -0600363 """
364 repo = pygit2.init_repository(self.gitdir)
365 self.repo = repo
366 new_tree = repo.TreeBuilder().write()
367
Simon Glass547cba62022-02-11 13:23:18 -0700368 # pylint doesn't seem to find this
369 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600370 author = pygit2.Signature('Test user', 'test@email.com')
371 committer = author
Simon Glasseb209e52020-10-29 21:46:15 -0600372 _ = repo.create_commit('HEAD', author, committer, 'Created master',
373 new_tree, [])
Simon Glass54f1c5b2020-07-05 21:41:50 -0600374
375 self.make_commit_with_file('Initial commit', '''
376Add a README
377
378''', 'README', '''This is the README file
379describing this project
380in very little detail''')
381
382 self.make_commit_with_file('pci: PCI implementation', '''
383Here is a basic PCI implementation
384
385''', 'pci.c', '''This is a file
386it has some contents
387and some more things''')
388 self.make_commit_with_file('main: Main program', '''
389Hello here is the second commit.
390''', 'main.c', '''This is the main file
391there is very little here
392but we can always add more later
393if we want to
394
395Series-to: u-boot
396Series-cc: Barry Crump <bcrump@whataroa.nz>
397''')
398 base_target = repo.revparse_single('HEAD')
399 self.make_commit_with_file('i2c: I2C things', '''
400This has some stuff to do with I2C
401''', 'i2c.c', '''And this is the file contents
402with some I2C-related things in it''')
403 self.make_commit_with_file('spi: SPI fixes', '''
404SPI needs some fixes
405and here they are
Simon Glassd0a0a582020-10-29 21:46:36 -0600406
407Signed-off-by: %s
408
409Series-to: u-boot
410Commit-notes:
411title of the series
412This is the cover letter for the series
413with various details
414END
415''' % self.leb, 'spi.c', '''Some fixes for SPI in this
Simon Glass54f1c5b2020-07-05 21:41:50 -0600416file to make SPI work
417better than before''')
418 first_target = repo.revparse_single('HEAD')
419
420 target = repo.revparse_single('HEAD~2')
Simon Glass547cba62022-02-11 13:23:18 -0700421 # pylint doesn't seem to find this
422 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600423 repo.reset(target.oid, pygit2.GIT_CHECKOUT_FORCE)
424 self.make_commit_with_file('video: Some video improvements', '''
425Fix up the video so that
426it looks more purple. Purple is
427a very nice colour.
428''', 'video.c', '''More purple here
429Purple and purple
430Even more purple
431Could not be any more purple''')
432 self.make_commit_with_file('serial: Add a serial driver', '''
433Here is the serial driver
434for my chip.
435
436Cover-letter:
437Series for my board
438This series implements support
439for my glorious board.
440END
Simon Glassa80986c2020-10-29 21:46:16 -0600441Series-links: 183237
Simon Glass54f1c5b2020-07-05 21:41:50 -0600442''', 'serial.c', '''The code for the
443serial driver is here''')
444 self.make_commit_with_file('bootm: Make it boot', '''
445This makes my board boot
446with a fix to the bootm
447command
448''', 'bootm.c', '''Fix up the bootm
449command to make the code as
450complicated as possible''')
451 second_target = repo.revparse_single('HEAD')
452
453 repo.branches.local.create('first', first_target)
454 repo.config.set_multivar('branch.first.remote', '', '.')
455 repo.config.set_multivar('branch.first.merge', '', 'refs/heads/base')
456
457 repo.branches.local.create('second', second_target)
458 repo.config.set_multivar('branch.second.remote', '', '.')
459 repo.config.set_multivar('branch.second.merge', '', 'refs/heads/base')
460
461 repo.branches.local.create('base', base_target)
462 return repo
463
Simon Glassd85bb8f2022-01-29 14:14:09 -0700464 def test_branch(self):
Simon Glass54f1c5b2020-07-05 21:41:50 -0600465 """Test creating patches from a branch"""
466 repo = self.make_git_tree()
467 target = repo.lookup_reference('refs/heads/first')
Simon Glass547cba62022-02-11 13:23:18 -0700468 # pylint doesn't seem to find this
469 # pylint: disable=E1101
Simon Glass54f1c5b2020-07-05 21:41:50 -0600470 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
471 control.setup()
472 try:
473 orig_dir = os.getcwd()
474 os.chdir(self.gitdir)
475
476 # Check that it can detect the current branch
Simon Glass761648b2022-01-29 14:14:11 -0700477 self.assertEqual(2, gitutil.count_commits_to_branch(None))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600478 col = terminal.Color()
479 with capture_sys_output() as _:
480 _, cover_fname, patch_files = control.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600481 col, branch=None, count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100482 ignore_binary=False, signoff=True)
Simon Glass54f1c5b2020-07-05 21:41:50 -0600483 self.assertIsNone(cover_fname)
484 self.assertEqual(2, len(patch_files))
Simon Glass2eb4da72020-07-05 21:41:51 -0600485
486 # Check that it can detect a different branch
Simon Glass761648b2022-01-29 14:14:11 -0700487 self.assertEqual(3, gitutil.count_commits_to_branch('second'))
Simon Glass2eb4da72020-07-05 21:41:51 -0600488 with capture_sys_output() as _:
489 _, cover_fname, patch_files = control.prepare_patches(
Simon Glassb3bf4e12020-07-05 21:41:52 -0600490 col, branch='second', count=-1, start=0, end=0,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100491 ignore_binary=False, signoff=True)
Simon Glass2eb4da72020-07-05 21:41:51 -0600492 self.assertIsNotNone(cover_fname)
493 self.assertEqual(3, len(patch_files))
Simon Glassb3bf4e12020-07-05 21:41:52 -0600494
495 # Check that it can skip patches at the end
496 with capture_sys_output() as _:
497 _, cover_fname, patch_files = control.prepare_patches(
498 col, branch='second', count=-1, start=0, end=1,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100499 ignore_binary=False, signoff=True)
Simon Glassb3bf4e12020-07-05 21:41:52 -0600500 self.assertIsNotNone(cover_fname)
501 self.assertEqual(2, len(patch_files))
Simon Glass54f1c5b2020-07-05 21:41:50 -0600502 finally:
503 os.chdir(orig_dir)
Simon Glass06202d62020-10-29 21:46:27 -0600504
Simon Glassd85bb8f2022-01-29 14:14:09 -0700505 def test_tags(self):
Simon Glass06202d62020-10-29 21:46:27 -0600506 """Test collection of tags in a patchstream"""
507 text = '''This is a patch
508
509Signed-off-by: Terminator
Simon Glass3b762cc2020-10-29 21:46:28 -0600510Reviewed-by: %s
511Reviewed-by: %s
Simon Glass06202d62020-10-29 21:46:27 -0600512Tested-by: %s
Simon Glass3b762cc2020-10-29 21:46:28 -0600513''' % (self.joe, self.mary, self.leb)
Simon Glass06202d62020-10-29 21:46:27 -0600514 pstrm = PatchStream.process_text(text)
515 self.assertEqual(pstrm.commit.rtags, {
Simon Glass3b762cc2020-10-29 21:46:28 -0600516 'Reviewed-by': {self.joe, self.mary},
Simon Glass06202d62020-10-29 21:46:27 -0600517 'Tested-by': {self.leb}})
Simon Glass3b762cc2020-10-29 21:46:28 -0600518
Simon Glassd85bb8f2022-01-29 14:14:09 -0700519 def test_invalid_tag(self):
Patrick Delaunay6bbdd0c2021-07-22 16:51:42 +0200520 """Test invalid tag in a patchstream"""
521 text = '''This is a patch
522
523Serie-version: 2
524'''
525 with self.assertRaises(ValueError) as exc:
526 pstrm = PatchStream.process_text(text)
527 self.assertEqual("Line 3: Invalid tag = 'Serie-version: 2'",
528 str(exc.exception))
529
Simon Glassd85bb8f2022-01-29 14:14:09 -0700530 def test_missing_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600531 """Test a missing END tag"""
532 text = '''This is a patch
533
534Cover-letter:
535This is the title
536missing END after this line
537Signed-off-by: Fred
538'''
539 pstrm = PatchStream.process_text(text)
540 self.assertEqual(["Missing 'END' in section 'cover'"],
541 pstrm.commit.warn)
542
Simon Glassd85bb8f2022-01-29 14:14:09 -0700543 def test_missing_blank_line(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600544 """Test a missing blank line after a tag"""
545 text = '''This is a patch
546
547Series-changes: 2
548- First line of changes
549- Missing blank line after this line
550Signed-off-by: Fred
551'''
552 pstrm = PatchStream.process_text(text)
553 self.assertEqual(["Missing 'blank line' in section 'Series-changes'"],
554 pstrm.commit.warn)
555
Simon Glassd85bb8f2022-01-29 14:14:09 -0700556 def test_invalid_commit_tag(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600557 """Test an invalid Commit-xxx tag"""
558 text = '''This is a patch
559
560Commit-fred: testing
561'''
562 pstrm = PatchStream.process_text(text)
563 self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn)
564
Simon Glassd85bb8f2022-01-29 14:14:09 -0700565 def test_self_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600566 """Test a tested by tag by this user"""
567 test_line = 'Tested-by: %s@napier.com' % os.getenv('USER')
568 text = '''This is a patch
569
570%s
571''' % test_line
572 pstrm = PatchStream.process_text(text)
573 self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn)
574
Simon Glassd85bb8f2022-01-29 14:14:09 -0700575 def test_space_before_tab(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600576 """Test a space before a tab"""
577 text = '''This is a patch
578
579+ \tSomething
580'''
581 pstrm = PatchStream.process_text(text)
582 self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn)
583
Simon Glassd85bb8f2022-01-29 14:14:09 -0700584 def test_lines_after_test(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600585 """Test detecting lines after TEST= line"""
586 text = '''This is a patch
587
588TEST=sometest
589more lines
590here
591'''
592 pstrm = PatchStream.process_text(text)
593 self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn)
594
Simon Glassd85bb8f2022-01-29 14:14:09 -0700595 def test_blank_line_at_end(self):
Simon Glass3b762cc2020-10-29 21:46:28 -0600596 """Test detecting a blank line at the end of a file"""
597 text = '''This is a patch
598
599diff --git a/lib/fdtdec.c b/lib/fdtdec.c
600index c072e54..942244f 100644
601--- a/lib/fdtdec.c
602+++ b/lib/fdtdec.c
603@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void)
604 }
605
606 gd->ram_size = (phys_size_t)(res.end - res.start + 1);
607- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size);
608+ debug("%s: Initial DRAM size %llx\n", __func__,
609+ (unsigned long long)gd->ram_size);
610+
611diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
612
613--
6142.7.4
615
616 '''
617 pstrm = PatchStream.process_text(text)
618 self.assertEqual(
619 ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"],
620 pstrm.commit.warn)
Simon Glass1c1f2072020-10-29 21:46:34 -0600621
Simon Glassd85bb8f2022-01-29 14:14:09 -0700622 def test_no_upstream(self):
Simon Glass1c1f2072020-10-29 21:46:34 -0600623 """Test CountCommitsToBranch when there is no upstream"""
624 repo = self.make_git_tree()
625 target = repo.lookup_reference('refs/heads/base')
Simon Glass547cba62022-02-11 13:23:18 -0700626 # pylint doesn't seem to find this
627 # pylint: disable=E1101
Simon Glass1c1f2072020-10-29 21:46:34 -0600628 self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE)
629
630 # Check that it can detect the current branch
631 try:
632 orig_dir = os.getcwd()
633 os.chdir(self.gitdir)
634 with self.assertRaises(ValueError) as exc:
Simon Glass761648b2022-01-29 14:14:11 -0700635 gitutil.count_commits_to_branch(None)
Simon Glass1c1f2072020-10-29 21:46:34 -0600636 self.assertIn(
637 "Failed to determine upstream: fatal: no upstream configured for branch 'base'",
638 str(exc.exception))
639 finally:
640 os.chdir(orig_dir)
Simon Glass3db916d2020-10-29 21:46:35 -0600641
642 @staticmethod
Simon Glassf9b03cf2020-11-03 13:54:14 -0700643 def _fake_patchwork(url, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600644 """Fake Patchwork server for the function below
645
646 This handles accessing a series, providing a list consisting of a
647 single patch
Simon Glassf9b03cf2020-11-03 13:54:14 -0700648
649 Args:
650 url (str): URL of patchwork server
651 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600652 """
653 re_series = re.match(r'series/(\d*)/$', subpath)
654 if re_series:
655 series_num = re_series.group(1)
656 if series_num == '1234':
657 return {'patches': [
658 {'id': '1', 'name': 'Some patch'}]}
659 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
660
Simon Glassd85bb8f2022-01-29 14:14:09 -0700661 def test_status_mismatch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600662 """Test Patchwork patches not matching the series"""
663 series = Series()
664
665 with capture_sys_output() as (_, err):
Simon Glassf9b03cf2020-11-03 13:54:14 -0700666 status.collect_patches(series, 1234, None, self._fake_patchwork)
Simon Glass3db916d2020-10-29 21:46:35 -0600667 self.assertIn('Warning: Patchwork reports 1 patches, series has 0',
668 err.getvalue())
669
Simon Glassd85bb8f2022-01-29 14:14:09 -0700670 def test_status_read_patch(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600671 """Test handling a single patch in Patchwork"""
672 series = Series()
673 series.commits = [Commit('abcd')]
674
Simon Glassf9b03cf2020-11-03 13:54:14 -0700675 patches = status.collect_patches(series, 1234, None,
676 self._fake_patchwork)
Simon Glass3db916d2020-10-29 21:46:35 -0600677 self.assertEqual(1, len(patches))
678 patch = patches[0]
679 self.assertEqual('1', patch.id)
680 self.assertEqual('Some patch', patch.raw_subject)
681
Simon Glassd85bb8f2022-01-29 14:14:09 -0700682 def test_parse_subject(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600683 """Test parsing of the patch subject"""
684 patch = status.Patch('1')
685
686 # Simple patch not in a series
687 patch.parse_subject('Testing')
688 self.assertEqual('Testing', patch.raw_subject)
689 self.assertEqual('Testing', patch.subject)
690 self.assertEqual(1, patch.seq)
691 self.assertEqual(1, patch.count)
692 self.assertEqual(None, patch.prefix)
693 self.assertEqual(None, patch.version)
694
695 # First patch in a series
696 patch.parse_subject('[1/2] Testing')
697 self.assertEqual('[1/2] Testing', patch.raw_subject)
698 self.assertEqual('Testing', patch.subject)
699 self.assertEqual(1, patch.seq)
700 self.assertEqual(2, patch.count)
701 self.assertEqual(None, patch.prefix)
702 self.assertEqual(None, patch.version)
703
704 # Second patch in a series
705 patch.parse_subject('[2/2] Testing')
706 self.assertEqual('Testing', patch.subject)
707 self.assertEqual(2, patch.seq)
708 self.assertEqual(2, patch.count)
709 self.assertEqual(None, patch.prefix)
710 self.assertEqual(None, patch.version)
711
712 # RFC patch
713 patch.parse_subject('[RFC,3/7] Testing')
714 self.assertEqual('Testing', patch.subject)
715 self.assertEqual(3, patch.seq)
716 self.assertEqual(7, patch.count)
717 self.assertEqual('RFC', patch.prefix)
718 self.assertEqual(None, patch.version)
719
720 # Version patch
721 patch.parse_subject('[v2,3/7] Testing')
722 self.assertEqual('Testing', patch.subject)
723 self.assertEqual(3, patch.seq)
724 self.assertEqual(7, patch.count)
725 self.assertEqual(None, patch.prefix)
726 self.assertEqual('v2', patch.version)
727
728 # All fields
729 patch.parse_subject('[RESEND,v2,3/7] Testing')
730 self.assertEqual('Testing', patch.subject)
731 self.assertEqual(3, patch.seq)
732 self.assertEqual(7, patch.count)
733 self.assertEqual('RESEND', patch.prefix)
734 self.assertEqual('v2', patch.version)
735
736 # RFC only
737 patch.parse_subject('[RESEND] Testing')
738 self.assertEqual('Testing', patch.subject)
739 self.assertEqual(1, patch.seq)
740 self.assertEqual(1, patch.count)
741 self.assertEqual('RESEND', patch.prefix)
742 self.assertEqual(None, patch.version)
743
Simon Glassd85bb8f2022-01-29 14:14:09 -0700744 def test_compare_series(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600745 """Test operation of compare_with_series()"""
746 commit1 = Commit('abcd')
747 commit1.subject = 'Subject 1'
748 commit2 = Commit('ef12')
749 commit2.subject = 'Subject 2'
750 commit3 = Commit('3456')
751 commit3.subject = 'Subject 2'
752
753 patch1 = status.Patch('1')
754 patch1.subject = 'Subject 1'
755 patch2 = status.Patch('2')
756 patch2.subject = 'Subject 2'
757 patch3 = status.Patch('3')
758 patch3.subject = 'Subject 2'
759
760 series = Series()
761 series.commits = [commit1]
762 patches = [patch1]
763 patch_for_commit, commit_for_patch, warnings = (
764 status.compare_with_series(series, patches))
765 self.assertEqual(1, len(patch_for_commit))
766 self.assertEqual(patch1, patch_for_commit[0])
767 self.assertEqual(1, len(commit_for_patch))
768 self.assertEqual(commit1, commit_for_patch[0])
769
770 series.commits = [commit1]
771 patches = [patch1, patch2]
772 patch_for_commit, commit_for_patch, warnings = (
773 status.compare_with_series(series, patches))
774 self.assertEqual(1, len(patch_for_commit))
775 self.assertEqual(patch1, patch_for_commit[0])
776 self.assertEqual(1, len(commit_for_patch))
777 self.assertEqual(commit1, commit_for_patch[0])
778 self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"],
779 warnings)
780
781 series.commits = [commit1, commit2]
782 patches = [patch1]
783 patch_for_commit, commit_for_patch, warnings = (
784 status.compare_with_series(series, patches))
785 self.assertEqual(1, len(patch_for_commit))
786 self.assertEqual(patch1, patch_for_commit[0])
787 self.assertEqual(1, len(commit_for_patch))
788 self.assertEqual(commit1, commit_for_patch[0])
789 self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"],
790 warnings)
791
792 series.commits = [commit1, commit2, commit3]
793 patches = [patch1, patch2]
794 patch_for_commit, commit_for_patch, warnings = (
795 status.compare_with_series(series, patches))
796 self.assertEqual(2, len(patch_for_commit))
797 self.assertEqual(patch1, patch_for_commit[0])
798 self.assertEqual(patch2, patch_for_commit[1])
799 self.assertEqual(1, len(commit_for_patch))
800 self.assertEqual(commit1, commit_for_patch[0])
801 self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')",
802 "Multiple commits match patch 2 ('Subject 2'):\n"
803 ' Subject 2\n Subject 2'],
804 warnings)
805
806 series.commits = [commit1, commit2]
807 patches = [patch1, patch2, patch3]
808 patch_for_commit, commit_for_patch, warnings = (
809 status.compare_with_series(series, patches))
810 self.assertEqual(1, len(patch_for_commit))
811 self.assertEqual(patch1, patch_for_commit[0])
812 self.assertEqual(2, len(commit_for_patch))
813 self.assertEqual(commit1, commit_for_patch[0])
814 self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n"
815 ' Subject 2\n Subject 2',
816 "Cannot find commit for patch 3 ('Subject 2')"],
817 warnings)
818
Simon Glassf9b03cf2020-11-03 13:54:14 -0700819 def _fake_patchwork2(self, url, subpath):
Simon Glass3db916d2020-10-29 21:46:35 -0600820 """Fake Patchwork server for the function below
821
822 This handles accessing series, patches and comments, providing the data
823 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700824
825 Args:
826 url (str): URL of patchwork server
827 subpath (str): URL subpath to use
Simon Glass3db916d2020-10-29 21:46:35 -0600828 """
829 re_series = re.match(r'series/(\d*)/$', subpath)
830 re_patch = re.match(r'patches/(\d*)/$', subpath)
831 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
832 if re_series:
833 series_num = re_series.group(1)
834 if series_num == '1234':
835 return {'patches': self.patches}
836 elif re_patch:
837 patch_num = int(re_patch.group(1))
838 patch = self.patches[patch_num - 1]
839 return patch
840 elif re_comments:
841 patch_num = int(re_comments.group(1))
842 patch = self.patches[patch_num - 1]
843 return patch.comments
844 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
845
Simon Glassd85bb8f2022-01-29 14:14:09 -0700846 def test_find_new_responses(self):
Simon Glass3db916d2020-10-29 21:46:35 -0600847 """Test operation of find_new_responses()"""
848 commit1 = Commit('abcd')
849 commit1.subject = 'Subject 1'
850 commit2 = Commit('ef12')
851 commit2.subject = 'Subject 2'
852
853 patch1 = status.Patch('1')
854 patch1.parse_subject('[1/2] Subject 1')
855 patch1.name = patch1.raw_subject
856 patch1.content = 'This is my patch content'
857 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
858
859 patch1.comments = [comment1a]
860
861 patch2 = status.Patch('2')
862 patch2.parse_subject('[2/2] Subject 2')
863 patch2.name = patch2.raw_subject
864 patch2.content = 'Some other patch content'
865 comment2a = {
866 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
867 (self.mary, self.leb)}
868 comment2b = {'content': 'Reviewed-by: %s' % self.fred}
869 patch2.comments = [comment2a, comment2b]
870
871 # This test works by setting up commits and patch for use by the fake
872 # Rest API function _fake_patchwork2(). It calls various functions in
873 # the status module after setting up tags in the commits, checking that
874 # things behaves as expected
875 self.commits = [commit1, commit2]
876 self.patches = [patch1, patch2]
877 count = 2
878 new_rtag_list = [None] * count
Simon Glass2112d072020-10-29 21:46:38 -0600879 review_list = [None, None]
Simon Glass3db916d2020-10-29 21:46:35 -0600880
881 # Check that the tags are picked up on the first patch
Simon Glass2112d072020-10-29 21:46:38 -0600882 status.find_new_responses(new_rtag_list, review_list, 0, commit1,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700883 patch1, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600884 self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}})
885
886 # Now the second patch
Simon Glass2112d072020-10-29 21:46:38 -0600887 status.find_new_responses(new_rtag_list, review_list, 1, commit2,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700888 patch2, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600889 self.assertEqual(new_rtag_list[1], {
890 'Reviewed-by': {self.mary, self.fred},
891 'Tested-by': {self.leb}})
892
893 # Now add some tags to the commit, which means they should not appear as
894 # 'new' tags when scanning comments
895 new_rtag_list = [None] * count
896 commit1.rtags = {'Reviewed-by': {self.joe}}
Simon Glass2112d072020-10-29 21:46:38 -0600897 status.find_new_responses(new_rtag_list, review_list, 0, commit1,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700898 patch1, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600899 self.assertEqual(new_rtag_list[0], {})
900
901 # For the second commit, add Ed and Fred, so only Mary should be left
902 commit2.rtags = {
903 'Tested-by': {self.leb},
904 'Reviewed-by': {self.fred}}
Simon Glass2112d072020-10-29 21:46:38 -0600905 status.find_new_responses(new_rtag_list, review_list, 1, commit2,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700906 patch2, None, self._fake_patchwork2)
Simon Glass3db916d2020-10-29 21:46:35 -0600907 self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}})
908
909 # Check that the output patches expectations:
910 # 1 Subject 1
911 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
912 # 2 Subject 2
913 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
914 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
915 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
916 # 1 new response available in patchwork
917
918 series = Series()
919 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -0700920 terminal.set_print_test_mode()
Simon Glass2112d072020-10-29 21:46:38 -0600921 status.check_patchwork_status(series, '1234', None, None, False, False,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700922 None, self._fake_patchwork2)
Simon Glass02811582022-01-29 14:14:18 -0700923 lines = iter(terminal.get_print_test_lines())
Simon Glass3db916d2020-10-29 21:46:35 -0600924 col = terminal.Color()
925 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
926 next(lines))
927 self.assertEqual(
928 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
929 bright=False),
930 next(lines))
931 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False),
932 next(lines))
933
934 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE),
935 next(lines))
936 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -0600937 terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -0600938 bright=False),
939 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -0600940 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -0600941 next(lines))
942 self.assertEqual(
Simon Glass2112d072020-10-29 21:46:38 -0600943 terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False,
Simon Glass3db916d2020-10-29 21:46:35 -0600944 bright=False),
945 next(lines))
Simon Glass2112d072020-10-29 21:46:38 -0600946 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False),
Simon Glass3db916d2020-10-29 21:46:35 -0600947 next(lines))
948 self.assertEqual(
949 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
950 next(lines))
951 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
952 next(lines))
953 self.assertEqual(terminal.PrintLine(
Simon Glassd0a0a582020-10-29 21:46:36 -0600954 '1 new response available in patchwork (use -d to write them to a new branch)',
955 None), next(lines))
956
Simon Glassf9b03cf2020-11-03 13:54:14 -0700957 def _fake_patchwork3(self, url, subpath):
Simon Glassd0a0a582020-10-29 21:46:36 -0600958 """Fake Patchwork server for the function below
959
960 This handles accessing series, patches and comments, providing the data
961 in self.patches to the caller
Simon Glassf9b03cf2020-11-03 13:54:14 -0700962
963 Args:
964 url (str): URL of patchwork server
965 subpath (str): URL subpath to use
Simon Glassd0a0a582020-10-29 21:46:36 -0600966 """
967 re_series = re.match(r'series/(\d*)/$', subpath)
968 re_patch = re.match(r'patches/(\d*)/$', subpath)
969 re_comments = re.match(r'patches/(\d*)/comments/$', subpath)
970 if re_series:
971 series_num = re_series.group(1)
972 if series_num == '1234':
973 return {'patches': self.patches}
974 elif re_patch:
975 patch_num = int(re_patch.group(1))
976 patch = self.patches[patch_num - 1]
977 return patch
978 elif re_comments:
979 patch_num = int(re_comments.group(1))
980 patch = self.patches[patch_num - 1]
981 return patch.comments
982 raise ValueError('Fake Patchwork does not understand: %s' % subpath)
983
Simon Glassd85bb8f2022-01-29 14:14:09 -0700984 def test_create_branch(self):
Simon Glassd0a0a582020-10-29 21:46:36 -0600985 """Test operation of create_branch()"""
986 repo = self.make_git_tree()
987 branch = 'first'
988 dest_branch = 'first2'
989 count = 2
990 gitdir = os.path.join(self.gitdir, '.git')
991
992 # Set up the test git tree. We use branch 'first' which has two commits
993 # in it
994 series = patchstream.get_metadata_for_list(branch, gitdir, count)
995 self.assertEqual(2, len(series.commits))
996
997 patch1 = status.Patch('1')
998 patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
999 patch1.name = patch1.raw_subject
1000 patch1.content = 'This is my patch content'
1001 comment1a = {'content': 'Reviewed-by: %s\n' % self.joe}
1002
1003 patch1.comments = [comment1a]
1004
1005 patch2 = status.Patch('2')
1006 patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
1007 patch2.name = patch2.raw_subject
1008 patch2.content = 'Some other patch content'
1009 comment2a = {
1010 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1011 (self.mary, self.leb)}
1012 comment2b = {
1013 'content': 'Reviewed-by: %s' % self.fred}
1014 patch2.comments = [comment2a, comment2b]
1015
1016 # This test works by setting up patches for use by the fake Rest API
1017 # function _fake_patchwork3(). The fake patch comments above should
1018 # result in new review tags that are collected and added to the commits
1019 # created in the destination branch.
1020 self.patches = [patch1, patch2]
1021 count = 2
1022
1023 # Expected output:
1024 # 1 i2c: I2C things
1025 # + Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1026 # 2 spi: SPI fixes
1027 # + Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1028 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1029 # + Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1030 # 4 new responses available in patchwork
1031 # 4 responses added from patchwork into new branch 'first2'
1032 # <unittest.result.TestResult run=8 errors=0 failures=0>
1033
Simon Glass02811582022-01-29 14:14:18 -07001034 terminal.set_print_test_mode()
Simon Glassd0a0a582020-10-29 21:46:36 -06001035 status.check_patchwork_status(series, '1234', branch, dest_branch,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001036 False, False, None, self._fake_patchwork3,
1037 repo)
Simon Glass02811582022-01-29 14:14:18 -07001038 lines = terminal.get_print_test_lines()
Simon Glassd0a0a582020-10-29 21:46:36 -06001039 self.assertEqual(12, len(lines))
1040 self.assertEqual(
1041 "4 responses added from patchwork into new branch 'first2'",
1042 lines[11].text)
1043
1044 # Check that the destination branch has the new tags
1045 new_series = patchstream.get_metadata_for_list(dest_branch, gitdir,
1046 count)
1047 self.assertEqual(
1048 {'Reviewed-by': {self.joe}},
1049 new_series.commits[0].rtags)
1050 self.assertEqual(
1051 {'Tested-by': {self.leb},
1052 'Reviewed-by': {self.fred, self.mary}},
1053 new_series.commits[1].rtags)
1054
1055 # Now check the actual test of the first commit message. We expect to
1056 # see the new tags immediately below the old ones.
1057 stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir)
1058 lines = iter([line.strip() for line in stdout.splitlines()
1059 if '-by:' in line])
1060
1061 # First patch should have the review tag
1062 self.assertEqual('Reviewed-by: %s' % self.joe, next(lines))
1063
1064 # Second patch should have the sign-off then the tested-by and two
1065 # reviewed-by tags
1066 self.assertEqual('Signed-off-by: %s' % self.leb, next(lines))
1067 self.assertEqual('Reviewed-by: %s' % self.fred, next(lines))
1068 self.assertEqual('Reviewed-by: %s' % self.mary, next(lines))
1069 self.assertEqual('Tested-by: %s' % self.leb, next(lines))
Simon Glassda8a2922020-10-29 21:46:37 -06001070
Simon Glassd85bb8f2022-01-29 14:14:09 -07001071 def test_parse_snippets(self):
Simon Glassda8a2922020-10-29 21:46:37 -06001072 """Test parsing of review snippets"""
1073 text = '''Hi Fred,
1074
1075This is a comment from someone.
1076
1077Something else
1078
1079On some recent date, Fred wrote:
1080> This is why I wrote the patch
1081> so here it is
1082
1083Now a comment about the commit message
1084A little more to say
1085
1086Even more
1087
1088> diff --git a/file.c b/file.c
1089> Some more code
1090> Code line 2
1091> Code line 3
1092> Code line 4
1093> Code line 5
1094> Code line 6
1095> Code line 7
1096> Code line 8
1097> Code line 9
1098
1099And another comment
1100
Simon Glassd85bb8f2022-01-29 14:14:09 -07001101> @@ -153,8 +143,13 @@ def check_patch(fname, show_types=False):
Simon Glassda8a2922020-10-29 21:46:37 -06001102> further down on the file
1103> and more code
1104> +Addition here
1105> +Another addition here
1106> codey
1107> more codey
1108
1109and another thing in same file
1110
1111> @@ -253,8 +243,13 @@
1112> with no function context
1113
1114one more thing
1115
1116> diff --git a/tools/patman/main.py b/tools/patman/main.py
1117> +line of code
1118now a very long comment in a different file
1119line2
1120line3
1121line4
1122line5
1123line6
1124line7
1125line8
1126'''
1127 pstrm = PatchStream.process_text(text, True)
1128 self.assertEqual([], pstrm.commit.warn)
1129
1130 # We expect to the filename and up to 5 lines of code context before
1131 # each comment. The 'On xxx wrote:' bit should be removed.
1132 self.assertEqual(
1133 [['Hi Fred,',
1134 'This is a comment from someone.',
1135 'Something else'],
1136 ['> This is why I wrote the patch',
1137 '> so here it is',
1138 'Now a comment about the commit message',
1139 'A little more to say', 'Even more'],
1140 ['> File: file.c', '> Code line 5', '> Code line 6',
1141 '> Code line 7', '> Code line 8', '> Code line 9',
1142 'And another comment'],
1143 ['> File: file.c',
Simon Glassd85bb8f2022-01-29 14:14:09 -07001144 '> Line: 153 / 143: def check_patch(fname, show_types=False):',
Simon Glassda8a2922020-10-29 21:46:37 -06001145 '> and more code', '> +Addition here', '> +Another addition here',
1146 '> codey', '> more codey', 'and another thing in same file'],
1147 ['> File: file.c', '> Line: 253 / 243',
1148 '> with no function context', 'one more thing'],
1149 ['> File: tools/patman/main.py', '> +line of code',
1150 'now a very long comment in a different file',
1151 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']],
1152 pstrm.snippets)
Simon Glass2112d072020-10-29 21:46:38 -06001153
Simon Glassd85bb8f2022-01-29 14:14:09 -07001154 def test_review_snippets(self):
Simon Glass2112d072020-10-29 21:46:38 -06001155 """Test showing of review snippets"""
1156 def _to_submitter(who):
1157 m_who = re.match('(.*) <(.*)>', who)
1158 return {
1159 'name': m_who.group(1),
1160 'email': m_who.group(2)
1161 }
1162
1163 commit1 = Commit('abcd')
1164 commit1.subject = 'Subject 1'
1165 commit2 = Commit('ef12')
1166 commit2.subject = 'Subject 2'
1167
1168 patch1 = status.Patch('1')
1169 patch1.parse_subject('[1/2] Subject 1')
1170 patch1.name = patch1.raw_subject
1171 patch1.content = 'This is my patch content'
1172 comment1a = {'submitter': _to_submitter(self.joe),
1173 'content': '''Hi Fred,
1174
1175On some date Fred wrote:
1176
1177> diff --git a/file.c b/file.c
1178> Some code
1179> and more code
1180
1181Here is my comment above the above...
1182
1183
1184Reviewed-by: %s
1185''' % self.joe}
1186
1187 patch1.comments = [comment1a]
1188
1189 patch2 = status.Patch('2')
1190 patch2.parse_subject('[2/2] Subject 2')
1191 patch2.name = patch2.raw_subject
1192 patch2.content = 'Some other patch content'
1193 comment2a = {
1194 'content': 'Reviewed-by: %s\nTested-by: %s\n' %
1195 (self.mary, self.leb)}
1196 comment2b = {'submitter': _to_submitter(self.fred),
1197 'content': '''Hi Fred,
1198
1199On some date Fred wrote:
1200
1201> diff --git a/tools/patman/commit.py b/tools/patman/commit.py
1202> @@ -41,6 +41,9 @@ class Commit:
1203> self.rtags = collections.defaultdict(set)
1204> self.warn = []
1205>
1206> + def __str__(self):
1207> + return self.subject
1208> +
Simon Glassd85bb8f2022-01-29 14:14:09 -07001209> def add_change(self, version, info):
Simon Glass2112d072020-10-29 21:46:38 -06001210> """Add a new change line to the change list for a version.
1211>
1212A comment
1213
1214Reviewed-by: %s
1215''' % self.fred}
1216 patch2.comments = [comment2a, comment2b]
1217
1218 # This test works by setting up commits and patch for use by the fake
1219 # Rest API function _fake_patchwork2(). It calls various functions in
1220 # the status module after setting up tags in the commits, checking that
1221 # things behaves as expected
1222 self.commits = [commit1, commit2]
1223 self.patches = [patch1, patch2]
1224
1225 # Check that the output patches expectations:
1226 # 1 Subject 1
1227 # Reviewed-by: Joe Bloggs <joe@napierwallies.co.nz>
1228 # 2 Subject 2
1229 # Tested-by: Lord Edmund Blackaddër <weasel@blackadder.org>
1230 # Reviewed-by: Fred Bloggs <f.bloggs@napier.net>
1231 # + Reviewed-by: Mary Bloggs <mary@napierwallies.co.nz>
1232 # 1 new response available in patchwork
1233
1234 series = Series()
1235 series.commits = [commit1, commit2]
Simon Glass02811582022-01-29 14:14:18 -07001236 terminal.set_print_test_mode()
Simon Glass2112d072020-10-29 21:46:38 -06001237 status.check_patchwork_status(series, '1234', None, None, False, True,
Simon Glassf9b03cf2020-11-03 13:54:14 -07001238 None, self._fake_patchwork2)
Simon Glass02811582022-01-29 14:14:18 -07001239 lines = iter(terminal.get_print_test_lines())
Simon Glass2112d072020-10-29 21:46:38 -06001240 col = terminal.Color()
1241 self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
1242 next(lines))
1243 self.assertEqual(
1244 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1245 next(lines))
1246 self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(lines))
1247
1248 self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED),
1249 next(lines))
1250 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1251 self.assertEqual(terminal.PrintLine('', None), next(lines))
1252 self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA),
1253 next(lines))
1254 self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA),
1255 next(lines))
1256 self.assertEqual(terminal.PrintLine(' > and more code', col.MAGENTA),
1257 next(lines))
1258 self.assertEqual(terminal.PrintLine(
1259 ' Here is my comment above the above...', None), next(lines))
1260 self.assertEqual(terminal.PrintLine('', None), next(lines))
1261
1262 self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE),
1263 next(lines))
1264 self.assertEqual(
1265 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1266 next(lines))
1267 self.assertEqual(terminal.PrintLine(self.fred, col.WHITE),
1268 next(lines))
1269 self.assertEqual(
1270 terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False),
1271 next(lines))
1272 self.assertEqual(terminal.PrintLine(self.mary, col.WHITE),
1273 next(lines))
1274 self.assertEqual(
1275 terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False),
1276 next(lines))
1277 self.assertEqual(terminal.PrintLine(self.leb, col.WHITE),
1278 next(lines))
1279
1280 self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED),
1281 next(lines))
1282 self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines))
1283 self.assertEqual(terminal.PrintLine('', None), next(lines))
1284 self.assertEqual(terminal.PrintLine(
1285 ' > File: tools/patman/commit.py', col.MAGENTA), next(lines))
1286 self.assertEqual(terminal.PrintLine(
1287 ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(lines))
1288 self.assertEqual(terminal.PrintLine(
1289 ' > + return self.subject', col.MAGENTA), next(lines))
1290 self.assertEqual(terminal.PrintLine(
1291 ' > +', col.MAGENTA), next(lines))
1292 self.assertEqual(
Simon Glassd85bb8f2022-01-29 14:14:09 -07001293 terminal.PrintLine(' > def add_change(self, version, info):',
Simon Glass2112d072020-10-29 21:46:38 -06001294 col.MAGENTA),
1295 next(lines))
1296 self.assertEqual(terminal.PrintLine(
1297 ' > """Add a new change line to the change list for a version.',
1298 col.MAGENTA), next(lines))
1299 self.assertEqual(terminal.PrintLine(
1300 ' >', col.MAGENTA), next(lines))
1301 self.assertEqual(terminal.PrintLine(
1302 ' A comment', None), next(lines))
1303 self.assertEqual(terminal.PrintLine('', None), next(lines))
1304
1305 self.assertEqual(terminal.PrintLine(
1306 '4 new responses available in patchwork (use -d to write them to a new branch)',
1307 None), next(lines))
Simon Glass6a222e62021-08-01 16:02:39 -06001308
Simon Glassd85bb8f2022-01-29 14:14:09 -07001309 def test_insert_tags(self):
Simon Glass6a222e62021-08-01 16:02:39 -06001310 """Test inserting of review tags"""
1311 msg = '''first line
1312second line.'''
1313 tags = [
1314 'Reviewed-by: Bin Meng <bmeng.cn@gmail.com>',
1315 'Tested-by: Bin Meng <bmeng.cn@gmail.com>'
1316 ]
1317 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.com>'
1318 tag_str = '\n'.join(tags)
1319
1320 new_msg = patchstream.insert_tags(msg, tags)
1321 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1322
1323 new_msg = patchstream.insert_tags(msg + '\n', tags)
1324 self.assertEqual(msg + '\n\n' + tag_str, new_msg)
1325
1326 msg += '\n\n' + signoff
1327 new_msg = patchstream.insert_tags(msg, tags)
1328 self.assertEqual(msg + '\n' + tag_str, new_msg)