diff --git a/conftest.py b/conftest.py index cd41a03..15ace27 100644 --- a/conftest.py +++ b/conftest.py @@ -23,8 +23,8 @@ def isolated_dir() -> Iterator[Path]: def config(isolated_dir: Path) -> Config: cache_dir = isolated_dir / "cache" cache_dir.mkdir() - cache_database_path = cache_dir / "cache.sqlite3" + cache_database_path = cache_dir / "cache.sqlite3" with sqlite3.connect(cache_database_path) as conn: with SCHEMA_PATH.open("r") as fp: conn.executescript(fp.read()) @@ -33,14 +33,79 @@ def config(isolated_dir: Path) -> Config: latest_schema_hash = hashlib.sha256(fp.read()).hexdigest() conn.execute("INSERT INTO _schema_hash (value) VALUES (?)", (latest_schema_hash,)) + music_source_dir = isolated_dir / "source" + music_source_dir.mkdir() + + mount_dir = isolated_dir / "mount" + mount_dir.mkdir() + return Config( - music_source_dir=isolated_dir / "source", - fuse_mount_dir=isolated_dir / "mount", + music_source_dir=music_source_dir, + fuse_mount_dir=mount_dir, cache_dir=cache_dir, cache_database_path=cache_database_path, ) +@pytest.fixture() +def seeded_cache(config: Config) -> Iterator[None]: + dirpaths = [ + config.music_source_dir / "r1", + config.music_source_dir / "r2", + ] + filepaths = [ + config.music_source_dir / "r1" / "01.m4a", + config.music_source_dir / "r1" / "02.m4a", + config.music_source_dir / "r2" / "01.m4a", + ] + + with sqlite3.connect(config.cache_database_path) as conn: + conn.executescript( + f"""\ +INSERT INTO releases (id, source_path, virtual_dirname, title, release_type, release_year, new) +VALUES ('r1', '{dirpaths[0]}', 'r1', 'Release 1', 'album', 2023, true) + , ('r2', '{dirpaths[1]}', 'r2', 'Release 2', 'album', 2021, false); + +INSERT INTO releases_genres (release_id, genre, genre_sanitized) +VALUES ('r1', 'Techno', 'Techno') + , ('r1', 'Deep House', 'Deep House') + , ('r2', 'Classical', 'Classical'); + +INSERT INTO releases_labels (release_id, label, label_sanitized) +VALUES ('r1', 'Silk Music', 'Silk Music') + , ('r2', 'Native State', 'Native State'); + +INSERT INTO tracks +(id, source_path, virtual_filename, title, release_id, track_number, disc_number, duration_seconds) +VALUES ('t1', '{filepaths[0]}', '01.m4a', 'Track 1', 'r1', '01', '01', 120) + , ('t2', '{filepaths[1]}', '02.m4a', 'Track 2', 'r1', '02', '01', 240) + , ('t3', '{filepaths[2]}', '01.m4a', 'Track 1', 'r2', '01', '01', 120); + +INSERT INTO releases_artists (release_id, artist, artist_sanitized, role) +VALUES ('r1', 'Techno Man', 'Techno Man', 'main') + , ('r1', 'Bass Man', 'Bass Man', 'main') + , ('r2', 'Violin Woman', 'Violin Woman', 'main') + , ('r2', 'Conductor Woman', 'Conductor Woman', 'guest'); + +INSERT INTO tracks_artists (track_id, artist, artist_sanitized, role) +VALUES ('t1', 'Techno Man', 'Techno Man', 'main') + , ('t1', 'Bass Man', 'Bass Man', 'main') + , ('t2', 'Techno Man', 'Techno Man', 'main') + , ('t2', 'Bass Man', 'Bass Man', 'main') + , ('t3', 'Violin Woman', 'Violin Woman', 'main') + , ('t3', 'Conductor Woman', 'Conductor Woman', 'guest'); + """ + ) + + for d in dirpaths: + d.mkdir() + for f in filepaths: + f.touch() + + yield + return None + + def freeze_database_time(conn: sqlite3.Connection) -> None: """ This function freezes the CURRENT_TIMESTAMP function in SQLite3 to diff --git a/pyproject.toml b/pyproject.toml index 8628a7f..bf0da66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,9 @@ explicit_package_bases = true module = "fuse" ignore_missing_imports = true [[tool.mypy.overrides]] +module = "pytest_cov.embed" +ignore_missing_imports = true +[[tool.mypy.overrides]] module = "setuptools" ignore_missing_imports = true diff --git a/rose/cache/read_test.py b/rose/cache/read_test.py index b7edae6..46c4bdc 100644 --- a/rose/cache/read_test.py +++ b/rose/cache/read_test.py @@ -1,6 +1,7 @@ from pathlib import Path -from rose.cache.database import connect +import pytest + from rose.cache.dataclasses import CachedArtist, CachedRelease, CachedTrack from rose.cache.read import ( artist_exists, @@ -17,53 +18,13 @@ from rose.foundation.conf import Config -def seed_data(c: Config) -> None: - with connect(c) as conn: - conn.executescript( - """\ -INSERT INTO releases (id, source_path, virtual_dirname, title, release_type, release_year, new) -VALUES ('r1', '/tmp/r1', 'r1', 'Release 1', 'album', 2023, true) - , ('r2', '/tmp/r2', 'r2', 'Release 2', 'album', 2021, false); - -INSERT INTO releases_genres (release_id, genre, genre_sanitized) -VALUES ('r1', 'Techno', 'Techno') - , ('r1', 'Deep House', 'Deep House') - , ('r2', 'Classical', 'Classical'); - -INSERT INTO releases_labels (release_id, label, label_sanitized) -VALUES ('r1', 'Silk Music', 'Silk Music') - , ('r2', 'Native State', 'Native State'); - -INSERT INTO tracks -(id, source_path, virtual_filename, title, release_id, track_number, disc_number, duration_seconds) -VALUES ('t1', '/tmp/r1/01.m4a', '01.m4a', 'Track 1', 'r1', '01', '01', 120) - , ('t2', '/tmp/r1/02.m4a', '02.m4a', 'Track 2', 'r1', '02', '01', 240) - , ('t3', '/tmp/r2/01.m4a', '01.m4a', 'Track 1', 'r2', '01', '01', 120); - -INSERT INTO releases_artists (release_id, artist, artist_sanitized, role) -VALUES ('r1', 'Techno Man', 'Techno Man', 'main') - , ('r1', 'Bass Man', 'Bass Man', 'main') - , ('r2', 'Violin Woman', 'Violin Woman', 'main') - , ('r2', 'Conductor Woman', 'Conductor Woman', 'guest'); - -INSERT INTO tracks_artists (track_id, artist, artist_sanitized, role) -VALUES ('t1', 'Techno Man', 'Techno Man', 'main') - , ('t1', 'Bass Man', 'Bass Man', 'main') - , ('t2', 'Techno Man', 'Techno Man', 'main') - , ('t2', 'Bass Man', 'Bass Man', 'main') - , ('t3', 'Violin Woman', 'Violin Woman', 'main') - , ('t3', 'Conductor Woman', 'Conductor Woman', 'guest'); - """ - ) - - +@pytest.mark.usefixtures("seeded_cache") def test_list_releases(config: Config) -> None: - seed_data(config) albums = list(list_releases(config)) assert albums == [ CachedRelease( id="r1", - source_path=Path("/tmp/r1"), + source_path=Path(config.music_source_dir / "r1"), virtual_dirname="r1", title="Release 1", release_type="album", @@ -78,7 +39,7 @@ def test_list_releases(config: Config) -> None: ), CachedRelease( id="r2", - source_path=Path("/tmp/r2"), + source_path=Path(config.music_source_dir / "r2"), virtual_dirname="r2", title="Release 2", release_type="album", @@ -96,7 +57,7 @@ def test_list_releases(config: Config) -> None: assert list(list_releases(config, sanitized_artist_filter="Techno Man")) == [ CachedRelease( id="r1", - source_path=Path("/tmp/r1"), + source_path=Path(config.music_source_dir / "r1"), virtual_dirname="r1", title="Release 1", release_type="album", @@ -114,7 +75,7 @@ def test_list_releases(config: Config) -> None: assert list(list_releases(config, sanitized_genre_filter="Techno")) == [ CachedRelease( id="r1", - source_path=Path("/tmp/r1"), + source_path=Path(config.music_source_dir / "r1"), virtual_dirname="r1", title="Release 1", release_type="album", @@ -132,7 +93,7 @@ def test_list_releases(config: Config) -> None: assert list(list_releases(config, sanitized_label_filter="Silk Music")) == [ CachedRelease( id="r1", - source_path=Path("/tmp/r1"), + source_path=Path(config.music_source_dir / "r1"), virtual_dirname="r1", title="Release 1", release_type="album", @@ -148,13 +109,13 @@ def test_list_releases(config: Config) -> None: ] +@pytest.mark.usefixtures("seeded_cache") def test_list_tracks(config: Config) -> None: - seed_data(config) tracks = list(list_tracks(config, "r1")) assert tracks == [ CachedTrack( id="t1", - source_path=Path("/tmp/r1/01.m4a"), + source_path=Path(config.music_source_dir / "r1" / "01.m4a"), virtual_filename="01.m4a", title="Track 1", release_id="r1", @@ -168,7 +129,7 @@ def test_list_tracks(config: Config) -> None: ), CachedTrack( id="t2", - source_path=Path("/tmp/r1/02.m4a"), + source_path=Path(config.music_source_dir / "r1" / "02.m4a"), virtual_filename="02.m4a", title="Track 2", release_id="r1", @@ -183,50 +144,50 @@ def test_list_tracks(config: Config) -> None: ] +@pytest.mark.usefixtures("seeded_cache") def test_list_artists(config: Config) -> None: - seed_data(config) artists = list(list_artists(config)) assert set(artists) == {"Techno Man", "Bass Man", "Violin Woman", "Conductor Woman"} +@pytest.mark.usefixtures("seeded_cache") def test_list_genres(config: Config) -> None: - seed_data(config) genres = list(list_genres(config)) assert set(genres) == {"Techno", "Deep House", "Classical"} +@pytest.mark.usefixtures("seeded_cache") def test_list_labels(config: Config) -> None: - seed_data(config) labels = list(list_labels(config)) assert set(labels) == {"Silk Music", "Native State"} +@pytest.mark.usefixtures("seeded_cache") def test_release_exists(config: Config) -> None: - seed_data(config) assert release_exists(config, "r1") assert not release_exists(config, "lalala") +@pytest.mark.usefixtures("seeded_cache") def test_track_exists(config: Config) -> None: - seed_data(config) assert track_exists(config, "r1", "01.m4a") assert not track_exists(config, "lalala", "lalala") assert not track_exists(config, "r1", "lalala") +@pytest.mark.usefixtures("seeded_cache") def test_artist_exists(config: Config) -> None: - seed_data(config) assert artist_exists(config, "Bass Man") assert not artist_exists(config, "lalala") +@pytest.mark.usefixtures("seeded_cache") def test_genre_exists(config: Config) -> None: - seed_data(config) assert genre_exists(config, "Deep House") assert not genre_exists(config, "lalala") +@pytest.mark.usefixtures("seeded_cache") def test_label_exists(config: Config) -> None: - seed_data(config) assert label_exists(config, "Silk Music") assert not label_exists(config, "Cotton Music") diff --git a/rose/virtualfs/__test__.py b/rose/virtualfs/__test__.py new file mode 100644 index 0000000..f7081b2 --- /dev/null +++ b/rose/virtualfs/__test__.py @@ -0,0 +1,69 @@ +import time +from contextlib import contextmanager +from multiprocessing import Process +from pathlib import Path +from typing import Iterator + +import pytest + +from rose.foundation.conf import Config +from rose.virtualfs import mount_virtualfs, unmount_virtualfs + + +@contextmanager +def startfs(c: Config) -> Iterator[None]: + p = Process(target=mount_virtualfs, args=[c, ["-f"]]) + try: + p.start() + time.sleep(0.1) + yield + unmount_virtualfs(c) + p.join(timeout=1) + finally: + if p.exitcode is None: # pragma: no cover + p.terminate() + + +@pytest.mark.usefixtures("seeded_cache") +def test_virtual_filesystem(config: Config) -> None: + def can_read(p: Path) -> bool: + with p.open("rb") as fp: + return fp.read(256) != b"\x00" * 256 + + with startfs(config): + root = config.fuse_mount_dir + assert (root / "albums").is_dir() + assert (root / "albums" / "r1").is_dir() + assert not (root / "albums" / "lalala").exists() + assert (root / "albums" / "r1" / "01.m4a").is_file() + assert not (root / "albums" / "r1" / "lala.m4a").exists() + assert can_read(root / "albums" / "r1" / "01.m4a") + + assert (root / "artists").is_dir() + assert (root / "artists" / "Bass Man").is_dir() + assert not (root / "artists" / "lalala").exists() + assert (root / "artists" / "Bass Man" / "r1").is_dir() + assert not (root / "artists" / "Bass Man" / "lalala").exists() + assert (root / "artists" / "Bass Man" / "r1" / "01.m4a").is_file() + assert not (root / "artists" / "Bass Man" / "r1" / "lalala.m4a").exists() + assert can_read(root / "artists" / "Bass Man" / "r1" / "01.m4a") + + assert (root / "genres").is_dir() + assert (root / "genres" / "Techno").is_dir() + assert not (root / "genres" / "lalala").exists() + assert (root / "genres" / "Techno" / "r1").is_dir() + assert not (root / "genres" / "Techno" / "lalala").exists() + assert (root / "genres" / "Techno" / "r1" / "01.m4a").is_file() + assert not (root / "genres" / "Techno" / "r1" / "lalala.m4a").exists() + assert can_read(root / "genres" / "Techno" / "r1" / "01.m4a") + + assert (root / "labels").is_dir() + assert (root / "labels" / "Silk Music").is_dir() + assert not (root / "labels" / "lalala").exists() + assert (root / "labels" / "Silk Music" / "r1").is_dir() + assert not (root / "labels" / "Silk Music" / "lalala").exists() + assert (root / "labels" / "Silk Music" / "r1" / "01.m4a").is_file() + assert not (root / "labels" / "Silk Music" / "r1" / "lalala").exists() + assert can_read(root / "labels" / "Silk Music" / "r1" / "01.m4a") + + assert not (root / "lalala").exists()