"""A ReplayGain re-level rewrites a track's tags without changing its size or its mtime, which is precisely the pair rsync's quick check compares. These cover the list that is fed back to rsync to copy those tracks anyway.""" import io import subprocess import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "tools")) import touched_albums # noqa: E402 def album(root, name, *tracks): directory = root / name directory.mkdir(parents=True, exist_ok=True) for track in tracks: (directory / track).write_bytes(b"x") return directory def listing(mirror, *lines): return touched_albums.remaining(mirror, *touched_albums.touched(list(lines))) def test_the_siblings_of_a_new_track_are_listed(tmp_path): album(tmp_path, "Artist/Album", "01.mp3", "02.mp3", "03.mp3") assert listing(tmp_path, "4096 Artist/Album/03.mp3") == [ "Artist/Album/01.mp3", "Artist/Album/02.mp3", ] def test_a_track_rsync_just_copied_is_not_copied_twice(tmp_path): """A quality upgrade replaces every track on the record. Listing them again would send the album across twice.""" album(tmp_path, "Artist/Album", "01.mp3", "02.mp3") assert listing(tmp_path, "4096 Artist/Album/01.mp3", "4096 Artist/Album/02.mp3") == [] def test_a_deletion_relevels_what_is_left(tmp_path): album(tmp_path, "Artist/Album", "01.mp3", "02.mp3") assert listing(tmp_path, "deleting Artist/Album/03.mp3") == [ "Artist/Album/01.mp3", "Artist/Album/02.mp3", ] def test_an_album_deleted_outright_lists_nothing(tmp_path): assert listing(tmp_path, "deleting Artist/Gone/01.mp3") == [] def test_untouched_albums_are_left_alone(tmp_path): album(tmp_path, "Artist/Changed", "01.mp3", "02.mp3") album(tmp_path, "Artist/Quiet", "01.mp3", "02.mp3") assert listing(tmp_path, "4096 Artist/Changed/01.mp3") == ["Artist/Changed/02.mp3"] def test_a_replaced_cover_is_not_an_album_change(tmp_path): """Only a track can change an album's gains, and covers are replaced often enough that treating one as a re-level would copy records for nothing.""" album(tmp_path, "Artist/Album", "01.mp3", "02.mp3") assert listing(tmp_path, "17408 Artist/Album/cover.jpg") == [] def test_directories_are_not_mistaken_for_tracks(tmp_path): album(tmp_path, "Artist/Album", "01.mp3") assert listing(tmp_path, "4096 Artist/Album/", "deleting Artist/Old/") == [] def test_rsync_talking_to_the_operator_is_not_a_path(tmp_path): album(tmp_path, "Artist/Album", "01.mp3", "02.mp3") assert ( listing( tmp_path, "sending incremental file list", "", "sent 1,234 bytes received 56 bytes 2,580.00 bytes/sec", "total size is 7,890 speedup is 6.12", ) == [] ) def test_a_track_at_the_mirror_root_does_not_pull_in_the_whole_tree(tmp_path): """Nothing writes a mirror this way, but the directory of a root-level file is the root, and recursing from there would be the whole library.""" (tmp_path / "loose.mp3").write_bytes(b"x") (tmp_path / "other.mp3").write_bytes(b"x") album(tmp_path, "Artist/Album", "01.mp3") assert listing(tmp_path, "4096 loose.mp3") == ["other.mp3"] def test_the_paths_are_written_one_per_line(tmp_path): """They are fed straight back to rsync as --files-from.""" album(tmp_path, "Artist/Album", "01.mp3", "02.mp3") out = io.StringIO() touched_albums.main( ["--mirror", str(tmp_path)], stream=["4096 Artist/Album/01.mp3"], out=out, ) assert out.getvalue() == "Artist/Album/02.mp3\n" def test_it_runs_as_a_script(tmp_path): album(tmp_path, "Artist/Album", "01.mp3", "02.mp3") completed = subprocess.run( [sys.executable, touched_albums.__file__, "--mirror", str(tmp_path)], input="4096 Artist/Album/01.mp3\n", capture_output=True, text=True, ) assert completed.returncode == 0 assert completed.stdout == "Artist/Album/02.mp3\n"