From: Christian Brauner <brauner@kernel.org>
To: "Kernel.org Tools" <tools@kernel.org>
Cc: Konstantin Ryabitsev <konstantin@linuxfoundation.org>,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH RFC 08/11] review: test backward revision discovery
Date: Sat, 18 Jul 2026 00:37:44 +0200 [thread overview]
Message-ID: <20260718-work-b4-multiver-rows-v1-8-3c539d2a3095@kernel.org> (raw)
In-Reply-To: <20260718-work-b4-multiver-rows-v1-0-3c539d2a3095@kernel.org>
Cover discover_older_revisions() (records all previous versions with
an explicit wantvers, offline error, v1 no-op, nothing-new short
circuit without polling) and the tracker action wiring (menu entry
presence, worker invocation with the selected series).
Assisted-by: LLM
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
src/tests/test_review_tracking.py | 118 ++++++++++++++++++++++++++++++++++++++
src/tests/test_tui_tracking.py | 44 ++++++++++++++
2 files changed, 162 insertions(+)
diff --git a/src/tests/test_review_tracking.py b/src/tests/test_review_tracking.py
index 5002aaa..b26933d 100644
--- a/src/tests/test_review_tracking.py
+++ b/src/tests/test_review_tracking.py
@@ -4895,3 +4895,121 @@ class TestRevisionAwareSyncHelpers:
).fetchone()[0]
conn.close()
assert rev_seen == 6
+
+
+def _series_msgs(base: str, rev: int, n: int = 2) -> list[EmailMessage]:
+ """Raw messages forming one n-patch series at the given revision."""
+ msgs = []
+ for i in range(1, n + 1):
+ msg = EmailMessage()
+ msg['Subject'] = f'[PATCH v{rev} {i}/{n}] {base}: part {i}'
+ msg['From'] = 'Author <author@example.com>'
+ msg['Date'] = 'Thu, 19 Mar 2026 08:51:12 +0530'
+ msg['Message-Id'] = f'<{base}-v{rev}-p{i}@example.com>'
+ msg.set_payload(_pos_diff(i))
+ msgs.append(msg)
+ return msgs
+
+
+class TestDiscoverOlderRevisions:
+ """The backward lore search records all previous versions."""
+
+ def test_records_all_previous_versions(
+ self, tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
+ conn = review_tracking.init_db('disc-all')
+ review_tracking.add_revision(conn, 'cid', 3, 'thing-v3-p1@example.com')
+ conn.close()
+ monkeypatch.setattr(b4, 'can_network', True)
+ monkeypatch.setattr(
+ review_tracking, '_fetch_thread_mbox_bytes', lambda msgid: b'raw'
+ )
+ base_msgs = _series_msgs('thing', 3)
+ monkeypatch.setattr(
+ b4, 'split_and_dedupe_pi_results', lambda raw: list(base_msgs)
+ )
+ seen_calls: list[tuple[int, Any]] = []
+
+ def _fake_extra(
+ msgs: list[EmailMessage],
+ direction: int = 1,
+ wantvers: Any = None,
+ nocache: bool = False,
+ ) -> list[EmailMessage]:
+ seen_calls.append((direction, wantvers))
+ return list(msgs) + _series_msgs('thing', 2) + _series_msgs('thing', 1)
+
+ monkeypatch.setattr(b4.mbox, 'get_extra_series', _fake_extra)
+ polled: list[str] = []
+ monkeypatch.setattr(
+ review_tracking,
+ 'update_revision_message_counts',
+ lambda identifier, series_list, topdir=None: polled.append(
+ series_list[0]['change_id']
+ ),
+ )
+ series = {
+ 'change_id': 'cid',
+ 'revision': 3,
+ 'message_id': 'thing-v3-p1@example.com',
+ 'status': 'new',
+ }
+ result = review_tracking.discover_older_revisions('disc-all', series, '')
+ assert result == {'found': 2, 'revisions': [1, 2], 'error': None}
+ assert seen_calls == [(-1, [1, 2])]
+ assert polled == ['cid']
+ conn = review_tracking.get_db('disc-all')
+ revs = {r['revision'] for r in review_tracking.get_revisions(conn, 'cid')}
+ conn.close()
+ assert revs == {1, 2, 3}
+
+ def test_offline_reports_error(self, tmp_path: pytest.TempPathFactory) -> None:
+ review_tracking.init_db('disc-off').close()
+ series = {'change_id': 'cid', 'revision': 3, 'message_id': 'v3@x'}
+ result = review_tracking.discover_older_revisions('disc-off', series, '')
+ assert result == {'found': 0, 'revisions': [], 'error': 'offline'}
+
+ def test_v1_is_noop(
+ self, tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
+ review_tracking.init_db('disc-v1').close()
+
+ def _boom(msgid: str) -> bytes:
+ raise AssertionError('must not fetch for v1')
+
+ monkeypatch.setattr(review_tracking, '_fetch_thread_mbox_bytes', _boom)
+ series = {'change_id': 'cid', 'revision': 1, 'message_id': 'v1@x'}
+ result = review_tracking.discover_older_revisions('disc-v1', series, '')
+ assert result == {'found': 0, 'revisions': [], 'error': None}
+
+ def test_nothing_new_found(
+ self, tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
+ conn = review_tracking.init_db('disc-none')
+ review_tracking.add_revision(conn, 'cid', 3, 'thing-v3-p1@example.com')
+ conn.close()
+ monkeypatch.setattr(b4, 'can_network', True)
+ monkeypatch.setattr(
+ review_tracking, '_fetch_thread_mbox_bytes', lambda msgid: b'raw'
+ )
+ base_msgs = _series_msgs('thing', 3)
+ monkeypatch.setattr(
+ b4, 'split_and_dedupe_pi_results', lambda raw: list(base_msgs)
+ )
+ monkeypatch.setattr(
+ b4.mbox,
+ 'get_extra_series',
+ lambda msgs, direction=1, wantvers=None, nocache=False: list(msgs),
+ )
+
+ def _no_poll(*args: Any, **kw: Any) -> None:
+ raise AssertionError('poller must not run when nothing was found')
+
+ monkeypatch.setattr(review_tracking, 'update_revision_message_counts', _no_poll)
+ series = {
+ 'change_id': 'cid',
+ 'revision': 3,
+ 'message_id': 'thing-v3-p1@example.com',
+ }
+ result = review_tracking.discover_older_revisions('disc-none', series, '')
+ assert result == {'found': 0, 'revisions': [], 'error': None}
diff --git a/src/tests/test_tui_tracking.py b/src/tests/test_tui_tracking.py
index eb1105c..e9d646e 100644
--- a/src/tests/test_tui_tracking.py
+++ b/src/tests/test_tui_tracking.py
@@ -4137,3 +4137,47 @@ class TestDoTakeMergeConflict:
common_dir = b4.git_get_common_dir(gitdir)
assert common_dir is not None
assert not os.path.isdir(os.path.join(common_dir, 'b4-take-worktree'))
+
+
+class TestDiscoverOlderAction:
+ """The 'Find older revisions' action wires into the discovery seam."""
+
+ @pytest.mark.asyncio
+ async def test_action_menu_offers_discover(self, tmp_path: pathlib.Path) -> None:
+ _seed_db(
+ 'test-discover-menu',
+ [{'change_id': 'cid-d', 'revision': 3, 'status': 'new'}],
+ )
+ app = TrackingApp('test-discover-menu')
+ async with app.run_test(size=(120, 30)) as pilot:
+ await pilot.pause()
+ await pilot.press('a')
+ await pilot.pause()
+ keys = [key for key, _label in app.screen._actions]
+ assert 'discover' in keys
+
+ @pytest.mark.asyncio
+ async def test_action_runs_discovery(self, tmp_path: pathlib.Path) -> None:
+ _seed_db(
+ 'test-discover-run',
+ [{'change_id': 'cid-d', 'revision': 3, 'status': 'new'}],
+ )
+ calls: List[Tuple[str, Optional[str]]] = []
+
+ def _fake_discover(
+ identifier: str,
+ series: Dict[str, Any],
+ linkmask: str,
+ topdir: Optional[str] = None,
+ ) -> Dict[str, Any]:
+ calls.append((identifier, series.get('change_id')))
+ return {'found': 2, 'revisions': [1, 2], 'error': None}
+
+ app = TrackingApp('test-discover-run')
+ with patch.object(tracking, 'discover_older_revisions', _fake_discover):
+ async with app.run_test(size=(120, 30)) as pilot:
+ await pilot.pause()
+ app.action_discover_older()
+ await app.workers.wait_for_complete()
+ await pilot.pause()
+ assert calls == [('test-discover-run', 'cid-d')]
--
2.53.0
next prev parent reply other threads:[~2026-07-17 22:38 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 22:37 [PATCH RFC 00/11] review: track and browse every version of a tracked series Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 01/11] review-tui: fix rethreaded series thread viewing Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 02/11] review: track message counts for all revisions of a series Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 03/11] review: test per-revision message tracking Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 04/11] review-tui: poll every revision on u/U updates Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 05/11] review-tui: guarantee the tracked revision in revision lists Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 06/11] review: add backward discovery of older series revisions Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 07/11] review-tui: add a "Find older revisions" action Christian Brauner
2026-07-17 22:37 ` Christian Brauner [this message]
2026-07-17 22:37 ` [PATCH RFC 09/11] review-tui: extract the Msgs column renderer from TrackedSeriesItem Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 10/11] review-tui: expand tracked series into per-version rows Christian Brauner
2026-07-17 22:37 ` [PATCH RFC 11/11] review-tui: test per-version tracker rows Christian Brauner
2026-07-27 20:43 ` [PATCH RFC 00/11] review: track and browse every version of a tracked series Konstantin Ryabitsev
2026-07-27 21:27 ` Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260718-work-b4-multiver-rows-v1-8-3c539d2a3095@kernel.org \
--to=brauner@kernel.org \
--cc=konstantin@linuxfoundation.org \
--cc=tools@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox