Migrate get_directory to Adapter system

This commit is contained in:
Sumner Evans
2020-05-13 21:55:41 -06:00
parent 14ffb75063
commit d54dcdbd8b
21 changed files with 715 additions and 871 deletions

View File

@@ -1,6 +1,26 @@
from pathlib import Path
from time import sleep
from sublime.adapters import Result
import pytest
from sublime.adapters import AdapterManager, Result
from sublime.config import AppConfiguration, ServerConfiguration
@pytest.fixture
def adapter_manager(tmp_path: Path):
config = AppConfiguration(
servers=[
ServerConfiguration(
name="foo", server_address="bar", username="baz", password="ohea",
)
],
current_server_index=0,
cache_location=tmp_path.as_posix(),
)
AdapterManager.reset(config)
yield
AdapterManager.shutdown()
def test_result_immediate():
@@ -24,7 +44,7 @@ def test_result_immediate_callback():
def test_result_future():
def resolve_later() -> int:
sleep(1)
sleep(0.1)
return 42
result = Result(resolve_later)
@@ -35,7 +55,7 @@ def test_result_future():
def test_result_future_callback():
def resolve_later() -> int:
sleep(1)
sleep(0.1)
return 42
check_done = False
@@ -49,21 +69,48 @@ def test_result_future_callback():
result = Result(resolve_later)
result.add_done_callback(check_done_callback)
# Should take much less than 2 seconds to complete. If the assertion fails, then the
# Should take much less than 1 seconds to complete. If the assertion fails, then the
# check_done_callback failed.
t = 0
while not check_done:
assert t < 2
assert t < 1
t += 0.1
sleep(0.1)
def test_default_value():
def resolve_fail() -> int:
sleep(1)
sleep(0.1)
raise Exception()
result = Result(resolve_fail, default_value=42)
assert not result.data_is_available
assert result.result() == 42
assert result.data_is_available
def test_cancel():
def resolve_later() -> int:
sleep(0.1)
return 42
cancel_called = False
def on_cancel():
nonlocal cancel_called
cancel_called = True
result = Result(resolve_later, on_cancel=on_cancel)
result.cancel()
assert cancel_called
assert not result.data_is_available
with pytest.raises(Exception):
result.result()
def test_get_song_details(adapter_manager: AdapterManager):
# song = AdapterManager.get_song_details("1")
# print(song)
# assert 0
# TODO
pass

View File

@@ -8,14 +8,21 @@ import pytest
from peewee import SelectQuery
from sublime import util
from sublime.adapters import api_objects as SublimeAPI, CacheMissError
from sublime.adapters.filesystem import FilesystemAdapter
from sublime.adapters.subsonic import api_objects as SubsonicAPI
MOCK_DATA_FILES = Path(__file__).parent.joinpath("mock_data")
MOCK_ALBUM_ART = MOCK_DATA_FILES.joinpath("album-art.png")
MOCK_ALBUM_ART2 = MOCK_DATA_FILES.joinpath("album-art2.png")
MOCK_ALBUM_ART3 = MOCK_DATA_FILES.joinpath("album-art3.png")
MOCK_SONG_FILE = MOCK_DATA_FILES.joinpath("test-song.mp3")
MOCK_SONG_FILE2 = MOCK_DATA_FILES.joinpath("test-song2.mp3")
MOCK_ALBUM_ART_HASH = "5d7bee4f3fe25b18cd2a66f1c9767e381bc64328"
MOCK_ALBUM_ART2_HASH = "031a8a1ca01f64f851a22d5478e693825a00fb23"
MOCK_ALBUM_ART3_HASH = "46a8af0f8fe370e59202a545803e8bbb3a4a41ee"
MOCK_SONG_FILE_HASH = "fe12d0712dbfd6ff7f75ef3783856a7122a78b0a"
MOCK_SONG_FILE2_HASH = "c32597c724e2e484dbf5856930b2e5bb80de13b7"
MOCK_SUBSONIC_SONGS = [
SubsonicAPI.Song(
@@ -28,7 +35,7 @@ MOCK_SUBSONIC_SONGS = [
artist_id="art1",
duration=timedelta(seconds=20.8),
path="foo/song2.mp3",
cover_art="2",
cover_art="s2",
_genre="Bar",
),
SubsonicAPI.Song(
@@ -41,7 +48,7 @@ MOCK_SUBSONIC_SONGS = [
artist_id="art2",
duration=timedelta(seconds=10.2),
path="foo/song1.mp3",
cover_art="1",
cover_art="s1",
_genre="Foo",
),
SubsonicAPI.Song(
@@ -54,7 +61,7 @@ MOCK_SUBSONIC_SONGS = [
artist_id="art2",
duration=timedelta(seconds=10.2),
path="foo/song1.mp3",
cover_art="1",
cover_art="s1",
_genre="Foo",
),
]
@@ -89,21 +96,16 @@ def mock_data_files(
def verify_songs(
actual_songs: Iterable[SublimeAPI.Song], expected_songs: Iterable[SubsonicAPI.Song]
):
actual_songs, expected_songs = (list(actual_songs), list(expected_songs))
assert len(actual_songs) == len(expected_songs)
for actual, song in zip(actual_songs, expected_songs):
for k, v in asdict(song).items():
ignore = (
"_genre",
"_album",
"_artist",
"_parent",
"album_id",
"artist_id",
)
if k in ignore:
if k in ("_genre", "_album", "_artist", "_parent", "album_id", "artist_id"):
continue
print(k) # noqa: T001
print(k, "->", v) # noqa: T001
actual_value = getattr(actual, k, None)
if k == "album":
assert ("a1", "foo") == (actual_value.id, actual_value.name)
elif k == "genre":
@@ -292,7 +294,7 @@ def test_invalidate_playlist(cache_adapter: FilesystemAdapter):
SubsonicAPI.PlaylistWithSongs("2", "test2", cover_art="pl_2", songs=[]),
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("pl_2",), MOCK_ALBUM_ART,
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("pl_2",), MOCK_ALBUM_ART2,
)
stale_uri_1 = cache_adapter.get_cover_art_uri("pl_test1", "file")
@@ -338,43 +340,31 @@ def test_invalidate_playlist(cache_adapter: FilesystemAdapter):
assert e.partial_data == stale_uri_2
def test_invalidate_song_data(cache_adapter: FilesystemAdapter):
def test_invalidate_song_file(cache_adapter: FilesystemAdapter):
CACHE_KEYS = FilesystemAdapter.CachedDataKey
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.SONG_DETAILS, ("2",), MOCK_SUBSONIC_SONGS[0]
CACHE_KEYS.SONG_DETAILS, ("2",), MOCK_SUBSONIC_SONGS[0]
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.SONG_DETAILS, ("1",), MOCK_SUBSONIC_SONGS[1]
CACHE_KEYS.SONG_DETAILS, ("1",), MOCK_SUBSONIC_SONGS[1]
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("1",), MOCK_ALBUM_ART,
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.SONG_FILE, ("1",), MOCK_SONG_FILE
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.SONG_FILE, ("2",), MOCK_SONG_FILE
CACHE_KEYS.COVER_ART_FILE, ("s1", "song"), MOCK_ALBUM_ART,
)
cache_adapter.ingest_new_data(CACHE_KEYS.SONG_FILE, ("1",), MOCK_SONG_FILE)
cache_adapter.ingest_new_data(CACHE_KEYS.SONG_FILE, ("2",), MOCK_SONG_FILE2)
stale_song_file = cache_adapter.get_song_uri("1", "file")
stale_cover_art_file = cache_adapter.get_cover_art_uri("1", "file")
cache_adapter.invalidate_data(FilesystemAdapter.CachedDataKey.SONG_FILE, ("1",))
cache_adapter.invalidate_data(CACHE_KEYS.SONG_FILE, ("1",))
cache_adapter.invalidate_data(CACHE_KEYS.COVER_ART_FILE, ("s1", "song"))
try:
with pytest.raises(CacheMissError):
cache_adapter.get_song_uri("1", "file")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data
assert e.partial_data == stale_song_file
try:
cache_adapter.get_cover_art_uri("1", "file")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data
assert e.partial_data == stale_cover_art_file
with pytest.raises(CacheMissError):
cache_adapter.get_cover_art_uri("s1", "file")
# Make sure it didn't delete the other ones.
assert cache_adapter.get_song_uri("2", "file").endswith("song2.mp3")
# Make sure it didn't delete the other song.
assert cache_adapter.get_song_uri("2", "file").endswith(MOCK_SONG_FILE2_HASH)
def test_delete_playlists(cache_adapter: FilesystemAdapter):
@@ -410,11 +400,13 @@ def test_delete_playlists(cache_adapter: FilesystemAdapter):
# Even if the cover art failed to be deleted, it should cache miss.
shutil.copy(
MOCK_ALBUM_ART,
str(cache_adapter.cover_art_dir.joinpath(util.params_hash("pl_1"))),
MOCK_ALBUM_ART, str(cache_adapter.cover_art_dir.joinpath(MOCK_ALBUM_ART_HASH)),
)
with pytest.raises(CacheMissError):
try:
cache_adapter.get_cover_art_uri("pl_1", "file")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data is None
def test_delete_song_data(cache_adapter: FilesystemAdapter):
@@ -422,16 +414,17 @@ def test_delete_song_data(cache_adapter: FilesystemAdapter):
FilesystemAdapter.CachedDataKey.SONG_DETAILS, ("1",), MOCK_SUBSONIC_SONGS[1]
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("1",), MOCK_ALBUM_ART,
FilesystemAdapter.CachedDataKey.SONG_FILE, ("1",), MOCK_SONG_FILE
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.SONG_FILE, ("1",), MOCK_SONG_FILE
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("s1",), MOCK_ALBUM_ART,
)
music_file_path = cache_adapter.get_song_uri("1", "file")
cover_art_path = cache_adapter.get_cover_art_uri("1", "file")
cover_art_path = cache_adapter.get_cover_art_uri("s1", "file")
cache_adapter.delete_data(FilesystemAdapter.CachedDataKey.SONG_FILE, ("1",))
cache_adapter.delete_data(FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("s1",))
assert not Path(music_file_path).exists()
assert not Path(cover_art_path).exists()
@@ -443,7 +436,7 @@ def test_delete_song_data(cache_adapter: FilesystemAdapter):
assert e.partial_data is None
try:
cache_adapter.get_cover_art_uri("1", "file")
cache_adapter.get_cover_art_uri("s1", "file")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data is None
@@ -648,7 +641,7 @@ def test_caching_get_artist(cache_adapter: FilesystemAdapter):
)
artist = cache_adapter.get_artist("1")
assert (
assert artist.artist_image_url and (
artist.id,
artist.name,
artist.album_count,
@@ -686,7 +679,7 @@ def test_caching_get_artist(cache_adapter: FilesystemAdapter):
)
artist = cache_adapter.get_artist("1")
assert (
assert artist.artist_image_url and (
artist.id,
artist.name,
artist.album_count,
@@ -728,7 +721,7 @@ def test_caching_get_album(cache_adapter: FilesystemAdapter):
)
album = cache_adapter.get_album("a1")
assert album
assert album and album.cover_art
assert (
album.id,
album.name,
@@ -747,9 +740,9 @@ def test_caching_invalidate_artist(cache_adapter: FilesystemAdapter):
# Simulate the artist details being retrieved from Subsonic.
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.ARTIST,
("1",),
("artist1",),
SubsonicAPI.ArtistAndArtistInfo(
"1",
"artist1",
"Bar",
album_count=1,
artist_image_url="image",
@@ -768,47 +761,40 @@ def test_caching_invalidate_artist(cache_adapter: FilesystemAdapter):
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.ALBUM,
("1",),
SubsonicAPI.Album("1", "Foo", artist_id="1", cover_art="1"),
SubsonicAPI.Album("1", "Foo", artist_id="artist1", cover_art="1"),
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.ALBUM,
("2",),
SubsonicAPI.Album("2", "Bar", artist_id="1", cover_art="2"),
SubsonicAPI.Album("2", "Bar", artist_id="artist1", cover_art="2"),
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("image",), MOCK_ALBUM_ART,
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("image",), MOCK_ALBUM_ART3,
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("1",), MOCK_ALBUM_ART,
)
cache_adapter.ingest_new_data(
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("2",), MOCK_ALBUM_ART,
FilesystemAdapter.CachedDataKey.COVER_ART_FILE, ("2",), MOCK_ALBUM_ART2,
)
stale_artist = cache_adapter.get_artist("1")
stale_artist = cache_adapter.get_artist("artist1")
stale_album_1 = cache_adapter.get_album("1")
stale_album_2 = cache_adapter.get_album("2")
stale_artist_artwork = cache_adapter.get_cover_art_uri("image", "file")
stale_cover_art_1 = cache_adapter.get_cover_art_uri("1", "file")
stale_cover_art_2 = cache_adapter.get_cover_art_uri("2", "file")
cache_adapter.invalidate_data(FilesystemAdapter.CachedDataKey.ARTIST, ("1",))
cache_adapter.invalidate_data(FilesystemAdapter.CachedDataKey.ARTIST, ("artist1",))
# Test the cascade of cache invalidations.
try:
cache_adapter.get_artist("1")
cache_adapter.get_artist("artist1")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data
assert e.partial_data == stale_artist
try:
cache_adapter.get_cover_art_uri("image", "file")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data
assert e.partial_data == stale_artist_artwork
try:
cache_adapter.get_album("1")
assert 0, "DID NOT raise CacheMissError"
@@ -823,6 +809,13 @@ def test_caching_invalidate_artist(cache_adapter: FilesystemAdapter):
assert e.partial_data
assert e.partial_data == stale_album_2
try:
cache_adapter.get_cover_art_uri("image", "file")
assert 0, "DID NOT raise CacheMissError"
except CacheMissError as e:
assert e.partial_data
assert e.partial_data == stale_artist_artwork
try:
cache_adapter.get_cover_art_uri("1", "file")
assert 0, "DID NOT raise CacheMissError"

View File

@@ -1,2 +1,4 @@
test-song.mp3 was originally named Happy_Music-2018-09-18_-_Beautiful_Memories_-_David_Fesliyan.mp3
which is royalty free music from https://www.fesliyanstudios.com
The test songs are royalty free music from https://www.fesliyanstudios.com
* test-song.mp3 (originally named Happy_Music-2018-09-18_-_Beautiful_Memories_-_David_Fesliyan.mp3)
* test-song2.mp3 (originally named 2017-03-24_-_Lone_Rider_-_David_Fesliyan.mp3)

View File

@@ -0,0 +1 @@
really not a PNG

View File

@@ -0,0 +1 @@
definitely not a PNG. Stop looking lol

Binary file not shown.