mirror of
https://github.com/beetbox/beets.git
synced 2026-01-03 14:32:55 +01:00
tests: obviate on-disk test library
This commit is contained in:
parent
9993993cab
commit
4d0db3fcb4
4 changed files with 116 additions and 131 deletions
|
|
@ -126,6 +126,16 @@ class TestCase(unittest.TestCase):
|
|||
self.assertFalse(os.path.exists(path),
|
||||
'file exists: %s' % path)
|
||||
|
||||
class LibTestCase(TestCase):
|
||||
"""A test case that includes an in-memory library object (`lib`) and
|
||||
an item added to the library (`i`).
|
||||
"""
|
||||
def setUp(self):
|
||||
super(LibTestCase, self).setUp()
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.i = item(self.lib)
|
||||
|
||||
|
||||
|
||||
|
||||
# Mock timing.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -33,28 +33,11 @@ from beets import config
|
|||
|
||||
TEMP_LIB = os.path.join(_common.RSRC, 'test_copy.blb')
|
||||
|
||||
def lib():
|
||||
shutil.copy(os.path.join(_common.RSRC, 'test.blb'), TEMP_LIB)
|
||||
return beets.library.Library(TEMP_LIB)
|
||||
def remove_lib():
|
||||
if os.path.exists(TEMP_LIB):
|
||||
os.unlink(TEMP_LIB)
|
||||
def boracay(l):
|
||||
return beets.library.Item(l,
|
||||
**l._connection().execute('select * from items where id=3').fetchone()
|
||||
)
|
||||
# Shortcut to path normalization.
|
||||
np = util.normpath
|
||||
|
||||
class LoadTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(LoadTest, self).setUp()
|
||||
self.lib = lib()
|
||||
self.i = boracay(self.lib)
|
||||
def tearDown(self):
|
||||
super(LoadTest, self).tearDown()
|
||||
self.lib._connection().close()
|
||||
remove_lib()
|
||||
|
||||
class LoadTest(_common.LibTestCase):
|
||||
def test_load_restores_data_from_db(self):
|
||||
original_title = self.i.title
|
||||
self.i.title = 'something'
|
||||
|
|
@ -63,25 +46,18 @@ class LoadTest(_common.TestCase):
|
|||
|
||||
def test_load_clears_dirty_flags(self):
|
||||
self.i.artist = 'something'
|
||||
self.assertTrue('artist' in self.i._dirty)
|
||||
self.i.load()
|
||||
self.assertTrue('artist' not in self.i._dirty)
|
||||
|
||||
class StoreTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(StoreTest, self).setUp()
|
||||
self.lib = lib()
|
||||
self.i = boracay(self.lib)
|
||||
def tearDown(self):
|
||||
super(StoreTest, self).tearDown()
|
||||
self.lib._connection().close()
|
||||
remove_lib()
|
||||
|
||||
class StoreTest(_common.LibTestCase):
|
||||
def test_store_changes_database_value(self):
|
||||
self.i.year = 1987
|
||||
self.i.store()
|
||||
new_year = self.lib._connection().execute(
|
||||
'select year from items where '
|
||||
'title="Boracay"').fetchone()['year']
|
||||
'title="the title"').fetchone()['year']
|
||||
self.assertEqual(new_year, 1987)
|
||||
|
||||
def test_store_only_writes_dirty_fields(self):
|
||||
|
|
@ -90,7 +66,7 @@ class StoreTest(_common.TestCase):
|
|||
self.i.store()
|
||||
new_genre = self.lib._connection().execute(
|
||||
'select genre from items where '
|
||||
'title="Boracay"').fetchone()['genre']
|
||||
'title="the title"').fetchone()['genre']
|
||||
self.assertEqual(new_genre, original_genre)
|
||||
|
||||
def test_store_clears_dirty_flags(self):
|
||||
|
|
@ -98,14 +74,12 @@ class StoreTest(_common.TestCase):
|
|||
self.i.store()
|
||||
self.assertTrue('composer' not in self.i._dirty)
|
||||
|
||||
|
||||
class AddTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(AddTest, self).setUp()
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.i = item()
|
||||
def tearDown(self):
|
||||
super(AddTest, self).tearDown()
|
||||
self.lib._connection().close()
|
||||
|
||||
def test_item_add_inserts_row(self):
|
||||
self.lib.add(self.i)
|
||||
|
|
@ -122,21 +96,14 @@ class AddTest(_common.TestCase):
|
|||
'where composer="the composer"').fetchone()['grouping']
|
||||
self.assertEqual(new_grouping, self.i.grouping)
|
||||
|
||||
class RemoveTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(RemoveTest, self).setUp()
|
||||
self.lib = lib()
|
||||
self.i = boracay(self.lib)
|
||||
def tearDown(self):
|
||||
super(RemoveTest, self).tearDown()
|
||||
self.lib._connection().close()
|
||||
remove_lib()
|
||||
|
||||
class RemoveTest(_common.LibTestCase):
|
||||
def test_remove_deletes_from_db(self):
|
||||
self.i.remove()
|
||||
c = self.lib._connection().execute('select * from items where id=3')
|
||||
c = self.lib._connection().execute('select * from items')
|
||||
self.assertEqual(c.fetchone(), None)
|
||||
|
||||
|
||||
class GetSetTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(GetSetTest, self).setUp()
|
||||
|
|
@ -157,6 +124,7 @@ class GetSetTest(_common.TestCase):
|
|||
def test_invalid_field_raises_attributeerror(self):
|
||||
self.assertRaises(AttributeError, getattr, self.i, 'xyzzy')
|
||||
|
||||
|
||||
class DestinationTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(DestinationTest, self).setUp()
|
||||
|
|
@ -458,6 +426,7 @@ class DestinationTest(_common.TestCase):
|
|||
dest = self.i.destination(platform='linux2', fragment=True)
|
||||
self.assertEqual(dest, u'foo.caf\xe9')
|
||||
|
||||
|
||||
class PathFormattingMixin(object):
|
||||
"""Utilities for testing path formatting."""
|
||||
def _setf(self, fmt):
|
||||
|
|
@ -467,6 +436,7 @@ class PathFormattingMixin(object):
|
|||
i = self.i
|
||||
self.assertEqual(i.destination(pathmod=posixpath), dest)
|
||||
|
||||
|
||||
class DestinationFunctionTest(_common.TestCase, PathFormattingMixin):
|
||||
def setUp(self):
|
||||
super(DestinationFunctionTest, self).setUp()
|
||||
|
|
@ -518,6 +488,7 @@ class DestinationFunctionTest(_common.TestCase, PathFormattingMixin):
|
|||
self._setf(u'%foo{bar}')
|
||||
self._assert_dest('/base/%foo{bar}')
|
||||
|
||||
|
||||
class DisambiguationTest(_common.TestCase, PathFormattingMixin):
|
||||
def setUp(self):
|
||||
super(DisambiguationTest, self).setUp()
|
||||
|
|
@ -578,6 +549,7 @@ class DisambiguationTest(_common.TestCase, PathFormattingMixin):
|
|||
self._setf(u'foo%aunique{albumartist album,albumtype}/$title')
|
||||
self._assert_dest('/base/foo [foo_bar]/the title', self.i1)
|
||||
|
||||
|
||||
class PathConversionTest(_common.TestCase):
|
||||
def test_syspath_windows_format(self):
|
||||
path = ntpath.join('a', 'b', 'c')
|
||||
|
|
@ -608,6 +580,7 @@ class PathConversionTest(_common.TestCase):
|
|||
outpath = self._windows_bytestring_path(path)
|
||||
self.assertEqual(outpath, u'C:\\caf\xe9'.encode('utf8'))
|
||||
|
||||
|
||||
class PluginDestinationTest(_common.TestCase):
|
||||
# Mock the plugins.template_values(item) function.
|
||||
def _template_values(self, item):
|
||||
|
|
@ -651,6 +624,7 @@ class PluginDestinationTest(_common.TestCase):
|
|||
}
|
||||
self._assert_dest('the artist bar_baz')
|
||||
|
||||
|
||||
class MigrationTest(_common.TestCase):
|
||||
"""Tests the ability to change the database schema between
|
||||
versions.
|
||||
|
|
@ -758,6 +732,7 @@ class MigrationTest(_common.TestCase):
|
|||
album = c.fetchone()
|
||||
self.assertEqual(album['albumartist'], 'theartist')
|
||||
|
||||
|
||||
class AlbumInfoTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(AlbumInfoTest, self).setUp()
|
||||
|
|
@ -845,6 +820,7 @@ class AlbumInfoTest(_common.TestCase):
|
|||
self.i.remove()
|
||||
self.assertEqual(len(self.lib.albums()), 0)
|
||||
|
||||
|
||||
class ArtDestinationTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(ArtDestinationTest, self).setUp()
|
||||
|
|
@ -871,6 +847,7 @@ class ArtDestinationTest(_common.TestCase):
|
|||
art = self.ai.art_destination('something.jpg')
|
||||
self.assert_('artYimage' in art)
|
||||
|
||||
|
||||
class PathStringTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(PathStringTest, self).setUp()
|
||||
|
|
@ -954,6 +931,7 @@ class PathStringTest(_common.TestCase):
|
|||
alb = self.lib.get_album(alb.id)
|
||||
self.assert_(isinstance(alb.artpath, str))
|
||||
|
||||
|
||||
class PathTruncationTest(_common.TestCase):
|
||||
def test_truncate_bytestring(self):
|
||||
p = util.truncate_path('abcde/fgh', posixpath, 4)
|
||||
|
|
@ -967,6 +945,7 @@ class PathTruncationTest(_common.TestCase):
|
|||
p = util.truncate_path(u'abcde/fgh.ext', posixpath, 5)
|
||||
self.assertEqual(p, u'abcde/f.ext')
|
||||
|
||||
|
||||
class MtimeTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(MtimeTest, self).setUp()
|
||||
|
|
@ -1001,6 +980,7 @@ class MtimeTest(_common.TestCase):
|
|||
self.i.read()
|
||||
self.assertGreaterEqual(self.i.mtime, self._mtime())
|
||||
|
||||
|
||||
class ImportTimeTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(ImportTimeTest, self).setUp()
|
||||
|
|
@ -1016,8 +996,10 @@ class ImportTimeTest(_common.TestCase):
|
|||
self.singleton = item(self.lib)
|
||||
self.assertGreater(self.singleton.added, 0)
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='suite')
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import beets.library
|
|||
|
||||
pqp = beets.library.parse_query_part
|
||||
|
||||
some_item = _common.item()
|
||||
|
||||
class QueryParseTest(unittest.TestCase):
|
||||
def test_one_basic_term(self):
|
||||
|
|
@ -70,10 +69,11 @@ class QueryParseTest(unittest.TestCase):
|
|||
r = ('year', '1999..2010', beets.library.NumericQuery)
|
||||
self.assertEqual(pqp(q), r)
|
||||
|
||||
|
||||
class AnyFieldQueryTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.lib.add(some_item)
|
||||
_common.item(self.lib)
|
||||
|
||||
def test_no_restriction(self):
|
||||
q = beets.library.AnyFieldQuery('title', beets.library.ITEM_KEYS,
|
||||
|
|
@ -90,25 +90,44 @@ class AnyFieldQueryTest(unittest.TestCase):
|
|||
beets.library.SubstringQuery)
|
||||
self.assertEqual(self.lib.items(q).get(), None)
|
||||
|
||||
# Convenient asserts for matching items.
|
||||
|
||||
class AssertsMixin(object):
|
||||
def assert_matched(self, results, titles):
|
||||
self.assertEqual([i.title for i in results], titles)
|
||||
|
||||
|
||||
# A test case class providing a library with some dummy data and some
|
||||
# assertions involving that data.
|
||||
class DummyDataTestCase(_common.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
super(DummyDataTestCase, self).setUp()
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
items = [_common.item() for _ in range(3)]
|
||||
items[0].title = 'foo bar'
|
||||
items[0].artist = 'one'
|
||||
items[0].album = 'baz'
|
||||
items[0].year = 2001
|
||||
items[1].title = 'baz qux'
|
||||
items[1].artist = 'two'
|
||||
items[1].album = 'baz'
|
||||
items[1].year = 2002
|
||||
items[2].title = 'beets 4 eva'
|
||||
items[2].artist = 'three'
|
||||
items[2].album = 'foo'
|
||||
items[2].year = 2003
|
||||
for item in items:
|
||||
self.lib.add(item)
|
||||
self.lib.add_album(items[:2])
|
||||
|
||||
def assert_matched_all(self, results):
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
'foo bar',
|
||||
'baz qux',
|
||||
'beets 4 eva',
|
||||
])
|
||||
|
||||
class GetTest(unittest.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
self.lib = beets.library.Library(
|
||||
os.path.join(_common.RSRC, 'test.blb')
|
||||
)
|
||||
|
||||
class GetTest(DummyDataTestCase):
|
||||
def test_get_empty(self):
|
||||
q = ''
|
||||
results = self.lib.items(q)
|
||||
|
|
@ -120,24 +139,24 @@ class GetTest(unittest.TestCase, AssertsMixin):
|
|||
self.assert_matched_all(results)
|
||||
|
||||
def test_get_one_keyed_term(self):
|
||||
q = 'artist:Lil'
|
||||
q = 'title:qux'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
self.assert_matched(results, ['baz qux'])
|
||||
|
||||
def test_get_one_keyed_regexp(self):
|
||||
q = r'artist::L.+y'
|
||||
q = r'artist::t.+r'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
self.assert_matched(results, ['beets 4 eva'])
|
||||
|
||||
def test_get_one_unkeyed_term(self):
|
||||
q = 'Terry'
|
||||
q = 'three'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Boracay'])
|
||||
self.assert_matched(results, ['beets 4 eva'])
|
||||
|
||||
def test_get_one_unkeyed_regexp(self):
|
||||
q = r':y$'
|
||||
q = r':x$'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Boracay'])
|
||||
self.assert_matched(results, ['baz qux'])
|
||||
|
||||
def test_get_no_matches(self):
|
||||
q = 'popebear'
|
||||
|
|
@ -152,101 +171,91 @@ class GetTest(unittest.TestCase, AssertsMixin):
|
|||
self.assert_matched(results, [])
|
||||
|
||||
def test_term_case_insensitive(self):
|
||||
q = 'UNCoVER'
|
||||
q = 'oNE'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Lovers Who Uncover'])
|
||||
self.assert_matched(results, ['foo bar'])
|
||||
|
||||
def test_regexp_case_sensitive(self):
|
||||
q = r':UNCoVER'
|
||||
q = r':oNE'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [])
|
||||
q = r':Uncover'
|
||||
q = r':one'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Lovers Who Uncover'])
|
||||
self.assert_matched(results, ['foo bar'])
|
||||
|
||||
def test_term_case_insensitive_with_key(self):
|
||||
q = 'album:stiLL'
|
||||
q = 'artist:thrEE'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
self.assert_matched(results, ['beets 4 eva'])
|
||||
|
||||
def test_key_case_insensitive(self):
|
||||
q = 'ArTiST:Allen'
|
||||
q = 'ArTiST:three'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
self.assert_matched(results, ['beets 4 eva'])
|
||||
|
||||
def test_unkeyed_term_matches_multiple_columns(self):
|
||||
q = 'little'
|
||||
q = 'baz'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
'foo bar',
|
||||
'baz qux',
|
||||
])
|
||||
|
||||
def test_unkeyed_regexp_matches_multiple_columns(self):
|
||||
q = r':^T'
|
||||
q = r':z$'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
'foo bar',
|
||||
'baz qux',
|
||||
])
|
||||
|
||||
def test_keyed_term_matches_only_one_column(self):
|
||||
q = 'artist:little'
|
||||
q = 'title:baz'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
self.assert_matched(results, ['baz qux'])
|
||||
|
||||
def test_keyed_regexp_matches_only_one_column(self):
|
||||
q = r'album::\sS'
|
||||
q = r'title::baz'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Lovers Who Uncover',
|
||||
'baz qux',
|
||||
])
|
||||
|
||||
def test_multiple_terms_narrow_search(self):
|
||||
q = 'little ones'
|
||||
q = 'qux baz'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
'baz qux',
|
||||
])
|
||||
|
||||
def test_multiple_regexps_narrow_search(self):
|
||||
q = r':\sS :^T'
|
||||
q = r':baz :qux'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Lovers Who Uncover'])
|
||||
self.assert_matched(results, ['baz qux'])
|
||||
|
||||
def test_mixed_terms_regexps_narrow_search(self):
|
||||
q = r':\sS lily'
|
||||
q = r':baz qux'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
self.assert_matched(results, ['baz qux'])
|
||||
|
||||
def test_single_year(self):
|
||||
q = 'year:2006'
|
||||
q = 'year:2001'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Lovers Who Uncover',
|
||||
])
|
||||
self.assert_matched(results, ['foo bar'])
|
||||
|
||||
def test_year_range(self):
|
||||
q = 'year:2000..2010'
|
||||
q = 'year:2000..2002'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'foo bar',
|
||||
'baz qux',
|
||||
])
|
||||
|
||||
def test_bad_year(self):
|
||||
q = 'year:delete from items'
|
||||
self.assertRaises(ValueError, self.lib.items, q)
|
||||
|
||||
|
||||
class MemoryGetTest(unittest.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
self.album_item = _common.item()
|
||||
|
|
@ -315,6 +324,7 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin):
|
|||
results = self.lib.items(q)
|
||||
self.assertFalse(results)
|
||||
|
||||
|
||||
class MatchTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.item = _common.item()
|
||||
|
|
@ -359,6 +369,7 @@ class MatchTest(unittest.TestCase):
|
|||
q = beets.library.NumericQuery('bitrate', '200000..300000')
|
||||
self.assertFalse(q.match(self.item))
|
||||
|
||||
|
||||
class PathQueryTest(unittest.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
|
|
@ -418,45 +429,25 @@ class PathQueryTest(unittest.TestCase, AssertsMixin):
|
|||
results = self.lib.items(q)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
class BrowseTest(unittest.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
self.lib = beets.library.Library(
|
||||
os.path.join(_common.RSRC, 'test.blb')
|
||||
)
|
||||
|
||||
def test_album_list(self):
|
||||
albums = list(self.lib.albums())
|
||||
album_names = [a.album for a in albums]
|
||||
for aname in ['Alright, Still', 'Person Pitch', 'Sing Song',
|
||||
'Terry Tales & Fallen Gates EP']:
|
||||
self.assert_(aname in album_names)
|
||||
self.assertEqual(len(albums), 4)
|
||||
|
||||
def test_item_list(self):
|
||||
items = self.lib.items()
|
||||
self.assert_matched(items, [
|
||||
'Littlest Things',
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
class DefaultSearchFieldsTest(DummyDataTestCase):
|
||||
def test_albums_matches_album(self):
|
||||
albums = list(self.lib.albums('person'))
|
||||
albums = list(self.lib.albums('baz'))
|
||||
self.assertEqual(len(albums), 1)
|
||||
|
||||
def test_albums_matches_albumartist(self):
|
||||
albums = list(self.lib.albums('panda'))
|
||||
albums = list(self.lib.albums(['album artist']))
|
||||
self.assertEqual(len(albums), 1)
|
||||
|
||||
def test_items_matches_title(self):
|
||||
items = self.lib.items('boracay')
|
||||
self.assert_matched(items, ['Boracay'])
|
||||
items = self.lib.items('beets')
|
||||
self.assert_matched(items, ['beets 4 eva'])
|
||||
|
||||
def test_items_does_not_match_year(self):
|
||||
items = self.lib.items('2007')
|
||||
items = self.lib.items('2001')
|
||||
self.assert_matched(items, [])
|
||||
|
||||
|
||||
class StringParseTest(unittest.TestCase):
|
||||
def test_single_field_query(self):
|
||||
q = beets.library.AndQuery.from_string(u'albumtype:soundtrack')
|
||||
|
|
@ -466,8 +457,10 @@ class StringParseTest(unittest.TestCase):
|
|||
self.assertEqual(subq.field, 'albumtype')
|
||||
self.assertEqual(subq.pattern, 'soundtrack')
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='suite')
|
||||
|
|
|
|||
Loading…
Reference in a new issue