[PATCH b4 v2 16/44] tests: cover the opt-in branch guard in edit_in_editor
Christian Brauner <[email protected]> Fri, 31 Jul 2026 23:58:57 +0200
| Newsgroups | org.kernel.linux.tools |
|---|---|
| Message-ID | <20260731-work-b4-editor-branch-guard-v2-16-243fd19d322d@kernel.org> |
A caller that does not opt in keeps its text across a branch switch. One that does is refused and the text is kept in a temporary file. Signed-off-by: Christian Brauner (Amutable) <[email protected]> --- src/tests/test___init__.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/tests/test___init__.py b/src/tests/test___init__.py index b02ed12..d9addac 100644 --- a/src/tests/test___init__.py +++ b/src/tests/test___init__.py @@ -1234,3 +1234,67 @@ def test_git_run_command_log_fixup_looks_past_option_prefix(gitdir: str) -> None assert out.startswith('commit '), out sha = out.split('\n', 1)[0].split()[1] assert len(sha) == 40, f'log abbreviated the sha despite the fixup: {sha}' + + +def _fake_editor(tmp_path: pathlib.Path, body: str) -> str: + """A stand-in $EDITOR that runs *body* and leaves the buffer alone.""" + script = tmp_path / 'fake-editor.sh' + script.write_text(f'#!/bin/sh\n{body}\n') + script.chmod(0o755) + return str(script) + + +def test_edit_in_editor_without_guard_survives_branch_switch( + gitdir: str, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Callers that write to an explicit ref get their text back even if HEAD + moved while the editor was open. + + The review TUI stores replies on the review branch and reads the patch it + is replying to by SHA, so a branch switch -- its own, or the user's in + another terminal sharing the worktree -- is none of its business.""" + monkeypatch.setenv( + 'GIT_EDITOR', _fake_editor(tmp_path, f'git -C "{gitdir}" checkout -q -b side') + ) + assert b4.edit_in_editor(b'my reply\n', filehint='reply.eml') == b'my reply\n' + assert b4.git_get_current_branch(gitdir) == 'side' + + +def test_edit_in_editor_guard_refuses_branch_switch( + gitdir: str, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A caller that opts in is refused when HEAD has moved on, and its text + is preserved in a temporary file.""" + monkeypatch.setenv( + 'GIT_EDITOR', _fake_editor(tmp_path, f'git -C "{gitdir}" checkout -q -b side') + ) + with pytest.raises(RuntimeError, match='Branch changed during file editing') as ex: + b4.edit_in_editor(b'my cover\n', guard_branch=True) + + saved = pathlib.Path(str(ex.value).split(' saved at ')[-1]) + try: + assert saved.read_bytes() == b'my cover\n' + finally: + saved.unlink() + + +def test_edit_in_editor_guard_covers_a_detached_head( + gitdir: str, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Starting detached is still a starting point worth guarding. + + 'b4 trailers -u' does not require a prep branch, so it can run with HEAD + detached and rewrite whatever branch is current when it applies.""" + ecode, out = b4.git_run_command(gitdir, ['checkout', '-q', '--detach']) + assert ecode == 0, out + monkeypatch.setenv( + 'GIT_EDITOR', _fake_editor(tmp_path, f'git -C "{gitdir}" checkout -q -b side') + ) + with pytest.raises(RuntimeError, match='Branch changed during file editing') as ex: + b4.edit_in_editor(b'my trailers\n', guard_branch=True) + + saved = pathlib.Path(str(ex.value).split(' saved at ')[-1]) + try: + assert saved.read_bytes() == b'my trailers\n' + finally: + saved.unlink() -- 2.53.0