test: prove the database lands at the device root, not in the music folder #10
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user