From 427b028aeb2f3678aced5568a5d86c2fff406f46 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:13 -0600 Subject: patman: Fix whitespace errors in func_test Fix up various indentation and other minor things to make pylint3 happier. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 810af9c6042..7fffe649974 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -24,20 +24,20 @@ from patman.test_util import capture_sys_output try: import pygit2 - HAVE_PYGIT2= True + HAVE_PYGIT2 = True except ModuleNotFoundError: HAVE_PYGIT2 = False @contextlib.contextmanager def capture(): - oldout,olderr = sys.stdout, sys.stderr + oldout, olderr = sys.stdout, sys.stderr try: - out=[StringIO(), StringIO()] - sys.stdout,sys.stderr = out + out = [StringIO(), StringIO()] + sys.stdout, sys.stderr = out yield out finally: - sys.stdout,sys.stderr = oldout, olderr + sys.stdout, sys.stderr = oldout, olderr out[0] = out[0].getvalue() out[1] = out[1].getvalue() @@ -160,10 +160,10 @@ class TestFunctional(unittest.TestCase): in_reply_to = mel count = 2 settings.alias = { - 'fdt': ['simon'], - 'u-boot': ['u-boot@lists.denx.de'], - 'simon': [ed], - 'fred': [fred], + 'fdt': ['simon'], + 'u-boot': ['u-boot@lists.denx.de'], + 'simon': [ed], + 'fred': [fred], } text = self.GetText('test01.txt') @@ -177,9 +177,9 @@ class TestFunctional(unittest.TestCase): cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags, add_maintainers, None) - cmd = gitutil.EmailPatches(series, cover_fname, args, - dry_run, not ignore_bad_tags, cc_file, - in_reply_to=in_reply_to, thread=None) + cmd = gitutil.EmailPatches( + series, cover_fname, args, dry_run, not ignore_bad_tags, + cc_file, in_reply_to=in_reply_to, thread=None) series.ShowActions(args, cmd, process_tags) cc_lines = open(cc_file, encoding='utf-8').read().splitlines() os.remove(cc_file) @@ -221,8 +221,9 @@ class TestFunctional(unittest.TestCase): self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), tools.ToUnicode(cc_lines[0])) - self.assertEqual(('%s %s\0%s\0%s\0%s' % (args[1], fred, ed, rick, - stefan)), tools.ToUnicode(cc_lines[1])) + self.assertEqual( + '%s %s\0%s\0%s\0%s' % (args[1], fred, ed, rick, stefan), + tools.ToUnicode(cc_lines[1])) expected = ''' This is a test of how the cover @@ -256,8 +257,8 @@ Simon Glass (2): ''' lines = open(cover_fname, encoding='utf-8').read().splitlines() self.assertEqual( - 'Subject: [RFC PATCH v3 0/2] test: A test patch series', - lines[3]) + 'Subject: [RFC PATCH v3 0/2] test: A test patch series', + lines[3]) self.assertEqual(expected.splitlines(), lines[7:]) for i, fname in enumerate(args): @@ -310,7 +311,7 @@ Changes in v2: tools.WriteFile(path, text, binary=False) index = self.repo.index index.add(fname) - author = pygit2.Signature('Test user', 'test@email.com') + author = pygit2.Signature('Test user', 'test@email.com') committer = author tree = index.write_tree() message = subject + '\n' + body @@ -335,7 +336,7 @@ Changes in v2: author = pygit2.Signature('Test user', 'test@email.com') committer = author commit = repo.create_commit('HEAD', author, committer, - 'Created master', new_tree, []) + 'Created master', new_tree, []) self.make_commit_with_file('Initial commit', ''' Add a README -- cgit v1.2.3 From 366954fdd75200852c134003de4a66b0a20f9362 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:14 -0600 Subject: patman: Use capture_sys_output() consistently One test still uses its own function for capturing output. Modify it to use the standard one in test_util Signed-off-by: Simon Glass --- tools/patman/func_test.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 7fffe649974..263cb340ef5 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -4,7 +4,6 @@ # Copyright 2017 Google, Inc # -import contextlib import os import re import shutil @@ -12,8 +11,6 @@ import sys import tempfile import unittest -from io import StringIO - from patman import control from patman import gitutil from patman import patchstream @@ -29,19 +26,6 @@ except ModuleNotFoundError: HAVE_PYGIT2 = False -@contextlib.contextmanager -def capture(): - oldout, olderr = sys.stdout, sys.stderr - try: - out = [StringIO(), StringIO()] - sys.stdout, sys.stderr = out - yield out - finally: - sys.stdout, sys.stderr = oldout, olderr - out[0] = out[0].getvalue() - out[1] = out[1].getvalue() - - class TestFunctional(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix='patman.') @@ -169,7 +153,7 @@ class TestFunctional(unittest.TestCase): text = self.GetText('test01.txt') series = patchstream.GetMetaDataForTest(text) cover_fname, args = self.CreatePatchesForTest(series) - with capture() as out: + with capture_sys_output() as out: patchstream.FixPatches(series, args) if cover_fname and series.get('cover'): patchstream.InsertCoverLetter(cover_fname, series, count) @@ -184,7 +168,7 @@ class TestFunctional(unittest.TestCase): cc_lines = open(cc_file, encoding='utf-8').read().splitlines() os.remove(cc_file) - lines = out[0].splitlines() + lines = out[0].getvalue().splitlines() self.assertEqual('Cleaned %s patches' % len(series.commits), lines[0]) self.assertEqual('Change log missing for v2', lines[1]) self.assertEqual('Change log missing for v3', lines[2]) -- cgit v1.2.3 From fca99117a5512ad0af87fabef3954dce353d765f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:15 -0600 Subject: patman: Fix remaining pylint3 warnings in func_test This fixes all but the ones about too many variables/statements. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 76 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 18 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 263cb340ef5..b3c3e5796ad 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -4,6 +4,8 @@ # Copyright 2017 Google, Inc # +"""Functional tests for checking that patman behaves correctly""" + import os import re import shutil @@ -27,6 +29,7 @@ except ModuleNotFoundError: class TestFunctional(unittest.TestCase): + """Functional tests for checking that patman behaves correctly""" def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix='patman.') self.gitdir = os.path.join(self.tmpdir, 'git') @@ -36,33 +39,69 @@ class TestFunctional(unittest.TestCase): shutil.rmtree(self.tmpdir) @staticmethod - def GetPath(fname): + def _get_path(fname): + """Get the path to a test file + + Args: + fname (str): Filename to obtain + + Returns: + str: Full path to file in the test directory + """ return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'test', fname) @classmethod - def GetText(self, fname): - return open(self.GetPath(fname), encoding='utf-8').read() + def _get_text(cls, fname): + """Read a file as text + + Args: + fname (str): Filename to read + + Returns: + str: Contents of file + """ + return open(cls._get_path(fname), encoding='utf-8').read() @classmethod - def GetPatchName(self, subject): + def _get_patch_name(cls, subject): + """Get the filename of a patch given its subject + + Args: + subject (str): Patch subject + + Returns: + str: Filename for that patch + """ fname = re.sub('[ :]', '-', subject) return fname.replace('--', '-') - def CreatePatchesForTest(self, series): + def _create_patches_for_test(self, series): + """Create patch files for use by tests + + This copies patch files from the test directory as needed by the series + + Args: + series (Series): Series containing commits to convert + + Returns: + tuple: + str: Cover-letter filename, or None if none + fname_list: list of str, each a patch filename + """ cover_fname = None fname_list = [] for i, commit in enumerate(series.commits): - clean_subject = self.GetPatchName(commit.subject) + clean_subject = self._get_patch_name(commit.subject) src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52]) fname = os.path.join(self.tmpdir, src_fname) - shutil.copy(self.GetPath(src_fname), fname) + shutil.copy(self._get_path(src_fname), fname) fname_list.append(fname) if series.get('cover'): src_fname = '0000-cover-letter.patch' cover_fname = os.path.join(self.tmpdir, src_fname) fname = os.path.join(self.tmpdir, src_fname) - shutil.copy(self.GetPath(src_fname), fname) + shutil.copy(self._get_path(src_fname), fname) return cover_fname, fname_list @@ -137,7 +176,8 @@ class TestFunctional(unittest.TestCase): stefan = b'Stefan Br\xc3\xbcns '.decode('utf-8') rick = 'Richard III ' mel = b'Lord M\xc3\xablchett '.decode('utf-8') - ed = b'Lond Edmund Blackadd\xc3\xabr Date: Thu, 29 Oct 2020 21:46:16 -0600 Subject: patman: Allow linking a series with patchwork Add a new Series-links tag to tell patman how to find the series in patchwork. Each item is the series ID optionally preceded by the series version that the link refers to. An empty version indicates this is the latest series. For example: Series-links: 209816 1:203302 Documentation is added in a later patch. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index b3c3e5796ad..ea3c84632c0 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -417,6 +417,7 @@ Series for my board This series implements support for my glorious board. END +Series-links: 183237 ''', 'serial.c', '''The code for the serial driver is here''') self.make_commit_with_file('bootm: Make it boot', ''' -- cgit v1.2.3 From d93720e13800423d3d6c9f8318d5a75ce7362639 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:19 -0600 Subject: patman: Rename functions in patchstream Rename these functions to lower case as per PEP8. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index ea3c84632c0..bdeccafda0b 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -191,12 +191,12 @@ class TestFunctional(unittest.TestCase): } text = self._get_text('test01.txt') - series = patchstream.GetMetaDataForTest(text) + series = patchstream.get_metadata_for_test(text) cover_fname, args = self._create_patches_for_test(series) with capture_sys_output() as out: - patchstream.FixPatches(series, args) + patchstream.fix_patches(series, args) if cover_fname and series.get('cover'): - patchstream.InsertCoverLetter(cover_fname, series, count) + patchstream.insert_cover_letter(cover_fname, series, count) series.DoChecks() cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags, add_maintainers, -- cgit v1.2.3 From 47f62952cce810c6e02eb216ec32ce69713534a7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:26 -0600 Subject: patman: Drop unused signoff member This is not used. Drop it. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index bdeccafda0b..2290ba95e9d 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -16,6 +16,7 @@ import unittest from patman import control from patman import gitutil from patman import patchstream +from patman.patchstream import PatchStream from patman import settings from patman import terminal from patman import tools -- cgit v1.2.3 From 7457051e41be1058494bcb96f8ebd05176a3e6cc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:27 -0600 Subject: patman: Add a test for PatchStream tags The current functional tests run most of patman. Add a smaller test that just checks tag handling with the PatchStream class. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 2290ba95e9d..2a0da8b3ccf 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -31,6 +31,9 @@ except ModuleNotFoundError: class TestFunctional(unittest.TestCase): """Functional tests for checking that patman behaves correctly""" + leb = (b'Lord Edmund Blackadd\xc3\xabr '. + decode('utf-8')) + def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix='patman.') self.gitdir = os.path.join(self.tmpdir, 'git') @@ -177,8 +180,6 @@ class TestFunctional(unittest.TestCase): stefan = b'Stefan Br\xc3\xbcns '.decode('utf-8') rick = 'Richard III ' mel = b'Lord M\xc3\xablchett '.decode('utf-8') - leb = (b'Lond Edmund Blackadd\xc3\xabr +Reviewed-by: Mary Bloggs +Tested-by: %s +''' % self.leb + pstrm = PatchStream.process_text(text) + self.assertEqual(pstrm.commit.rtags, { + 'Reviewed-by': {'Mary Bloggs ', + 'Joe Bloggs '}, + 'Tested-by': {self.leb}}) -- cgit v1.2.3 From 4af99874562f2ae26e84a9904009c7ce121d540d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:28 -0600 Subject: patman: Add some tests for warnings Add tests that check that warnings are generated when expected. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 111 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 9 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 2a0da8b3ccf..02d46ae5f73 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -33,6 +33,9 @@ class TestFunctional(unittest.TestCase): """Functional tests for checking that patman behaves correctly""" leb = (b'Lord Edmund Blackadd\xc3\xabr '. decode('utf-8')) + fred = 'Fred Bloggs ' + joe = 'Joe Bloggs ' + mary = 'Mary Bloggs ' def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix='patman.') @@ -180,7 +183,6 @@ class TestFunctional(unittest.TestCase): stefan = b'Stefan Br\xc3\xbcns '.decode('utf-8') rick = 'Richard III ' mel = b'Lord M\xc3\xablchett '.decode('utf-8') - fred = 'Fred Bloggs ' add_maintainers = [stefan, rick] dry_run = True in_reply_to = mel @@ -189,7 +191,7 @@ class TestFunctional(unittest.TestCase): 'fdt': ['simon'], 'u-boot': ['u-boot@lists.denx.de'], 'simon': [self.leb], - 'fred': [fred], + 'fred': [self.fred], } text = self._get_text('test01.txt') @@ -231,7 +233,7 @@ class TestFunctional(unittest.TestCase): self.assertEqual('Prefix:\t RFC', lines[line + 3]) self.assertEqual('Cover: 4 lines', lines[line + 4]) line += 5 - self.assertEqual(' Cc: %s' % fred, lines[line + 0]) + self.assertEqual(' Cc: %s' % self.fred, lines[line + 0]) self.assertEqual(' Cc: %s' % tools.FromUnicode(self.leb), lines[line + 1]) self.assertEqual(' Cc: %s' % tools.FromUnicode(mel), @@ -248,7 +250,7 @@ class TestFunctional(unittest.TestCase): self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), tools.ToUnicode(cc_lines[0])) self.assertEqual( - '%s %s\0%s\0%s\0%s' % (args[1], fred, self.leb, rick, stefan), + '%s %s\0%s\0%s\0%s' % (args[1], self.fred, self.leb, rick, stefan), tools.ToUnicode(cc_lines[1])) expected = ''' @@ -487,12 +489,103 @@ complicated as possible''') text = '''This is a patch Signed-off-by: Terminator -Reviewed-by: Joe Bloggs -Reviewed-by: Mary Bloggs +Reviewed-by: %s +Reviewed-by: %s Tested-by: %s -''' % self.leb +''' % (self.joe, self.mary, self.leb) pstrm = PatchStream.process_text(text) self.assertEqual(pstrm.commit.rtags, { - 'Reviewed-by': {'Mary Bloggs ', - 'Joe Bloggs '}, + 'Reviewed-by': {self.joe, self.mary}, 'Tested-by': {self.leb}}) + + def testMissingEnd(self): + """Test a missing END tag""" + text = '''This is a patch + +Cover-letter: +This is the title +missing END after this line +Signed-off-by: Fred +''' + pstrm = PatchStream.process_text(text) + self.assertEqual(["Missing 'END' in section 'cover'"], + pstrm.commit.warn) + + def testMissingBlankLine(self): + """Test a missing blank line after a tag""" + text = '''This is a patch + +Series-changes: 2 +- First line of changes +- Missing blank line after this line +Signed-off-by: Fred +''' + pstrm = PatchStream.process_text(text) + self.assertEqual(["Missing 'blank line' in section 'Series-changes'"], + pstrm.commit.warn) + + def testInvalidCommitTag(self): + """Test an invalid Commit-xxx tag""" + text = '''This is a patch + +Commit-fred: testing +''' + pstrm = PatchStream.process_text(text) + self.assertEqual(["Line 3: Ignoring Commit-fred"], pstrm.commit.warn) + + def testSelfTest(self): + """Test a tested by tag by this user""" + test_line = 'Tested-by: %s@napier.com' % os.getenv('USER') + text = '''This is a patch + +%s +''' % test_line + pstrm = PatchStream.process_text(text) + self.assertEqual(["Ignoring '%s'" % test_line], pstrm.commit.warn) + + def testSpaceBeforeTab(self): + """Test a space before a tab""" + text = '''This is a patch + ++ \tSomething +''' + pstrm = PatchStream.process_text(text) + self.assertEqual(["Line 3/0 has space before tab"], pstrm.commit.warn) + + def testLinesAfterTest(self): + """Test detecting lines after TEST= line""" + text = '''This is a patch + +TEST=sometest +more lines +here +''' + pstrm = PatchStream.process_text(text) + self.assertEqual(["Found 2 lines after TEST="], pstrm.commit.warn) + + def testBlankLineAtEnd(self): + """Test detecting a blank line at the end of a file""" + text = '''This is a patch + +diff --git a/lib/fdtdec.c b/lib/fdtdec.c +index c072e54..942244f 100644 +--- a/lib/fdtdec.c ++++ b/lib/fdtdec.c +@@ -1200,7 +1200,8 @@ int fdtdec_setup_mem_size_base(void) + } + + gd->ram_size = (phys_size_t)(res.end - res.start + 1); +- debug("%s: Initial DRAM size %llx\n", __func__, (u64)gd->ram_size); ++ debug("%s: Initial DRAM size %llx\n", __func__, ++ (unsigned long long)gd->ram_size); ++ +diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c + +-- +2.7.4 + + ''' + pstrm = PatchStream.process_text(text) + self.assertEqual( + ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"], + pstrm.commit.warn) -- cgit v1.2.3 From 8c17f8c5d21842bc95ca6e27680009b6bc650b0f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:29 -0600 Subject: patman: Convert testBasic() to use an interator On balance it is easier to use an iterator here, particularly if we need to insert lines due to new functionality. The only niggle is the need to keep the previous iterator value around in one case. Convert this test to use iter(). Signed-off-by: Simon Glass --- tools/patman/func_test.py | 55 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 02d46ae5f73..b39e3f671dc 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -212,40 +212,39 @@ class TestFunctional(unittest.TestCase): cc_lines = open(cc_file, encoding='utf-8').read().splitlines() os.remove(cc_file) - lines = out[0].getvalue().splitlines() - self.assertEqual('Cleaned %s patches' % len(series.commits), lines[0]) - self.assertEqual('Change log missing for v2', lines[1]) - self.assertEqual('Change log missing for v3', lines[2]) - self.assertEqual('Change log for unknown version v4', lines[3]) - self.assertEqual("Alias 'pci' not found", lines[4]) - self.assertIn('Dry run', lines[5]) - self.assertIn('Send a total of %d patches' % count, lines[7]) - line = 8 - for i in range(len(series.commits)): - self.assertEqual(' %s' % args[i], lines[line + 0]) - line += 1 - while 'Cc:' in lines[line]: - line += 1 - self.assertEqual('To: u-boot@lists.denx.de', lines[line]) - self.assertEqual('Cc: %s' % tools.FromUnicode(stefan), - lines[line + 1]) - self.assertEqual('Version: 3', lines[line + 2]) - self.assertEqual('Prefix:\t RFC', lines[line + 3]) - self.assertEqual('Cover: 4 lines', lines[line + 4]) - line += 5 - self.assertEqual(' Cc: %s' % self.fred, lines[line + 0]) + lines = iter(out[0].getvalue().splitlines()) + self.assertEqual('Cleaned %s patches' % len(series.commits), + next(lines)) + self.assertEqual('Change log missing for v2', next(lines)) + self.assertEqual('Change log missing for v3', next(lines)) + self.assertEqual('Change log for unknown version v4', next(lines)) + self.assertEqual("Alias 'pci' not found", next(lines)) + self.assertIn('Dry run', next(lines)) + self.assertEqual('', next(lines)) + self.assertIn('Send a total of %d patches' % count, next(lines)) + prev = next(lines) + for i, commit in enumerate(series.commits): + self.assertEqual(' %s' % args[i], prev) + while True: + prev = next(lines) + if 'Cc:' not in prev: + break + self.assertEqual('To: u-boot@lists.denx.de', prev) + self.assertEqual('Cc: %s' % tools.FromUnicode(stefan), next(lines)) + self.assertEqual('Version: 3', next(lines)) + self.assertEqual('Prefix:\t RFC', next(lines)) + self.assertEqual('Cover: 4 lines', next(lines)) + self.assertEqual(' Cc: %s' % self.fred, next(lines)) self.assertEqual(' Cc: %s' % tools.FromUnicode(self.leb), - lines[line + 1]) - self.assertEqual(' Cc: %s' % tools.FromUnicode(mel), - lines[line + 2]) - self.assertEqual(' Cc: %s' % rick, lines[line + 3]) + next(lines)) + self.assertEqual(' Cc: %s' % tools.FromUnicode(mel), next(lines)) + self.assertEqual(' Cc: %s' % rick, next(lines)) expected = ('Git command: git send-email --annotate ' '--in-reply-to="%s" --to "u-boot@lists.denx.de" ' '--cc "%s" --cc-cmd "%s --cc-cmd %s" %s %s' % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname, ' '.join(args))) - line += 4 - self.assertEqual(expected, tools.ToUnicode(lines[line])) + self.assertEqual(expected, tools.ToUnicode(next(lines))) self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), tools.ToUnicode(cc_lines[0])) -- cgit v1.2.3 From be051c0c7741d67f5093f6b61b64c45eb200235b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:34 -0600 Subject: patman: Detect missing upstream in CountCommitsToBranch At present if we fail to find the upstream then the error output is piped to wc, resulting in bogus results. Avoid the pipe and check the output directly. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index b39e3f671dc..cce3905c093 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -588,3 +588,22 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c self.assertEqual( ["Found possible blank line(s) at end of file 'lib/fdtdec.c'"], pstrm.commit.warn) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testNoUpstream(self): + """Test CountCommitsToBranch when there is no upstream""" + repo = self.make_git_tree() + target = repo.lookup_reference('refs/heads/base') + self.repo.checkout(target, strategy=pygit2.GIT_CHECKOUT_FORCE) + + # Check that it can detect the current branch + try: + orig_dir = os.getcwd() + os.chdir(self.gitdir) + with self.assertRaises(ValueError) as exc: + gitutil.CountCommitsToBranch(None) + self.assertIn( + "Failed to determine upstream: fatal: no upstream configured for branch 'base'", + str(exc.exception)) + finally: + os.chdir(orig_dir) -- cgit v1.2.3 From dc6df972c9ee2afefd937bee3771865012daccef Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:35 -0600 Subject: patman: Support checking for review tags in patchwork MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before sending out a new version of a series for review, it is important to add any review tags (e.g. Reviewed-by, Acked-by) collected by patchwork. Otherwise people waste time reviewing the same patch repeatedly, become frustrated and stop reviewing your patches. To help with this, add a new 'status' subcommand that checks patchwork for review tags, showing those which are not present in the local branch. This allows users to see what new review tags have been received and then add them. Sample output: $ patman status 1 Subject 1 Reviewed-by: Joe Bloggs 2 Subject 2 Tested-by: Lord Edmund Blackaddër Reviewed-by: Fred Bloggs + Reviewed-by: Mary Bloggs 1 new response available in patchwork The '+' indicates a new tag. Colours are used to make it easier to read. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 315 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index cce3905c093..722844e15d3 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -13,10 +13,13 @@ import sys import tempfile import unittest + +from patman.commit import Commit from patman import control from patman import gitutil from patman import patchstream from patman.patchstream import PatchStream +from patman.series import Series from patman import settings from patman import terminal from patman import tools @@ -25,6 +28,7 @@ from patman.test_util import capture_sys_output try: import pygit2 HAVE_PYGIT2 = True + from patman import status except ModuleNotFoundError: HAVE_PYGIT2 = False @@ -36,6 +40,8 @@ class TestFunctional(unittest.TestCase): fred = 'Fred Bloggs ' joe = 'Joe Bloggs ' mary = 'Mary Bloggs ' + commits = None + patches = None def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix='patman.') @@ -44,6 +50,7 @@ class TestFunctional(unittest.TestCase): def tearDown(self): shutil.rmtree(self.tmpdir) + terminal.SetPrintTestMode(False) @staticmethod def _get_path(fname): @@ -607,3 +614,311 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c str(exc.exception)) finally: os.chdir(orig_dir) + + @staticmethod + def _fake_patchwork(subpath): + """Fake Patchwork server for the function below + + This handles accessing a series, providing a list consisting of a + single patch + """ + re_series = re.match(r'series/(\d*)/$', subpath) + if re_series: + series_num = re_series.group(1) + if series_num == '1234': + return {'patches': [ + {'id': '1', 'name': 'Some patch'}]} + raise ValueError('Fake Patchwork does not understand: %s' % subpath) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testStatusMismatch(self): + """Test Patchwork patches not matching the series""" + series = Series() + + with capture_sys_output() as (_, err): + status.collect_patches(series, 1234, self._fake_patchwork) + self.assertIn('Warning: Patchwork reports 1 patches, series has 0', + err.getvalue()) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testStatusReadPatch(self): + """Test handling a single patch in Patchwork""" + series = Series() + series.commits = [Commit('abcd')] + + patches = status.collect_patches(series, 1234, self._fake_patchwork) + self.assertEqual(1, len(patches)) + patch = patches[0] + self.assertEqual('1', patch.id) + self.assertEqual('Some patch', patch.raw_subject) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testParseSubject(self): + """Test parsing of the patch subject""" + patch = status.Patch('1') + + # Simple patch not in a series + patch.parse_subject('Testing') + self.assertEqual('Testing', patch.raw_subject) + self.assertEqual('Testing', patch.subject) + self.assertEqual(1, patch.seq) + self.assertEqual(1, patch.count) + self.assertEqual(None, patch.prefix) + self.assertEqual(None, patch.version) + + # First patch in a series + patch.parse_subject('[1/2] Testing') + self.assertEqual('[1/2] Testing', patch.raw_subject) + self.assertEqual('Testing', patch.subject) + self.assertEqual(1, patch.seq) + self.assertEqual(2, patch.count) + self.assertEqual(None, patch.prefix) + self.assertEqual(None, patch.version) + + # Second patch in a series + patch.parse_subject('[2/2] Testing') + self.assertEqual('Testing', patch.subject) + self.assertEqual(2, patch.seq) + self.assertEqual(2, patch.count) + self.assertEqual(None, patch.prefix) + self.assertEqual(None, patch.version) + + # RFC patch + patch.parse_subject('[RFC,3/7] Testing') + self.assertEqual('Testing', patch.subject) + self.assertEqual(3, patch.seq) + self.assertEqual(7, patch.count) + self.assertEqual('RFC', patch.prefix) + self.assertEqual(None, patch.version) + + # Version patch + patch.parse_subject('[v2,3/7] Testing') + self.assertEqual('Testing', patch.subject) + self.assertEqual(3, patch.seq) + self.assertEqual(7, patch.count) + self.assertEqual(None, patch.prefix) + self.assertEqual('v2', patch.version) + + # All fields + patch.parse_subject('[RESEND,v2,3/7] Testing') + self.assertEqual('Testing', patch.subject) + self.assertEqual(3, patch.seq) + self.assertEqual(7, patch.count) + self.assertEqual('RESEND', patch.prefix) + self.assertEqual('v2', patch.version) + + # RFC only + patch.parse_subject('[RESEND] Testing') + self.assertEqual('Testing', patch.subject) + self.assertEqual(1, patch.seq) + self.assertEqual(1, patch.count) + self.assertEqual('RESEND', patch.prefix) + self.assertEqual(None, patch.version) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testCompareSeries(self): + """Test operation of compare_with_series()""" + commit1 = Commit('abcd') + commit1.subject = 'Subject 1' + commit2 = Commit('ef12') + commit2.subject = 'Subject 2' + commit3 = Commit('3456') + commit3.subject = 'Subject 2' + + patch1 = status.Patch('1') + patch1.subject = 'Subject 1' + patch2 = status.Patch('2') + patch2.subject = 'Subject 2' + patch3 = status.Patch('3') + patch3.subject = 'Subject 2' + + series = Series() + series.commits = [commit1] + patches = [patch1] + patch_for_commit, commit_for_patch, warnings = ( + status.compare_with_series(series, patches)) + self.assertEqual(1, len(patch_for_commit)) + self.assertEqual(patch1, patch_for_commit[0]) + self.assertEqual(1, len(commit_for_patch)) + self.assertEqual(commit1, commit_for_patch[0]) + + series.commits = [commit1] + patches = [patch1, patch2] + patch_for_commit, commit_for_patch, warnings = ( + status.compare_with_series(series, patches)) + self.assertEqual(1, len(patch_for_commit)) + self.assertEqual(patch1, patch_for_commit[0]) + self.assertEqual(1, len(commit_for_patch)) + self.assertEqual(commit1, commit_for_patch[0]) + self.assertEqual(["Cannot find commit for patch 2 ('Subject 2')"], + warnings) + + series.commits = [commit1, commit2] + patches = [patch1] + patch_for_commit, commit_for_patch, warnings = ( + status.compare_with_series(series, patches)) + self.assertEqual(1, len(patch_for_commit)) + self.assertEqual(patch1, patch_for_commit[0]) + self.assertEqual(1, len(commit_for_patch)) + self.assertEqual(commit1, commit_for_patch[0]) + self.assertEqual(["Cannot find patch for commit 2 ('Subject 2')"], + warnings) + + series.commits = [commit1, commit2, commit3] + patches = [patch1, patch2] + patch_for_commit, commit_for_patch, warnings = ( + status.compare_with_series(series, patches)) + self.assertEqual(2, len(patch_for_commit)) + self.assertEqual(patch1, patch_for_commit[0]) + self.assertEqual(patch2, patch_for_commit[1]) + self.assertEqual(1, len(commit_for_patch)) + self.assertEqual(commit1, commit_for_patch[0]) + self.assertEqual(["Cannot find patch for commit 3 ('Subject 2')", + "Multiple commits match patch 2 ('Subject 2'):\n" + ' Subject 2\n Subject 2'], + warnings) + + series.commits = [commit1, commit2] + patches = [patch1, patch2, patch3] + patch_for_commit, commit_for_patch, warnings = ( + status.compare_with_series(series, patches)) + self.assertEqual(1, len(patch_for_commit)) + self.assertEqual(patch1, patch_for_commit[0]) + self.assertEqual(2, len(commit_for_patch)) + self.assertEqual(commit1, commit_for_patch[0]) + self.assertEqual(["Multiple patches match commit 2 ('Subject 2'):\n" + ' Subject 2\n Subject 2', + "Cannot find commit for patch 3 ('Subject 2')"], + warnings) + + def _fake_patchwork2(self, subpath): + """Fake Patchwork server for the function below + + This handles accessing series, patches and comments, providing the data + in self.patches to the caller + """ + re_series = re.match(r'series/(\d*)/$', subpath) + re_patch = re.match(r'patches/(\d*)/$', subpath) + re_comments = re.match(r'patches/(\d*)/comments/$', subpath) + if re_series: + series_num = re_series.group(1) + if series_num == '1234': + return {'patches': self.patches} + elif re_patch: + patch_num = int(re_patch.group(1)) + patch = self.patches[patch_num - 1] + return patch + elif re_comments: + patch_num = int(re_comments.group(1)) + patch = self.patches[patch_num - 1] + return patch.comments + raise ValueError('Fake Patchwork does not understand: %s' % subpath) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testFindNewResponses(self): + """Test operation of find_new_responses()""" + commit1 = Commit('abcd') + commit1.subject = 'Subject 1' + commit2 = Commit('ef12') + commit2.subject = 'Subject 2' + + patch1 = status.Patch('1') + patch1.parse_subject('[1/2] Subject 1') + patch1.name = patch1.raw_subject + patch1.content = 'This is my patch content' + comment1a = {'content': 'Reviewed-by: %s\n' % self.joe} + + patch1.comments = [comment1a] + + patch2 = status.Patch('2') + patch2.parse_subject('[2/2] Subject 2') + patch2.name = patch2.raw_subject + patch2.content = 'Some other patch content' + comment2a = { + 'content': 'Reviewed-by: %s\nTested-by: %s\n' % + (self.mary, self.leb)} + comment2b = {'content': 'Reviewed-by: %s' % self.fred} + patch2.comments = [comment2a, comment2b] + + # This test works by setting up commits and patch for use by the fake + # Rest API function _fake_patchwork2(). It calls various functions in + # the status module after setting up tags in the commits, checking that + # things behaves as expected + self.commits = [commit1, commit2] + self.patches = [patch1, patch2] + count = 2 + new_rtag_list = [None] * count + + # Check that the tags are picked up on the first patch + status.find_new_responses(new_rtag_list, 0, commit1, patch1, + self._fake_patchwork2) + self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}}) + + # Now the second patch + status.find_new_responses(new_rtag_list, 1, commit2, patch2, + self._fake_patchwork2) + self.assertEqual(new_rtag_list[1], { + 'Reviewed-by': {self.mary, self.fred}, + 'Tested-by': {self.leb}}) + + # Now add some tags to the commit, which means they should not appear as + # 'new' tags when scanning comments + new_rtag_list = [None] * count + commit1.rtags = {'Reviewed-by': {self.joe}} + status.find_new_responses(new_rtag_list, 0, commit1, patch1, + self._fake_patchwork2) + self.assertEqual(new_rtag_list[0], {}) + + # For the second commit, add Ed and Fred, so only Mary should be left + commit2.rtags = { + 'Tested-by': {self.leb}, + 'Reviewed-by': {self.fred}} + status.find_new_responses(new_rtag_list, 1, commit2, patch2, + self._fake_patchwork2) + self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}}) + + # Check that the output patches expectations: + # 1 Subject 1 + # Reviewed-by: Joe Bloggs + # 2 Subject 2 + # Tested-by: Lord Edmund Blackaddër + # Reviewed-by: Fred Bloggs + # + Reviewed-by: Mary Bloggs + # 1 new response available in patchwork + + series = Series() + series.commits = [commit1, commit2] + terminal.SetPrintTestMode() + status.check_patchwork_status(series, '1234', self._fake_patchwork2) + lines = iter(terminal.GetPrintTestLines()) + col = terminal.Color() + self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), + next(lines)) + self.assertEqual( + terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False, + bright=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.joe, col.WHITE, bright=False), + next(lines)) + + self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE), + next(lines)) + self.assertEqual( + terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False, + bright=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False), + next(lines)) + self.assertEqual( + terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False, + bright=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False), + next(lines)) + self.assertEqual( + terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.mary, col.WHITE), + next(lines)) + self.assertEqual(terminal.PrintLine( + '1 new response available in patchwork', None), next(lines)) -- cgit v1.2.3 From 8f9ba3ab56c49880fb13fc483493b635f787627c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:36 -0600 Subject: patman: Support updating a branch with review tags It is tedious to add review tags into the local branch and errors can sometimes be made. Add an option to create a new branch with the review tags obtained from patchwork. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 127 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 3 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 722844e15d3..2e1529525eb 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -403,7 +403,16 @@ with some I2C-related things in it''') self.make_commit_with_file('spi: SPI fixes', ''' SPI needs some fixes and here they are -''', 'spi.c', '''Some fixes for SPI in this + +Signed-off-by: %s + +Series-to: u-boot +Commit-notes: +title of the series +This is the cover letter for the series +with various details +END +''' % self.leb, 'spi.c', '''Some fixes for SPI in this file to make SPI work better than before''') first_target = repo.revparse_single('HEAD') @@ -889,7 +898,8 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c series = Series() series.commits = [commit1, commit2] terminal.SetPrintTestMode() - status.check_patchwork_status(series, '1234', self._fake_patchwork2) + status.check_patchwork_status(series, '1234', None, None, False, + self._fake_patchwork2) lines = iter(terminal.GetPrintTestLines()) col = terminal.Color() self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), @@ -921,4 +931,115 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c self.assertEqual(terminal.PrintLine(self.mary, col.WHITE), next(lines)) self.assertEqual(terminal.PrintLine( - '1 new response available in patchwork', None), next(lines)) + '1 new response available in patchwork (use -d to write them to a new branch)', + None), next(lines)) + + def _fake_patchwork3(self, subpath): + """Fake Patchwork server for the function below + + This handles accessing series, patches and comments, providing the data + in self.patches to the caller + """ + re_series = re.match(r'series/(\d*)/$', subpath) + re_patch = re.match(r'patches/(\d*)/$', subpath) + re_comments = re.match(r'patches/(\d*)/comments/$', subpath) + if re_series: + series_num = re_series.group(1) + if series_num == '1234': + return {'patches': self.patches} + elif re_patch: + patch_num = int(re_patch.group(1)) + patch = self.patches[patch_num - 1] + return patch + elif re_comments: + patch_num = int(re_comments.group(1)) + patch = self.patches[patch_num - 1] + return patch.comments + raise ValueError('Fake Patchwork does not understand: %s' % subpath) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testCreateBranch(self): + """Test operation of create_branch()""" + repo = self.make_git_tree() + branch = 'first' + dest_branch = 'first2' + count = 2 + gitdir = os.path.join(self.gitdir, '.git') + + # Set up the test git tree. We use branch 'first' which has two commits + # in it + series = patchstream.get_metadata_for_list(branch, gitdir, count) + self.assertEqual(2, len(series.commits)) + + patch1 = status.Patch('1') + patch1.parse_subject('[1/2] %s' % series.commits[0].subject) + patch1.name = patch1.raw_subject + patch1.content = 'This is my patch content' + comment1a = {'content': 'Reviewed-by: %s\n' % self.joe} + + patch1.comments = [comment1a] + + patch2 = status.Patch('2') + patch2.parse_subject('[2/2] %s' % series.commits[1].subject) + patch2.name = patch2.raw_subject + patch2.content = 'Some other patch content' + comment2a = { + 'content': 'Reviewed-by: %s\nTested-by: %s\n' % + (self.mary, self.leb)} + comment2b = { + 'content': 'Reviewed-by: %s' % self.fred} + patch2.comments = [comment2a, comment2b] + + # This test works by setting up patches for use by the fake Rest API + # function _fake_patchwork3(). The fake patch comments above should + # result in new review tags that are collected and added to the commits + # created in the destination branch. + self.patches = [patch1, patch2] + count = 2 + + # Expected output: + # 1 i2c: I2C things + # + Reviewed-by: Joe Bloggs + # 2 spi: SPI fixes + # + Reviewed-by: Fred Bloggs + # + Reviewed-by: Mary Bloggs + # + Tested-by: Lord Edmund Blackaddër + # 4 new responses available in patchwork + # 4 responses added from patchwork into new branch 'first2' + # + + terminal.SetPrintTestMode() + status.check_patchwork_status(series, '1234', branch, dest_branch, + False, self._fake_patchwork3, repo) + lines = terminal.GetPrintTestLines() + self.assertEqual(12, len(lines)) + self.assertEqual( + "4 responses added from patchwork into new branch 'first2'", + lines[11].text) + + # Check that the destination branch has the new tags + new_series = patchstream.get_metadata_for_list(dest_branch, gitdir, + count) + self.assertEqual( + {'Reviewed-by': {self.joe}}, + new_series.commits[0].rtags) + self.assertEqual( + {'Tested-by': {self.leb}, + 'Reviewed-by': {self.fred, self.mary}}, + new_series.commits[1].rtags) + + # Now check the actual test of the first commit message. We expect to + # see the new tags immediately below the old ones. + stdout = patchstream.get_list(dest_branch, count=count, git_dir=gitdir) + lines = iter([line.strip() for line in stdout.splitlines() + if '-by:' in line]) + + # First patch should have the review tag + self.assertEqual('Reviewed-by: %s' % self.joe, next(lines)) + + # Second patch should have the sign-off then the tested-by and two + # reviewed-by tags + self.assertEqual('Signed-off-by: %s' % self.leb, next(lines)) + self.assertEqual('Reviewed-by: %s' % self.fred, next(lines)) + self.assertEqual('Reviewed-by: %s' % self.mary, next(lines)) + self.assertEqual('Tested-by: %s' % self.leb, next(lines)) -- cgit v1.2.3 From 6b3252e230a1b8ba763883a79906d69b9aa50415 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:37 -0600 Subject: patman: Support parsing of review snippets Add support for parsing the contents of a patchwork 'patch' web page containing comments received from reviewers. This allows patman to show these comments in a simple 'snippets' format. A snippet is some quoted code plus some unquoted comments below it. Each review is from a unique person/email and can produce multiple snippets, one for each part of the code that attracts a comment. Show the file and line-number info at the top of each snippet if available. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index 2e1529525eb..bbee4b77d66 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -1043,3 +1043,86 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c self.assertEqual('Reviewed-by: %s' % self.fred, next(lines)) self.assertEqual('Reviewed-by: %s' % self.mary, next(lines)) self.assertEqual('Tested-by: %s' % self.leb, next(lines)) + + def testParseSnippets(self): + """Test parsing of review snippets""" + text = '''Hi Fred, + +This is a comment from someone. + +Something else + +On some recent date, Fred wrote: +> This is why I wrote the patch +> so here it is + +Now a comment about the commit message +A little more to say + +Even more + +> diff --git a/file.c b/file.c +> Some more code +> Code line 2 +> Code line 3 +> Code line 4 +> Code line 5 +> Code line 6 +> Code line 7 +> Code line 8 +> Code line 9 + +And another comment + +> @@ -153,8 +143,13 @@ def CheckPatch(fname, show_types=False): +> further down on the file +> and more code +> +Addition here +> +Another addition here +> codey +> more codey + +and another thing in same file + +> @@ -253,8 +243,13 @@ +> with no function context + +one more thing + +> diff --git a/tools/patman/main.py b/tools/patman/main.py +> +line of code +now a very long comment in a different file +line2 +line3 +line4 +line5 +line6 +line7 +line8 +''' + pstrm = PatchStream.process_text(text, True) + self.assertEqual([], pstrm.commit.warn) + + # We expect to the filename and up to 5 lines of code context before + # each comment. The 'On xxx wrote:' bit should be removed. + self.assertEqual( + [['Hi Fred,', + 'This is a comment from someone.', + 'Something else'], + ['> This is why I wrote the patch', + '> so here it is', + 'Now a comment about the commit message', + 'A little more to say', 'Even more'], + ['> File: file.c', '> Code line 5', '> Code line 6', + '> Code line 7', '> Code line 8', '> Code line 9', + 'And another comment'], + ['> File: file.c', + '> Line: 153 / 143: def CheckPatch(fname, show_types=False):', + '> and more code', '> +Addition here', '> +Another addition here', + '> codey', '> more codey', 'and another thing in same file'], + ['> File: file.c', '> Line: 253 / 243', + '> with no function context', 'one more thing'], + ['> File: tools/patman/main.py', '> +line of code', + 'now a very long comment in a different file', + 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']], + pstrm.snippets) -- cgit v1.2.3 From dc4b2a9770b5b932cd6d98c33ebff6dc46de6849 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 29 Oct 2020 21:46:38 -0600 Subject: patman: Support listing comments from patchwork While reviewing feedback it is helpful to see the review comments on the command line to check that each has been addressed. Add an option to support that. Update the workflow documentation to describe the new features. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 186 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 172 insertions(+), 14 deletions(-) (limited to 'tools/patman/func_test.py') diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index bbee4b77d66..e2adf32c739 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -857,15 +857,16 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c self.patches = [patch1, patch2] count = 2 new_rtag_list = [None] * count + review_list = [None, None] # Check that the tags are picked up on the first patch - status.find_new_responses(new_rtag_list, 0, commit1, patch1, - self._fake_patchwork2) + status.find_new_responses(new_rtag_list, review_list, 0, commit1, + patch1, self._fake_patchwork2) self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}}) # Now the second patch - status.find_new_responses(new_rtag_list, 1, commit2, patch2, - self._fake_patchwork2) + status.find_new_responses(new_rtag_list, review_list, 1, commit2, + patch2, self._fake_patchwork2) self.assertEqual(new_rtag_list[1], { 'Reviewed-by': {self.mary, self.fred}, 'Tested-by': {self.leb}}) @@ -874,16 +875,16 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c # 'new' tags when scanning comments new_rtag_list = [None] * count commit1.rtags = {'Reviewed-by': {self.joe}} - status.find_new_responses(new_rtag_list, 0, commit1, patch1, - self._fake_patchwork2) + status.find_new_responses(new_rtag_list, review_list, 0, commit1, + patch1, self._fake_patchwork2) self.assertEqual(new_rtag_list[0], {}) # For the second commit, add Ed and Fred, so only Mary should be left commit2.rtags = { 'Tested-by': {self.leb}, 'Reviewed-by': {self.fred}} - status.find_new_responses(new_rtag_list, 1, commit2, patch2, - self._fake_patchwork2) + status.find_new_responses(new_rtag_list, review_list, 1, commit2, + patch2, self._fake_patchwork2) self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}}) # Check that the output patches expectations: @@ -898,7 +899,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c series = Series() series.commits = [commit1, commit2] terminal.SetPrintTestMode() - status.check_patchwork_status(series, '1234', None, None, False, + status.check_patchwork_status(series, '1234', None, None, False, False, self._fake_patchwork2) lines = iter(terminal.GetPrintTestLines()) col = terminal.Color() @@ -914,16 +915,16 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE), next(lines)) self.assertEqual( - terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False, + terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False, bright=False), next(lines)) - self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False), + self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False), next(lines)) self.assertEqual( - terminal.PrintLine(' Reviewed-by: ', col.GREEN, newline=False, + terminal.PrintLine(' Tested-by: ', col.GREEN, newline=False, bright=False), next(lines)) - self.assertEqual(terminal.PrintLine(self.fred, col.WHITE, bright=False), + self.assertEqual(terminal.PrintLine(self.leb, col.WHITE, bright=False), next(lines)) self.assertEqual( terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False), @@ -1010,7 +1011,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c terminal.SetPrintTestMode() status.check_patchwork_status(series, '1234', branch, dest_branch, - False, self._fake_patchwork3, repo) + False, False, self._fake_patchwork3, repo) lines = terminal.GetPrintTestLines() self.assertEqual(12, len(lines)) self.assertEqual( @@ -1044,6 +1045,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c self.assertEqual('Reviewed-by: %s' % self.mary, next(lines)) self.assertEqual('Tested-by: %s' % self.leb, next(lines)) + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') def testParseSnippets(self): """Test parsing of review snippets""" text = '''Hi Fred, @@ -1126,3 +1128,159 @@ line8 'now a very long comment in a different file', 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8']], pstrm.snippets) + + @unittest.skipIf(not HAVE_PYGIT2, 'Missing python3-pygit2') + def testReviewSnippets(self): + """Test showing of review snippets""" + def _to_submitter(who): + m_who = re.match('(.*) <(.*)>', who) + return { + 'name': m_who.group(1), + 'email': m_who.group(2) + } + + commit1 = Commit('abcd') + commit1.subject = 'Subject 1' + commit2 = Commit('ef12') + commit2.subject = 'Subject 2' + + patch1 = status.Patch('1') + patch1.parse_subject('[1/2] Subject 1') + patch1.name = patch1.raw_subject + patch1.content = 'This is my patch content' + comment1a = {'submitter': _to_submitter(self.joe), + 'content': '''Hi Fred, + +On some date Fred wrote: + +> diff --git a/file.c b/file.c +> Some code +> and more code + +Here is my comment above the above... + + +Reviewed-by: %s +''' % self.joe} + + patch1.comments = [comment1a] + + patch2 = status.Patch('2') + patch2.parse_subject('[2/2] Subject 2') + patch2.name = patch2.raw_subject + patch2.content = 'Some other patch content' + comment2a = { + 'content': 'Reviewed-by: %s\nTested-by: %s\n' % + (self.mary, self.leb)} + comment2b = {'submitter': _to_submitter(self.fred), + 'content': '''Hi Fred, + +On some date Fred wrote: + +> diff --git a/tools/patman/commit.py b/tools/patman/commit.py +> @@ -41,6 +41,9 @@ class Commit: +> self.rtags = collections.defaultdict(set) +> self.warn = [] +> +> + def __str__(self): +> + return self.subject +> + +> def AddChange(self, version, info): +> """Add a new change line to the change list for a version. +> +A comment + +Reviewed-by: %s +''' % self.fred} + patch2.comments = [comment2a, comment2b] + + # This test works by setting up commits and patch for use by the fake + # Rest API function _fake_patchwork2(). It calls various functions in + # the status module after setting up tags in the commits, checking that + # things behaves as expected + self.commits = [commit1, commit2] + self.patches = [patch1, patch2] + + # Check that the output patches expectations: + # 1 Subject 1 + # Reviewed-by: Joe Bloggs + # 2 Subject 2 + # Tested-by: Lord Edmund Blackaddër + # Reviewed-by: Fred Bloggs + # + Reviewed-by: Mary Bloggs + # 1 new response available in patchwork + + series = Series() + series.commits = [commit1, commit2] + terminal.SetPrintTestMode() + status.check_patchwork_status(series, '1234', None, None, False, True, + self._fake_patchwork2) + lines = iter(terminal.GetPrintTestLines()) + col = terminal.Color() + self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), + next(lines)) + self.assertEqual( + terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.joe, col.WHITE), next(lines)) + + self.assertEqual(terminal.PrintLine('Review: %s' % self.joe, col.RED), + next(lines)) + self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines)) + self.assertEqual(terminal.PrintLine('', None), next(lines)) + self.assertEqual(terminal.PrintLine(' > File: file.c', col.MAGENTA), + next(lines)) + self.assertEqual(terminal.PrintLine(' > Some code', col.MAGENTA), + next(lines)) + self.assertEqual(terminal.PrintLine(' > and more code', col.MAGENTA), + next(lines)) + self.assertEqual(terminal.PrintLine( + ' Here is my comment above the above...', None), next(lines)) + self.assertEqual(terminal.PrintLine('', None), next(lines)) + + self.assertEqual(terminal.PrintLine(' 2 Subject 2', col.BLUE), + next(lines)) + self.assertEqual( + terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.fred, col.WHITE), + next(lines)) + self.assertEqual( + terminal.PrintLine(' + Reviewed-by: ', col.GREEN, newline=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.mary, col.WHITE), + next(lines)) + self.assertEqual( + terminal.PrintLine(' + Tested-by: ', col.GREEN, newline=False), + next(lines)) + self.assertEqual(terminal.PrintLine(self.leb, col.WHITE), + next(lines)) + + self.assertEqual(terminal.PrintLine('Review: %s' % self.fred, col.RED), + next(lines)) + self.assertEqual(terminal.PrintLine(' Hi Fred,', None), next(lines)) + self.assertEqual(terminal.PrintLine('', None), next(lines)) + self.assertEqual(terminal.PrintLine( + ' > File: tools/patman/commit.py', col.MAGENTA), next(lines)) + self.assertEqual(terminal.PrintLine( + ' > Line: 41 / 41: class Commit:', col.MAGENTA), next(lines)) + self.assertEqual(terminal.PrintLine( + ' > + return self.subject', col.MAGENTA), next(lines)) + self.assertEqual(terminal.PrintLine( + ' > +', col.MAGENTA), next(lines)) + self.assertEqual( + terminal.PrintLine(' > def AddChange(self, version, info):', + col.MAGENTA), + next(lines)) + self.assertEqual(terminal.PrintLine( + ' > """Add a new change line to the change list for a version.', + col.MAGENTA), next(lines)) + self.assertEqual(terminal.PrintLine( + ' >', col.MAGENTA), next(lines)) + self.assertEqual(terminal.PrintLine( + ' A comment', None), next(lines)) + self.assertEqual(terminal.PrintLine('', None), next(lines)) + + self.assertEqual(terminal.PrintLine( + '4 new responses available in patchwork (use -d to write them to a new branch)', + None), next(lines)) -- cgit v1.2.3