mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
Some fixes for Windows support (especially for tests)
The main motive here isn't full Windows support per, but being able to run tests on Windows, as this is my main platform. Booting a VM just to run tests is cumbersome.
This commit is contained in:
parent
7d1825151e
commit
5c46c96b53
@ -143,25 +143,26 @@ class Cache(object):
|
|||||||
>>> with cache.set_fileobj(key) as fp:
|
>>> with cache.set_fileobj(key) as fp:
|
||||||
... json.dump(some_data, fp)
|
... json.dump(some_data, fp)
|
||||||
"""
|
"""
|
||||||
|
f = tempfile.NamedTemporaryFile(
|
||||||
|
dir=self._cache_dir, suffix=".part", delete=False
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
with tempfile.NamedTemporaryFile(
|
yield f
|
||||||
dir=self._cache_dir, suffix=".part", delete=True
|
|
||||||
) as f:
|
|
||||||
yield f
|
|
||||||
|
|
||||||
# seek to end and get position to get filesize
|
# seek to end and get position to get filesize
|
||||||
f.seek(0, 2)
|
f.seek(0, 2)
|
||||||
size = f.tell()
|
size = f.tell()
|
||||||
|
f.close()
|
||||||
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if self._auto_prune:
|
if self._auto_prune:
|
||||||
self._make_space(size, key=key)
|
self._make_space(size, key=key)
|
||||||
os.replace(f.name, self._filepath(key))
|
os.replace(f.name, self._filepath(key))
|
||||||
self._record_file(key, size)
|
self._record_file(key, size)
|
||||||
except OSError as e:
|
except:
|
||||||
# Ignore error from trying to delete the renamed temp file
|
f.close()
|
||||||
if e.errno != errno.ENOENT:
|
os.remove(f.name)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def set(self, key, value):
|
def set(self, key, value):
|
||||||
"""Set a literal value into the cache and return its path"""
|
"""Set a literal value into the cache and return its path"""
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013-2019 Alban 'spl0k' Féron
|
# Copyright (C) 2013-2020 Alban 'spl0k' Féron
|
||||||
# 2017 Óscar García Amor
|
# 2017 Óscar García Amor
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from configparser import RawConfigParser
|
from configparser import RawConfigParser
|
||||||
@ -39,7 +40,9 @@ class DefaultConfig(object):
|
|||||||
"mount_api": True,
|
"mount_api": True,
|
||||||
}
|
}
|
||||||
DAEMON = {
|
DAEMON = {
|
||||||
"socket": os.path.join(tempdir, "supysonic.sock"),
|
"socket": r"\\.\pipe\supysonic"
|
||||||
|
if sys.platform == "win32"
|
||||||
|
else os.path.join(tempdir, "supysonic.sock"),
|
||||||
"run_watcher": True,
|
"run_watcher": True,
|
||||||
"wait_delay": 5,
|
"wait_delay": 5,
|
||||||
"jukebox_command": None,
|
"jukebox_command": None,
|
||||||
|
@ -35,22 +35,25 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
self._make_request(
|
self._make_request(
|
||||||
"createInternetRadioStation",
|
"createInternetRadioStation",
|
||||||
{"u": "bob", "p": "B0b", "username": "alice"},
|
{"u": "bob", "p": "B0b", "username": "alice"},
|
||||||
error=50
|
error=50,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check params
|
# check params
|
||||||
self._make_request("createInternetRadioStation", error=10)
|
self._make_request("createInternetRadioStation", error=10)
|
||||||
self._make_request("createInternetRadioStation", {"streamUrl": "missingName"}, error=10)
|
self._make_request(
|
||||||
self._make_request("createInternetRadioStation", {"name": "missing stream"}, error=10)
|
"createInternetRadioStation", {"streamUrl": "missingName"}, error=10
|
||||||
|
)
|
||||||
|
self._make_request(
|
||||||
|
"createInternetRadioStation", {"name": "missing stream"}, error=10
|
||||||
|
)
|
||||||
|
|
||||||
# create w/ required fields
|
# create w/ required fields
|
||||||
stream_url = "http://example.com/radio/create"
|
stream_url = "http://example.com/radio/create"
|
||||||
name = "radio station"
|
name = "radio station"
|
||||||
|
|
||||||
self._make_request("createInternetRadioStation", {
|
self._make_request(
|
||||||
"streamUrl": stream_url,
|
"createInternetRadioStation", {"streamUrl": stream_url, "name": name}
|
||||||
"name": name,
|
)
|
||||||
})
|
|
||||||
|
|
||||||
# the correct value is 2 because _make_request uses GET then POST
|
# the correct value is 2 because _make_request uses GET then POST
|
||||||
self.assertRadioStationCountEqual(2)
|
self.assertRadioStationCountEqual(2)
|
||||||
@ -66,11 +69,10 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
name = "radio station1"
|
name = "radio station1"
|
||||||
homepage_url = "http://example.com/home"
|
homepage_url = "http://example.com/home"
|
||||||
|
|
||||||
self._make_request("createInternetRadioStation", {
|
self._make_request(
|
||||||
"streamUrl": stream_url,
|
"createInternetRadioStation",
|
||||||
"name": name,
|
{"streamUrl": stream_url, "name": name, "homepageUrl": homepage_url},
|
||||||
"homepageUrl": homepage_url,
|
)
|
||||||
})
|
|
||||||
|
|
||||||
# the correct value is 2 because _make_request uses GET then POST
|
# the correct value is 2 because _make_request uses GET then POST
|
||||||
self.assertRadioStationCountEqual(2)
|
self.assertRadioStationCountEqual(2)
|
||||||
@ -83,7 +85,7 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
self._make_request(
|
self._make_request(
|
||||||
"updateInternetRadioStation",
|
"updateInternetRadioStation",
|
||||||
{"u": "bob", "p": "B0b", "username": "alice"},
|
{"u": "bob", "p": "B0b", "username": "alice"},
|
||||||
error=50
|
error=50,
|
||||||
)
|
)
|
||||||
|
|
||||||
# test data
|
# test data
|
||||||
@ -107,67 +109,86 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# check params
|
# check params
|
||||||
self._make_request("updateInternetRadioStation", {
|
self._make_request(
|
||||||
"id": station.id, "homepageUrl": "missing required params",
|
"updateInternetRadioStation",
|
||||||
}, error=10)
|
{"id": station.id, "homepageUrl": "missing required params"},
|
||||||
self._make_request("updateInternetRadioStation", {
|
error=10,
|
||||||
"id": station.id, "name": "missing streamUrl",
|
)
|
||||||
}, error=10)
|
self._make_request(
|
||||||
self._make_request("updateInternetRadioStation", {
|
"updateInternetRadioStation",
|
||||||
"id": station.id, "streamUrl": "missing name",
|
{"id": station.id, "name": "missing streamUrl"},
|
||||||
}, error=10)
|
error=10,
|
||||||
|
)
|
||||||
|
self._make_request(
|
||||||
|
"updateInternetRadioStation",
|
||||||
|
{"id": station.id, "streamUrl": "missing name"},
|
||||||
|
error=10,
|
||||||
|
)
|
||||||
|
|
||||||
# update the record w/ required fields
|
# update the record w/ required fields
|
||||||
self._make_request("updateInternetRadioStation", {
|
self._make_request(
|
||||||
"id": station.id,
|
"updateInternetRadioStation",
|
||||||
"streamUrl": update["stream_url"],
|
{
|
||||||
"name": update["name"],
|
"id": station.id,
|
||||||
})
|
"streamUrl": update["stream_url"],
|
||||||
|
"name": update["name"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
with db_session:
|
with db_session:
|
||||||
rs_update = RadioStation[station.id]
|
rs_update = RadioStation[station.id]
|
||||||
|
|
||||||
self.assertRadioStationEquals(rs_update, update["stream_url"], update["name"], test["homepage_url"])
|
self.assertRadioStationEquals(
|
||||||
|
rs_update, update["stream_url"], update["name"], test["homepage_url"]
|
||||||
|
)
|
||||||
|
|
||||||
# update the record w/ all fields
|
# update the record w/ all fields
|
||||||
self._make_request("updateInternetRadioStation", {
|
self._make_request(
|
||||||
"id": station.id,
|
"updateInternetRadioStation",
|
||||||
"streamUrl": update["stream_url"],
|
{
|
||||||
"name": update["name"],
|
"id": station.id,
|
||||||
"homepageUrl": update["homepage_url"],
|
"streamUrl": update["stream_url"],
|
||||||
})
|
"name": update["name"],
|
||||||
|
"homepageUrl": update["homepage_url"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
with db_session:
|
with db_session:
|
||||||
rs_update = RadioStation[station.id]
|
rs_update = RadioStation[station.id]
|
||||||
|
|
||||||
self.assertRadioStationEquals(rs_update, update["stream_url"], update["name"], update["homepage_url"])
|
self.assertRadioStationEquals(
|
||||||
|
rs_update, update["stream_url"], update["name"], update["homepage_url"]
|
||||||
|
)
|
||||||
|
|
||||||
def test_delete_radio_station(self):
|
def test_delete_radio_station(self):
|
||||||
# test for non-admin access
|
# test for non-admin access
|
||||||
self._make_request(
|
self._make_request(
|
||||||
"deleteInternetRadioStation",
|
"deleteInternetRadioStation",
|
||||||
{"u": "bob", "p": "B0b", "username": "alice"},
|
{"u": "bob", "p": "B0b", "username": "alice"},
|
||||||
error=50
|
error=50,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check params
|
# check params
|
||||||
self._make_request("deleteInternetRadioStation", error=10)
|
self._make_request("deleteInternetRadioStation", error=10)
|
||||||
self._make_request("deleteInternetRadioStation", {"id": 1}, error=0)
|
self._make_request("deleteInternetRadioStation", {"id": 1}, error=0)
|
||||||
self._make_request("deleteInternetRadioStation", {"id": str(uuid.uuid4())}, error=70)
|
self._make_request(
|
||||||
|
"deleteInternetRadioStation", {"id": str(uuid.uuid4())}, error=70
|
||||||
|
)
|
||||||
|
|
||||||
# delete
|
# delete
|
||||||
with db_session:
|
with db_session:
|
||||||
station = RadioStation(
|
station = RadioStation(
|
||||||
stream_url="http://example.com/radio/delete",
|
stream_url="http://example.com/radio/delete",
|
||||||
name="Radio Delete",
|
name="Radio Delete",
|
||||||
homepage_url="http://example.com/update"
|
homepage_url="http://example.com/update",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._make_request("deleteInternetRadioStation", {"id": station.id}, skip_post=True)
|
self._make_request(
|
||||||
|
"deleteInternetRadioStation", {"id": station.id}, skip_post=True
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRadioStationCountEqual(0)
|
self.assertRadioStationCountEqual(0)
|
||||||
|
|
||||||
|
|
||||||
def test_get_radio_stations(self):
|
def test_get_radio_stations(self):
|
||||||
test_range = 3
|
test_range = 3
|
||||||
with db_session:
|
with db_session:
|
||||||
@ -180,7 +201,9 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
|
|
||||||
# verify happy path is clean
|
# verify happy path is clean
|
||||||
self.assertRadioStationCountEqual(test_range)
|
self.assertRadioStationCountEqual(test_range)
|
||||||
rv, child = self._make_request("getInternetRadioStations", tag="internetRadioStations")
|
rv, child = self._make_request(
|
||||||
|
"getInternetRadioStations", tag="internetRadioStations"
|
||||||
|
)
|
||||||
self.assertEqual(len(child), test_range)
|
self.assertEqual(len(child), test_range)
|
||||||
# This order is guaranteed to work because the api returns in order by name.
|
# This order is guaranteed to work because the api returns in order by name.
|
||||||
# Test data is sequential by design.
|
# Test data is sequential by design.
|
||||||
@ -190,7 +213,6 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
self.assertTrue(station.get("name").endswith("Radio {}".format(x)))
|
self.assertTrue(station.get("name").endswith("Radio {}".format(x)))
|
||||||
self.assertTrue(station.get("homePageUrl").endswith("update-{}".format(x)))
|
self.assertTrue(station.get("homePageUrl").endswith("update-{}".format(x)))
|
||||||
|
|
||||||
|
|
||||||
# test for non-admin access
|
# test for non-admin access
|
||||||
rv, child = self._make_request(
|
rv, child = self._make_request(
|
||||||
"getInternetRadioStations",
|
"getInternetRadioStations",
|
||||||
@ -199,4 +221,3 @@ class RadioStationTestCase(ApiTestBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(len(child), test_range)
|
self.assertEqual(len(child), test_range)
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
|
||||||
#
|
#
|
||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017-2018 Alban 'spl0k' Féron
|
# Copyright (C) 2017-2020 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
@ -46,11 +46,19 @@ class TranscodingTestCase(ApiTestBase):
|
|||||||
def test_no_transcoding_available(self):
|
def test_no_transcoding_available(self):
|
||||||
self._make_request("stream", {"id": self.trackid, "format": "wat"}, error=0)
|
self._make_request("stream", {"id": self.trackid, "format": "wat"}, error=0)
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
sys.platform == "win32",
|
||||||
|
"Can't test transcoding on Windows because of a lack of simple commandline tools",
|
||||||
|
)
|
||||||
def test_direct_transcode(self):
|
def test_direct_transcode(self):
|
||||||
rv = self._stream(maxBitRate=96, estimateContentLength="true")
|
rv = self._stream(maxBitRate=96, estimateContentLength="true")
|
||||||
self.assertIn("tests/assets/folder/silence.mp3", rv.data)
|
self.assertIn("tests/assets/folder/silence.mp3", rv.data)
|
||||||
self.assertTrue(rv.data.endswith("96"))
|
self.assertTrue(rv.data.endswith("96"))
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
sys.platform == "win32",
|
||||||
|
"Can't test transcoding on Windows because of a lack of simple commandline tools",
|
||||||
|
)
|
||||||
def test_decode_encode(self):
|
def test_decode_encode(self):
|
||||||
rv = self._stream(format="cat")
|
rv = self._stream(format="cat")
|
||||||
self.assertEqual(rv.data, "Pushing out some mp3 data...")
|
self.assertEqual(rv.data, "Pushing out some mp3 data...")
|
||||||
|
@ -1 +0,0 @@
|
|||||||
../folder/silence.mp3
|
|
BIN
tests/assets/formats/silence.mp3
Normal file
BIN
tests/assets/formats/silence.mp3
Normal file
Binary file not shown.
@ -1,20 +1,17 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
|
||||||
#
|
#
|
||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017-2018 Alban 'spl0k' Féron
|
# Copyright (C) 2017-2020 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import shlex
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from contextlib import contextmanager
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
@ -29,8 +26,8 @@ class CLITestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
conf = TestConfig(False, False)
|
conf = TestConfig(False, False)
|
||||||
self.__dbfile = tempfile.mkstemp()[1]
|
self.__db = tempfile.mkstemp()
|
||||||
conf.BASE["database_uri"] = "sqlite:///" + self.__dbfile
|
conf.BASE["database_uri"] = "sqlite:///" + self.__db[1]
|
||||||
init_database(conf.BASE["database_uri"])
|
init_database(conf.BASE["database_uri"])
|
||||||
|
|
||||||
self.__stdout = StringIO()
|
self.__stdout = StringIO()
|
||||||
@ -41,19 +38,15 @@ class CLITestCase(unittest.TestCase):
|
|||||||
self.__stdout.close()
|
self.__stdout.close()
|
||||||
self.__stderr.close()
|
self.__stderr.close()
|
||||||
release_database()
|
release_database()
|
||||||
os.unlink(self.__dbfile)
|
os.close(self.__db[0])
|
||||||
|
os.remove(self.__db[1])
|
||||||
|
|
||||||
@contextmanager
|
def __add_folder(self, name, path):
|
||||||
def _tempdir(self):
|
self.__cli.onecmd("folder add {0} {1}".format(name, shlex.quote(path)))
|
||||||
d = tempfile.mkdtemp()
|
|
||||||
try:
|
|
||||||
yield d
|
|
||||||
finally:
|
|
||||||
shutil.rmtree(d)
|
|
||||||
|
|
||||||
def test_folder_add(self):
|
def test_folder_add(self):
|
||||||
with self._tempdir() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
self.__cli.onecmd("folder add tmpfolder " + d)
|
self.__add_folder("tmpfolder", d)
|
||||||
|
|
||||||
with db_session:
|
with db_session:
|
||||||
f = Folder.select().first()
|
f = Folder.select().first()
|
||||||
@ -61,19 +54,19 @@ class CLITestCase(unittest.TestCase):
|
|||||||
self.assertEqual(f.path, d)
|
self.assertEqual(f.path, d)
|
||||||
|
|
||||||
def test_folder_add_errors(self):
|
def test_folder_add_errors(self):
|
||||||
with self._tempdir() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
self.__cli.onecmd("folder add f1 " + d)
|
self.__add_folder("f1", d)
|
||||||
self.__cli.onecmd("folder add f2 " + d)
|
self.__add_folder("f2", d)
|
||||||
with self._tempdir() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
self.__cli.onecmd("folder add f1 " + d)
|
self.__add_folder("f1", d)
|
||||||
self.__cli.onecmd("folder add f3 /invalid/path")
|
self.__cli.onecmd("folder add f3 /invalid/path")
|
||||||
|
|
||||||
with db_session:
|
with db_session:
|
||||||
self.assertEqual(Folder.select().count(), 1)
|
self.assertEqual(Folder.select().count(), 1)
|
||||||
|
|
||||||
def test_folder_delete(self):
|
def test_folder_delete(self):
|
||||||
with self._tempdir() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
self.__cli.onecmd("folder add tmpfolder " + d)
|
self.__add_folder("tmpfolder", d)
|
||||||
self.__cli.onecmd("folder delete randomfolder")
|
self.__cli.onecmd("folder delete randomfolder")
|
||||||
self.__cli.onecmd("folder delete tmpfolder")
|
self.__cli.onecmd("folder delete tmpfolder")
|
||||||
|
|
||||||
@ -81,15 +74,15 @@ class CLITestCase(unittest.TestCase):
|
|||||||
self.assertEqual(Folder.select().count(), 0)
|
self.assertEqual(Folder.select().count(), 0)
|
||||||
|
|
||||||
def test_folder_list(self):
|
def test_folder_list(self):
|
||||||
with self._tempdir() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
self.__cli.onecmd("folder add tmpfolder " + d)
|
self.__add_folder("tmpfolder", d)
|
||||||
self.__cli.onecmd("folder list")
|
self.__cli.onecmd("folder list")
|
||||||
self.assertIn("tmpfolder", self.__stdout.getvalue())
|
self.assertIn("tmpfolder", self.__stdout.getvalue())
|
||||||
self.assertIn(d, self.__stdout.getvalue())
|
self.assertIn(d, self.__stdout.getvalue())
|
||||||
|
|
||||||
def test_folder_scan(self):
|
def test_folder_scan(self):
|
||||||
with self._tempdir() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
self.__cli.onecmd("folder add tmpfolder " + d)
|
self.__add_folder("tmpfolder", d)
|
||||||
with tempfile.NamedTemporaryFile(dir=d):
|
with tempfile.NamedTemporaryFile(dir=d):
|
||||||
self.__cli.onecmd("folder scan")
|
self.__cli.onecmd("folder scan")
|
||||||
self.__cli.onecmd("folder scan tmpfolder nonexistent")
|
self.__cli.onecmd("folder scan tmpfolder nonexistent")
|
||||||
|
@ -54,7 +54,7 @@ class DbTestCase(unittest.TestCase):
|
|||||||
return (
|
return (
|
||||||
db.Folder.get(name="Root folder"),
|
db.Folder.get(name="Root folder"),
|
||||||
db.Folder.get(name="Child folder"),
|
db.Folder.get(name="Child folder"),
|
||||||
db.Folder.get(name="Child Folder (No Art)")
|
db.Folder.get(name="Child Folder (No Art)"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_some_tracks(self, artist=None, album=None):
|
def create_some_tracks(self, artist=None, album=None):
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
|
||||||
#
|
#
|
||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017-2018 Alban 'spl0k' Féron
|
# Copyright (C) 2017-2020 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import mutagen
|
import mutagen
|
||||||
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
@ -39,10 +39,15 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
@contextmanager
|
@contextmanager
|
||||||
def __temporary_track_copy(self):
|
def __temporary_track_copy(self):
|
||||||
track = db.Track.select().first()
|
track = db.Track.select().first()
|
||||||
with tempfile.NamedTemporaryFile(dir=os.path.dirname(track.path)) as tf:
|
with tempfile.NamedTemporaryFile(
|
||||||
|
dir=os.path.dirname(track.path), delete=False
|
||||||
|
) as tf:
|
||||||
with io.open(track.path, "rb") as f:
|
with io.open(track.path, "rb") as f:
|
||||||
tf.write(f.read())
|
tf.write(f.read())
|
||||||
yield tf
|
try:
|
||||||
|
yield tf.name
|
||||||
|
finally:
|
||||||
|
os.remove(tf.name)
|
||||||
|
|
||||||
def __scan(self, force=False):
|
def __scan(self, force=False):
|
||||||
self.scanner = Scanner(force=force)
|
self.scanner = Scanner(force=force)
|
||||||
@ -115,7 +120,7 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
with self.__temporary_track_copy() as tf:
|
with self.__temporary_track_copy() as tf:
|
||||||
self.__scan()
|
self.__scan()
|
||||||
self.assertEqual(db.Track.select().count(), 2)
|
self.assertEqual(db.Track.select().count(), 2)
|
||||||
self.scanner.move_file(tf.name, track.path)
|
self.scanner.move_file(tf, track.path)
|
||||||
commit()
|
commit()
|
||||||
self.assertEqual(db.Track.select().count(), 1)
|
self.assertEqual(db.Track.select().count(), 1)
|
||||||
|
|
||||||
@ -134,9 +139,10 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
self.__scan()
|
self.__scan()
|
||||||
self.assertEqual(db.Track.select().count(), 2)
|
self.assertEqual(db.Track.select().count(), 2)
|
||||||
|
|
||||||
tf.seek(0, 0)
|
with open(tf, "wb") as f:
|
||||||
tf.write(b"\x00" * 4096)
|
f.seek(0, 0)
|
||||||
tf.truncate()
|
f.write(b"\x00" * 4096)
|
||||||
|
f.truncate()
|
||||||
|
|
||||||
self.__scan(True)
|
self.__scan(True)
|
||||||
self.assertEqual(db.Track.select().count(), 1)
|
self.assertEqual(db.Track.select().count(), 1)
|
||||||
@ -145,7 +151,7 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
def test_rescan_removed_file(self):
|
def test_rescan_removed_file(self):
|
||||||
track = db.Track.select().first()
|
track = db.Track.select().first()
|
||||||
|
|
||||||
with self.__temporary_track_copy() as tf:
|
with self.__temporary_track_copy():
|
||||||
self.__scan()
|
self.__scan()
|
||||||
self.assertEqual(db.Track.select().count(), 2)
|
self.assertEqual(db.Track.select().count(), 2)
|
||||||
|
|
||||||
@ -158,7 +164,7 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
with self.__temporary_track_copy() as tf:
|
with self.__temporary_track_copy() as tf:
|
||||||
self.__scan()
|
self.__scan()
|
||||||
copy = db.Track.get(path=tf.name)
|
copy = db.Track.get(path=tf)
|
||||||
self.assertEqual(copy.artist.name, "Some artist")
|
self.assertEqual(copy.artist.name, "Some artist")
|
||||||
self.assertEqual(copy.album.name, "Awesome album")
|
self.assertEqual(copy.album.name, "Awesome album")
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import os
|
|
||||||
import unittest
|
import unittest
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
@ -21,10 +20,9 @@ from ..testbase import TestConfig
|
|||||||
|
|
||||||
class SecretTestCase(unittest.TestCase):
|
class SecretTestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.__dbfile = tempfile.mkstemp()[1]
|
|
||||||
self.__dir = tempfile.mkdtemp()
|
self.__dir = tempfile.mkdtemp()
|
||||||
self.config = TestConfig(False, False)
|
self.config = TestConfig(False, False)
|
||||||
self.config.BASE["database_uri"] = "sqlite:///" + self.__dbfile
|
self.config.BASE["database_uri"] = "sqlite://"
|
||||||
self.config.WEBAPP["cache_dir"] = self.__dir
|
self.config.WEBAPP["cache_dir"] = self.__dir
|
||||||
|
|
||||||
init_database(self.config.BASE["database_uri"])
|
init_database(self.config.BASE["database_uri"])
|
||||||
@ -32,7 +30,6 @@ class SecretTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
shutil.rmtree(self.__dir)
|
shutil.rmtree(self.__dir)
|
||||||
os.remove(self.__dbfile)
|
|
||||||
|
|
||||||
def test_key(self):
|
def test_key(self):
|
||||||
app1 = create_application(self.config)
|
app1 = create_application(self.config)
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
|
||||||
#
|
#
|
||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017-2018 Alban 'spl0k' Féron
|
# Copyright (C) 2017-2020 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import io
|
|
||||||
import mutagen
|
import mutagen
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@ -18,7 +16,6 @@ import unittest
|
|||||||
|
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
from supysonic.db import init_database, release_database, Track, Artist, Folder
|
from supysonic.db import init_database, release_database, Track, Artist, Folder
|
||||||
from supysonic.managers.folder import FolderManager
|
from supysonic.managers.folder import FolderManager
|
||||||
@ -37,8 +34,8 @@ class WatcherTestConfig(TestConfig):
|
|||||||
|
|
||||||
class WatcherTestBase(unittest.TestCase):
|
class WatcherTestBase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.__dbfile = tempfile.mkstemp()[1]
|
self.__db = tempfile.mkstemp()
|
||||||
dburi = "sqlite:///" + self.__dbfile
|
dburi = "sqlite:///" + self.__db[1]
|
||||||
init_database(dburi)
|
init_database(dburi)
|
||||||
|
|
||||||
conf = WatcherTestConfig(dburi)
|
conf = WatcherTestConfig(dburi)
|
||||||
@ -48,7 +45,8 @@ class WatcherTestBase(unittest.TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
release_database()
|
release_database()
|
||||||
os.unlink(self.__dbfile)
|
os.close(self.__db[0])
|
||||||
|
os.remove(self.__db[1])
|
||||||
|
|
||||||
def _start(self):
|
def _start(self):
|
||||||
self.__watcher.start()
|
self.__watcher.start()
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
# coding: utf-8
|
|
||||||
#
|
|
||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019 Alban 'spl0k' Féron
|
# Copyright (C) 2019-2020 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@ -20,6 +19,7 @@ from supysonic.managers.folder import FolderManager
|
|||||||
from supysonic.scanner import Scanner
|
from supysonic.scanner import Scanner
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.platform == "win32", "Windows doesn't allow space-only filenames")
|
||||||
class Issue148TestCase(unittest.TestCase):
|
class Issue148TestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.__dir = tempfile.mkdtemp()
|
self.__dir = tempfile.mkdtemp()
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@ -18,6 +19,9 @@ from supysonic.managers.folder import FolderManager
|
|||||||
from supysonic.scanner import Scanner
|
from supysonic.scanner import Scanner
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
sys.platform == "win32", "Windows doesn't seem too allow badly encoded paths"
|
||||||
|
)
|
||||||
class Issue85TestCase(unittest.TestCase):
|
class Issue85TestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.__dir = tempfile.mkdtemp()
|
self.__dir = tempfile.mkdtemp()
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
# coding: utf-8
|
|
||||||
#
|
|
||||||
# This file is part of Supysonic.
|
# This file is part of Supysonic.
|
||||||
# Supysonic is a Python implementation of the Subsonic server API.
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2017-2018 Alban 'spl0k' Féron
|
# Copyright (C) 2017-2020 Alban 'spl0k' Féron
|
||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import unittest
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
@ -82,9 +80,10 @@ class TestBase(unittest.TestCase):
|
|||||||
__with_api__ = False
|
__with_api__ = False
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.__db = tempfile.mkstemp()
|
||||||
self.__dir = tempfile.mkdtemp()
|
self.__dir = tempfile.mkdtemp()
|
||||||
config = TestConfig(self.__with_webui__, self.__with_api__)
|
config = TestConfig(self.__with_webui__, self.__with_api__)
|
||||||
config.BASE["database_uri"] = "sqlite:"
|
config.BASE["database_uri"] = "sqlite:///" + self.__db[1]
|
||||||
config.WEBAPP["cache_dir"] = self.__dir
|
config.WEBAPP["cache_dir"] = self.__dir
|
||||||
|
|
||||||
init_database(config.BASE["database_uri"])
|
init_database(config.BASE["database_uri"])
|
||||||
@ -107,3 +106,5 @@ class TestBase(unittest.TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
release_database()
|
release_database()
|
||||||
shutil.rmtree(self.__dir)
|
shutil.rmtree(self.__dir)
|
||||||
|
os.close(self.__db[0])
|
||||||
|
os.remove(self.__db[1])
|
||||||
|
@ -12,6 +12,7 @@ import unittest
|
|||||||
from .api.test_lyrics import LyricsTestCase
|
from .api.test_lyrics import LyricsTestCase
|
||||||
from .base.test_lastfm import LastFmTestCase
|
from .base.test_lastfm import LastFmTestCase
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user