test: prove the database lands at the device root, not in the music folder #10

Merged
lyrathorpe merged 4 commits from feat/database-in-sync into main 2026-08-26 20:38:53 +01:00
Showing only changes of commit 633fbbaf91 - Show all commits
+64
View File
@@ -271,3 +271,67 @@ def test_the_scan_reads_from_the_mirror_not_the_device():
assert 'ln -s "$mirror"' in script
assert 'cd "$scratch"' in script
def can_bind_mount():
"""User namespaces let an unprivileged process bind mount. Not everywhere,
notably not inside some containers, so the test that needs it skips."""
return (
subprocess.run(
["unshare", "-Umr", "true"], capture_output=True, check=False
).returncode
== 0
)
@pytest.mark.skipif(not can_bind_mount(), reason="needs unprivileged user namespaces")
def test_the_database_lands_at_the_device_root_not_the_music_folder(tmp_path):
"""The two tools disagree about where the root is. rsync copies artist
folders into <device>/Music; the database tool must run one level up, where
.rockbox lives, and must record /Music/... paths while reading the bytes
from the mirror. device_prefix is what reconciles them.
"""
mirror = tmp_path / "mirror" / "Pendulum" / "Immersion"
mirror.mkdir(parents=True)
(mirror / "01.mp3").write_bytes(b"not really an mp3")
card = tmp_path / "card"
(card / ".rockbox").mkdir(parents=True)
(card / "Music").mkdir()
device = tmp_path / "device"
device.mkdir()
tool = tmp_path / "fake-database"
# Records where it was run and what it could see, which is the whole
# question; producing a real database needs Rockbox's builder.
tool.write_text(
"#!/bin/sh\n"
"printf '%s\\n' \"$PWD\" > .rockbox/where.txt\n"
"ls Music/ > .rockbox/saw.txt\n"
"echo db > .rockbox/database_0.tcd\n"
)
tool.chmod(0o755)
script = (
f"mount --bind {card} {device} && "
f"XDG_CACHE_HOME={tmp_path / 'cache'} MUSIC_MIRROR_DATABASE_TOOL={tool} "
f"bash {SCRIPT} -f -S -U -Q {tmp_path / 'mirror'} {device / 'Music'}"
)
result = subprocess.run(
["unshare", "-Umr", "sh", "-c", script], capture_output=True, text=True
)
assert result.returncode == 0, result.stderr
# The database lands beside the device root, not inside Music.
assert (card / ".rockbox" / "database_0.tcd").is_file()
# Only *.tcd is copied across, so the markers stay in the scratch root --
# which is itself the point: nothing else is written to the device.
scratch = tmp_path / "cache" / "music-mirror" / "database" / ".rockbox"
assert not (card / ".rockbox" / "where.txt").exists()
# It ran in the scratch root, not on the card.
where = (scratch / "where.txt").read_text().strip()
assert where.endswith("music-mirror/database"), where
# ...and could walk into the mirror through a symlink named for the device
# prefix, which is how the paths come out as /Music/... while the bytes are
# read from somewhere else entirely.
assert "Pendulum" in (scratch / "saw.txt").read_text()