Instead of passing the URL and function to each call, put the fake into the Patchwork object instead.
Signed-off-by: Simon Glass <s...@chromium.org> --- tools/patman/control.py | 4 ++- tools/patman/func_test.py | 41 +++++++++++++-------------- tools/patman/patchwork.py | 26 ++++++++++++++++++ tools/patman/status.py | 58 ++++++++++----------------------------- 4 files changed, 62 insertions(+), 67 deletions(-) diff --git a/tools/patman/control.py b/tools/patman/control.py index 06a9dfd2bca..cb8552ed550 100644 --- a/tools/patman/control.py +++ b/tools/patman/control.py @@ -24,6 +24,7 @@ from u_boot_pylib import terminal from u_boot_pylib import tools from patman import checkpatch from patman import patchstream +from patman import patchwork from patman import send @@ -95,12 +96,13 @@ def patchwork_status(branch, count, start, end, dest_branch, force, # Allow the series to override the URL if 'patchwork_url' in series: url = series.patchwork_url + pwork = patchwork.Patchwork(url) # Import this here to avoid failing on other commands if the dependencies # are not present from patman import status status.check_and_show_status(series, found[0], branch, dest_branch, force, - show_comments, url) + show_comments, pwork) def do_patman(args): diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index ea96c508fa2..9c5e7d7dd51 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -6,6 +6,7 @@ """Functional tests for checking that patman behaves correctly""" +import asyncio import contextlib import os import pathlib @@ -767,14 +768,13 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c os.chdir(orig_dir) @staticmethod - def _fake_patchwork(url, subpath): + def _fake_patchwork(subpath): """Fake Patchwork server for the function below This handles accessing a series, providing a list consisting of a single patch Args: - url (str): URL of patchwork server subpath (str): URL subpath to use """ re_series = re.match(r'series/(\d*)/$', subpath) @@ -787,21 +787,17 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c def test_status_mismatch(self): """Test Patchwork patches not matching the series""" - series = Series() - + pwork = patchwork.Patchwork.for_testing(self._fake_patchwork) with terminal.capture() as (_, err): - patches = status.collect_patches(1234, None, self._fake_patchwork) + patches = status.collect_patches(1234, pwork) status.check_patch_count(0, len(patches)) self.assertIn('Warning: Patchwork reports 1 patches, series has 0', err.getvalue()) def test_status_read_patch(self): """Test handling a single patch in Patchwork""" - series = Series() - series.commits = [Commit('abcd')] - - patches = status.collect_patches(1234, None, - self._fake_patchwork) + pwork = patchwork.Patchwork.for_testing(self._fake_patchwork) + patches = status.collect_patches(1234, pwork) self.assertEqual(1, len(patches)) patch = patches[0] self.assertEqual('1', patch.id) @@ -944,14 +940,13 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c "Cannot find commit for patch 3 ('Subject 2')"], warnings) - def _fake_patchwork2(self, url, subpath): + 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 Args: - url (str): URL of patchwork server subpath (str): URL subpath to use """ re_series = re.match(r'series/(\d*)/$', subpath) @@ -1007,13 +1002,14 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c review_list = [None, None] # Check that the tags are picked up on the first patch + pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2) status.find_new_responses(new_rtag_list, review_list, 0, commit1, - patch1, None, self._fake_patchwork2) + patch1, pwork) self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}}) # Now the second patch status.find_new_responses(new_rtag_list, review_list, 1, commit2, - patch2, None, self._fake_patchwork2) + patch2, pwork) self.assertEqual(new_rtag_list[1], { 'Reviewed-by': {self.mary, self.fred}, 'Tested-by': {self.leb}}) @@ -1023,7 +1019,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c new_rtag_list = [None] * count commit1.rtags = {'Reviewed-by': {self.joe}} status.find_new_responses(new_rtag_list, review_list, 0, commit1, - patch1, None, self._fake_patchwork2) + patch1, pwork) self.assertEqual(new_rtag_list[0], {}) # For the second commit, add Ed and Fred, so only Mary should be left @@ -1031,7 +1027,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c 'Tested-by': {self.leb}, 'Reviewed-by': {self.fred}} status.find_new_responses(new_rtag_list, review_list, 1, commit2, - patch2, None, self._fake_patchwork2) + patch2, pwork) self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}}) # Check that the output patches expectations: @@ -1046,8 +1042,9 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c series = Series() series.commits = [commit1, commit2] terminal.set_print_test_mode() + pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2) status.check_and_show_status(series, '1234', None, None, False, False, - None, self._fake_patchwork2) + pwork) lines = iter(terminal.get_print_test_lines()) col = terminal.Color() self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), @@ -1082,14 +1079,13 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c '1 new response available in patchwork (use -d to write them to a new branch)', None), next(lines)) - def _fake_patchwork3(self, url, subpath): + 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 Args: - url (str): URL of patchwork server subpath (str): URL subpath to use """ re_series = re.match(r'series/(\d*)/$', subpath) @@ -1160,9 +1156,9 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c # <unittest.result.TestResult run=8 errors=0 failures=0> terminal.set_print_test_mode() + pwork = patchwork.Patchwork.for_testing(self._fake_patchwork3) status.check_and_show_status(series, '1234', branch, dest_branch, - False, False, None, self._fake_patchwork3, - repo) + False, False, pwork, repo) lines = terminal.get_print_test_lines() self.assertEqual(12, len(lines)) self.assertEqual( @@ -1362,8 +1358,9 @@ Reviewed-by: %s series = Series() series.commits = [commit1, commit2] terminal.set_print_test_mode() + pwork = patchwork.Patchwork.for_testing(self._fake_patchwork2) status.check_and_show_status(series, '1234', None, None, False, True, - None, self._fake_patchwork2) + pwork) lines = iter(terminal.get_print_test_lines()) col = terminal.Color() self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), diff --git a/tools/patman/patchwork.py b/tools/patman/patchwork.py index e0adbdb6481..3ec266f073e 100644 --- a/tools/patman/patchwork.py +++ b/tools/patman/patchwork.py @@ -139,6 +139,7 @@ class Patchwork: 'https://patchwork.ozlabs.org' """ self.url = url + self.fake_request = None self.proj_id = None self.link_name = None self._show_progress = show_progress @@ -160,6 +161,8 @@ class Patchwork: """ # print('subpath', subpath) self.request_count += 1 + if self.fake_request: + return self.fake_request(subpath) full_url = f'{self.url}/api/1.2/{subpath}' async with self.semaphore: @@ -178,6 +181,29 @@ class Patchwork: if i == RETRIES: raise + async def session_request(self, subpath): + async with aiohttp.ClientSession() as client: + return await self._request(client, subpath) + + def request(self, subpath): + return asyncio.run(self.session_request(subpath)) + + @staticmethod + def for_testing(func): + """Get an instance to use for testing + + Args: + func (function): Function to call to handle requests. The function + is passed a URL and is expected to return a dict with the + resulting data + + Returns: + Patchwork: testing instance + """ + pwork = Patchwork(None, show_progress=False) + pwork.fake_request = func + return pwork + async def get_series(self, client, link): """Read information about a series diff --git a/tools/patman/status.py b/tools/patman/status.py index ed4cca6f724..5c75d7b10c6 100644 --- a/tools/patman/status.py +++ b/tools/patman/status.py @@ -134,26 +134,7 @@ def compare_with_series(series, patches): return patch_for_commit, commit_for_patch, warnings -def call_rest_api(url, subpath): - """Call the patchwork API and return the result as JSON - - Args: - url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org' - subpath (str): URL subpath to use - - Returns: - dict: Json result - - Raises: - ValueError: the URL could not be read - """ - full_url = '%s/api/1.2/%s' % (url, subpath) - response = requests.get(full_url) - if response.status_code != 200: - raise ValueError("Could not read URL '%s'" % full_url) - return response.json() - -def collect_patches(series_id, url, rest_api=call_rest_api): +def collect_patches(series_id, pwork): """Collect patch information about a series from patchwork Uses the Patchwork REST API to collect information provided by patchwork @@ -161,9 +142,7 @@ def collect_patches(series_id, url, rest_api=call_rest_api): Args: series_id (str): Patch series ID number - url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org' - rest_api (function): API function to call to access Patchwork, for - testing + pwork (Patchwork): Patchwork object to use for reading Returns: list of Patch: List of patches sorted by sequence number @@ -172,7 +151,7 @@ def collect_patches(series_id, url, rest_api=call_rest_api): ValueError: if the URL could not be read or the web page does not follow the expected structure """ - data = rest_api(url, 'series/%s/' % series_id) + data = pwork.request('series/%s/' % series_id) # Get all the rows, which are patches patch_dict = data['patches'] @@ -193,8 +172,7 @@ def collect_patches(series_id, url, rest_api=call_rest_api): patches = sorted(patches, key=lambda x: x.seq) return patches -def find_new_responses(new_rtag_list, review_list, seq, cmt, patch, url, - rest_api=call_rest_api): +def find_new_responses(new_rtag_list, review_list, seq, cmt, patch, pwork): """Find new rtags collected by patchwork that we don't know about This is designed to be run in parallel, once for each commit/patch @@ -211,16 +189,14 @@ def find_new_responses(new_rtag_list, review_list, seq, cmt, patch, url, seq (int): Position in new_rtag_list to update cmt (Commit): Commit object for this commit patch (Patch): Corresponding Patch object for this patch - url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org' - rest_api (function): API function to call to access Patchwork, for - testing + pwork (Patchwork): Patchwork object to use for reading """ if not patch: return # Get the content for the patch email itself as well as all comments - data = rest_api(url, 'patches/%s/' % patch.id) - comment_data = rest_api(url, 'patches/%s/comments/' % patch.id) + data = pwork.request('patches/%s/' % patch.id) + comment_data = pwork.request('patches/%s/comments/' % patch.id) new_rtags, reviews = process_reviews(data['content'], comment_data, cmt.rtags) @@ -316,7 +292,7 @@ def create_branch(series, new_rtag_list, branch, dest_branch, overwrite, [parent.target]) return num_added -def check_status(series, series_id, url, rest_api=call_rest_api): +def check_status(series, series_id, pwork): """Check the status of a series on Patchwork This finds review tags and comments for a series in Patchwork, displaying @@ -325,9 +301,7 @@ def check_status(series, series_id, url, rest_api=call_rest_api): Args: series (Series): Series object for the existing branch series_id (str): Patch series ID number - url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org' - rest_api (function): API function to call to access Patchwork, for - testing + pwork (Patchwork): Patchwork object to use for reading Return: tuple: @@ -342,7 +316,7 @@ def check_status(series, series_id, url, rest_api=call_rest_api): list for each patch, each a: list of Review objects for the patch """ - patches = collect_patches(series_id, url, rest_api) + patches = collect_patches(series_id, pwork) count = len(series.commits) new_rtag_list = [None] * count review_list = [None] * count @@ -356,8 +330,7 @@ def check_status(series, series_id, url, rest_api=call_rest_api): with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor: futures = executor.map( find_new_responses, repeat(new_rtag_list), repeat(review_list), - range(count), series.commits, patch_list, repeat(url), - repeat(rest_api)) + range(count), series.commits, patch_list, repeat(pwork)) for fresponse in futures: if fresponse: raise fresponse.exception() @@ -445,8 +418,7 @@ def show_status(series, branch, dest_branch, force, patches, patch_for_commit, def check_and_show_status(series, link, branch, dest_branch, force, - show_comments, url, rest_api=call_rest_api, - test_repo=None): + show_comments, pwork, test_repo=None): """Read the series status from patchwork and show it to the user Args: @@ -456,12 +428,10 @@ def check_and_show_status(series, link, branch, dest_branch, force, dest_branch (str): Name of new branch to create, or None force (bool): True to force overwriting dest_branch if it exists show_comments (bool): True to show patch comments - url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org' - rest_api (function): API function to call to access Patchwork, for - testing + pwork (Patchwork): Patchwork object to use for reading test_repo (pygit2.Repository): Repo to use (use None unless testing) """ patches, patch_for_commit, new_rtag_list, review_list = check_status( - series, link, url, rest_api) + series, link, pwork) show_status(series, branch, dest_branch, force, patches, patch_for_commit, show_comments, new_rtag_list, review_list, test_repo) -- 2.43.0