Migrate get_directory to Adapter system
This commit is contained in:
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
|
1
tests/adapter_tests/mock_data/album-art2.png
Normal file
1
tests/adapter_tests/mock_data/album-art2.png
Normal file
@@ -0,0 +1 @@
|
||||
really not a PNG
|
1
tests/adapter_tests/mock_data/album-art3.png
Normal file
1
tests/adapter_tests/mock_data/album-art3.png
Normal file
@@ -0,0 +1 @@
|
||||
definitely not a PNG. Stop looking lol
|
BIN
tests/adapter_tests/mock_data/test-song2.mp3
Normal file
BIN
tests/adapter_tests/mock_data/test-song2.mp3
Normal file
Binary file not shown.
Reference in New Issue
Block a user