Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d6d1df2
conftest.py and four test files: one helper asks whether a system can…
Oct 16, 2026
0b1d1dd
test_bootstrap_branch_bytes.py: the no-fault test's Windows guard is …
Oct 16, 2026
6ba8c5d
conftest.py and two test files: `skip_message` is held by a test that…
Oct 16, 2026
4184972
test_stdout_encoding.py, test_adopt_git_reads.py, test_adopt_template…
Oct 17, 2026
6b5e907
test_non_utf8_git_reads.py and test_non_utf8_name_callers.py: the `ma…
Oct 17, 2026
4cdb427
Merge branch 'main' into fix/663-non-utf8-name-tests
brettheap Oct 17, 2026
d427cb2
Merge #723's final head 4cdb427c (landed as bad05d2)
Oct 17, 2026
76ae641
Merge main bad05d21.. after #723 landed as bad05d2
Oct 17, 2026
b18b243
tests: three tests ask the helper whether a name can be made, `make_f…
Oct 17, 2026
efb1727
tests: the emulated disk answers every errno but EILSEQ, `make_file` …
Oct 17, 2026
9ba530a
tests: `raised_for_every_errno` passes the errno's label to the failu…
Oct 17, 2026
8f6a38e
tests: the sweep's own test builds its `Disk` before the `pytest.rais…
Oct 17, 2026
f62b3de
Merge branch 'main' into fix/757-non-utf8-test-dirs
brettheap Oct 17, 2026
a149863
Merge origin/main (`e62b5e17`) into fix/757-non-utf8-test-dirs: #818'…
Oct 17, 2026
22238b4
Merge branch 'main' into fix/757-non-utf8-test-dirs
brettheap Oct 18, 2026
e3bb920
Merge #806's final head 22238b47 (landed as d6071c7)
Oct 18, 2026
fc3cb5e
Merge #806's squash d6071c7 after it landed, taking the branch's side…
Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions tests/test_adopt_template_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,16 +246,25 @@ def test_plan_names_the_elector_it_took_from_git_config(tmp_path):
b"again")


def refused_then_moved(base: Path, parent: Path, reason: bytes) -> None:
def refused_then_moved(base: Path, parent: Path, reason: bytes,
skip_where_git_fails: bool = True) -> None:
"""A source in the directory `parent`: `plan` and `check` pass it, as
#247 made them, and `execute` refuses its path as the assembly root's
clone URL, giving `reason`, before any leg exists. Then the remediation
the refusal gives, followed as it is worded -- `parent` renamed, then
`plan --source <its new path>`, `check` and `execute` run again -- ends
`adoption verified`."""
`adoption verified`.

A source repository that git cannot make under `parent` skips the test
where `skip_where_git_fails` is true, for a name that is not UTF-8, which
git may not work under. A name that is valid text asks for it false, and
the failure is raised: the cause is the disk or git, never the name
(#834)."""
try:
source = make_source_repo(parent / "Thing")
except AssertionError as exc:
if not skip_where_git_fails:
raise
pytest.skip(f"git will not work under {parent.name!r} here: {exc}")
runs = plan_check_execute(base, source)
assert_no_traceback_and_no_leg(base, runs)
Expand Down Expand Up @@ -306,13 +315,15 @@ def test_execute_refuses_a_local_source_directory_holding_a_bidi_control(
"""A source under a directory whose name begins with U+202E RIGHT-TO-LEFT
OVERRIDE: on main `execute` adopted it, and the shape's README showed
the rest of its `git clone` line reversed. Now it is refused as the
byte is, and moving the source adopts."""
byte is, and moving the source adopts. Nothing here is guarded: U+202E
is valid text, which a POSIX disk, NTFS and APFS hold and git works
under (reasoned, not run, on the last two), so an error of the disk when
the directory is made, and a failure of git when the source is, are the
test's own and are raised, not skipped (#834)."""
parent = tmp_path / "\u202ecaf"
try:
parent.mkdir()
except OSError as exc:
pytest.skip(f"no directory here is named '\\u202ecaf': {exc!r}")
refused_then_moved(tmp_path, parent, b"a bidirectional control")
parent.mkdir()
refused_then_moved(tmp_path, parent, b"a bidirectional control",
skip_where_git_fails=False)


# --- `check` and `execute`: a plan value edited by hand ----------------------
Expand Down
13 changes: 7 additions & 6 deletions tests/test_bootstrap_leg_moves.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@

import pytest

from conftest import IDENTITY_CONFIG, git, run_script
from conftest import (IDENTITY_CONFIG, git, run_script,
skip_where_a_name_cannot_be_not_utf_8)
from test_bootstrap_after_bump import ( # noqa: F401
ADDED, EDITED, MAIN, NOTHING_ELSE, bootstrapped, bumped,
failing_post_checkout, head_ref, left_branch, moved_and_refused, rev,
Expand Down Expand Up @@ -546,11 +547,11 @@ def test_a_branch_whose_name_is_not_utf8_is_read_back_on_every_run(
the check of where `checkout -b` left HEAD (S3) reads HEAD back; the
second places the leg on it. Each read `symbolic-ref` decoded as UTF-8,
and on base the second run ended in a `UnicodeDecodeError` traceback.
Made on a disk that takes the byte, skipped on one that does not."""
try:
(tmp_path / "probe-\udcff").write_bytes(b"")
except (OSError, ValueError):
pytest.skip("this disk takes no file name with the byte 0xff")
Made on a system and a disk that take the byte, skipped on one that
does not: `skip_where_a_name_cannot_be_not_utf_8` asks. Any other
error of the disk, a full one or a read-only `tmp_path`, fails the
test (#834)."""
skip_where_a_name_cannot_be_not_utf_8(tmp_path)
leg = root / "spec"
pin = rev(leg)
git("checkout", "-q", "--detach", pin, cwd=leg)
Expand Down
10 changes: 4 additions & 6 deletions tests/test_cli_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@

import pytest

from conftest import REPO, WINDOWS_SKIP, git
from conftest import (REPO, WINDOWS_SKIP, git,
skip_where_a_name_cannot_be_not_utf_8)

REFERENCE = REPO / "docs" / "cli.md"
GENERATOR = REPO / "scripts" / "render-cli-reference.py"
Expand Down Expand Up @@ -642,12 +643,9 @@ def test_a_checkout_whose_path_ends_in_a_space_is_a_checkout(tmp_path):

@WINDOWS_SKIP
def test_a_checkout_whose_path_is_not_utf8_is_a_checkout(tmp_path):
skip_where_a_name_cannot_be_not_utf_8(tmp_path)
parent = tmp_path / os.fsdecode(b"bad\xffname")
try:
parent.mkdir()
except OSError as error:
pytest.skip(f"this file system cannot hold a name that is not UTF-8: "
f"{error}")
parent.mkdir()
tree = a_checkout_at(parent / "openRepoShape")
with pytest.raises(UnicodeDecodeError):
os.fsencode(tree).decode("utf-8")
Expand Down
16 changes: 8 additions & 8 deletions tests/test_doctor_validator_verdicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@

import pytest

from conftest import WINDOWS_SKIP, clone_just_pushed, run_script
from conftest import (WINDOWS_SKIP, clone_just_pushed, run_script,
skip_where_a_name_cannot_be_not_utf_8)
#: The layer stand-in a chain descendant pins, from the module that scaffolds
#: one: a second way of building it would test a different one.
from test_scaffold_pin_and_reuse import layer_repo
Expand Down Expand Up @@ -542,16 +543,15 @@ def test_a_name_that_is_not_utf8_is_never_an_exception_quoted_as_a_finding(
today the row is the crash row; a script that prints such a name will
make it the `naming-unclassified` FINDING, and either is a true row --
what this test holds is that the exception is never the finding.
`WINDOWS_SKIP`: Windows has no name that is not UTF-8, and a disk that
refuses one (APFS) skips.
`WINDOWS_SKIP`: Windows has no name that is not UTF-8. A system or a
disk that cannot carry one (APFS's EILSEQ) skips, asked of
`skip_where_a_name_cannot_be_not_utf_8`; any other error of the disk,
a full one or a read-only `tmp_path`, fails the test (#834).
"""
skip_where_a_name_cannot_be_not_utf_8(tmp_path)
name = os.fsdecode(b"Atl\xffas")
here = tmp_path / "elsewhere" / name
try:
here.mkdir(parents=True)
except OSError as exc:
pytest.skip(f"this filesystem refuses a name that is not UTF-8: "
f"{exc}")
here.mkdir(parents=True)
result = doctor(standard, here, "--json")
payload = payload_of(result)
row = rows_of(result)["naming"]
Expand Down
21 changes: 17 additions & 4 deletions tests/test_non_utf8_git_reads.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

from __future__ import annotations

import errno
import os
import shutil
import subprocess
Expand Down Expand Up @@ -94,13 +95,25 @@ def make_file(path: Path, data: bytes = b"on: [push]\n") -> None:
system that carries the byte and a filesystem that stores it (Linux does;
APFS refuses it, and on Windows `os.fsencode` makes no byte of it, which
is asked FIRST: NTFS most likely takes the surrogate, and the file would
then not be the one the test means). A name that is UTF-8 is written."""
then not be the one the test means). A name that is UTF-8 is written.

Two answers are the system's, each skipped on a reason of its own: a
file name encoding that cannot spell the name (`UnicodeError`: ASCII
under `LC_ALL=C`) and a disk that refuses the name (EILSEQ: macOS).
Every other `OSError`, a full disk (ENOSPC), a read-only one (EROFS)
or a folder nobody can write in (EACCES), is the test's own and is
raised (#834)."""
skip_where_fsencode_gives_a_surrogate_no_byte(path.name)
try:
path.write_bytes(data)
except (OSError, UnicodeError) as exc:
pytest.skip("this filesystem cannot hold a file name that is not "
f"UTF-8: {exc}")
except UnicodeError as exc:
pytest.skip("this system's file name encoding cannot spell "
f"{path.name!r}: {exc}")
except OSError as exc:
if exc.errno != errno.EILSEQ:
raise
pytest.skip("this filesystem refuses a file name that is not "
f"UTF-8 (EILSEQ): {exc}")


def test_make_file_skips_where_fsencode_makes_no_byte_and_writes_nothing(
Expand Down
18 changes: 11 additions & 7 deletions tests/test_non_utf8_name_callers.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,14 @@ def mkdir(path, *args, **kwargs):
return mkdir


def fail_where_the_caller_ends(call, *args) -> None:
def fail_where_the_caller_ends(call, *args, said: str = "") -> None:
"""Fail where `call(*args)` ends without an error, SKIPPED or not: the
caller was to raise. A `pytest.raises` block may hold one call and no
more (python:S5778), so the failure is made here and not beside it."""
more (python:S5778), so the failure is made here and not beside it.
`said` goes in front of the message, for a caller that asks the same
question many times and says which one failed (#834)."""
reason = skip_message(call, *args)
pytest.fail("the caller ended "
pytest.fail(said + "the caller ended "
+ (f"SKIPPED: {reason}" if reason else "without an error"))


Expand Down Expand Up @@ -279,17 +281,19 @@ def test_make_files_utf8_name_test_skips_where_the_encoding_is_ascii(
def test_a_write_the_disk_refuses_still_fails_the_make_file_test(
tmp_path, monkeypatch, code):
"""The name is plain UTF-8, so a refused write is the disk's and not the
encoding's: the test FAILS on its assert, as it does without this change.
A skip on `make_file`'s own "filesystem" reason would end it SKIPPED, and
no run fails on that."""
encoding's: `make_file` raises it (#834), and the test FAILS on it. A
skip on `make_file`'s own "filesystem" reason would end it SKIPPED, and
no run fails on that; before #834 it did skip, and the test failed on
its assert that the call is not a skip."""
non_utf8_git_reads.skip_where_the_environment_cannot_carry(
non_utf8_git_reads.UTF8)

def write_bytes(self, data):
raise OSError(code, os.strerror(code), str(self))
monkeypatch.setattr(Path, "write_bytes", write_bytes)
with pytest.raises(AssertionError):
with pytest.raises(OSError) as raised:
fail_where_the_caller_ends(MAKE_FILE_TEST, tmp_path, monkeypatch)
assert raised.value.errno == code


@pytest.mark.parametrize("value", ["Jos\u00e9", "Mehr\u200cdad", "\u202eevil"],
Expand Down
Loading
Loading