* improved unitesting (compat level for python 2.6 and unittest)

* preparation for SoftChroot (stubs and mocks)
This commit is contained in:
maxkoryukov
2016-02-03 06:30:09 +05:00
parent 7bff1b6c3c
commit 5da950f61b
10 changed files with 112 additions and 27 deletions
+4 -1
View File
@@ -152,7 +152,10 @@ def main():
headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db') headphones.DB_FILE = os.path.join(headphones.DATA_DIR, 'headphones.db')
# Read config and start logging # Read config and start logging
headphones.initialize(config_file) try:
headphones.initialize(config_file)
except headphones.exceptions.SoftChrootError as e:
raise SystemExit('FATAL ERROR')
if headphones.DAEMON: if headphones.DAEMON:
headphones.daemonize() headphones.daemonize()
+12 -2
View File
@@ -29,7 +29,8 @@ from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger from apscheduler.triggers.interval import IntervalTrigger
from headphones import versioncheck, logger from headphones import versioncheck, logger
import headphones.config import headphones.config
from headphones.softchroot import SoftChroot
import headphones.exceptions
# (append new extras to the end) # (append new extras to the end)
POSSIBLE_EXTRAS = [ POSSIBLE_EXTRAS = [
@@ -74,6 +75,7 @@ started = False
DATA_DIR = None DATA_DIR = None
CONFIG = None CONFIG = None
SOFT_CHROOT = None
DB_FILE = None DB_FILE = None
@@ -92,11 +94,11 @@ MIRRORLIST = ["musicbrainz.org", "headphones", "custom"]
UMASK = None UMASK = None
def initialize(config_file): def initialize(config_file):
with INIT_LOCK: with INIT_LOCK:
global CONFIG global CONFIG
global SOFT_CHROOT
global _INITIALIZED global _INITIALIZED
global CURRENT_VERSION global CURRENT_VERSION
global LATEST_VERSION global LATEST_VERSION
@@ -136,6 +138,14 @@ def initialize(config_file):
logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR, logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR,
verbose=VERBOSE) verbose=VERBOSE)
if CONFIG.SOFT_CHROOT:
# soft chroot defined, lets try to initialize:
try:
SOFT_CHROOT = SoftChroot(str(CONFIG.SOFT_CHROOT))
except exceptions.SoftChrootError as e:
logger.error("SoftChroot error: %s", e)
raise e
if not CONFIG.CACHE_DIR: if not CONFIG.CACHE_DIR:
# Put the cache dir in the data dir for now # Put the cache dir in the data dir for now
CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache') CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache')
+1 -1
View File
@@ -1,6 +1,6 @@
#import unittest #import unittest
#import mock #import mock
from unittest import TestCase from headphones.unittestcompat import TestCase
import headphones.albumart import headphones.albumart
+2 -1
View File
@@ -236,6 +236,7 @@ _CONFIG_DEFINITIONS = {
'SAB_USERNAME': (str, 'SABnzbd', ''), 'SAB_USERNAME': (str, 'SABnzbd', ''),
'SAMPLINGFREQUENCY': (int, 'General', 44100), 'SAMPLINGFREQUENCY': (int, 'General', 44100),
'SEARCH_INTERVAL': (int, 'General', 1440), 'SEARCH_INTERVAL': (int, 'General', 1440),
'SOFT_CHROOT': (path, 'General', ''),
'SONGKICK_APIKEY': (str, 'Songkick', 'nd1We7dFW2RqxPw8'), 'SONGKICK_APIKEY': (str, 'Songkick', 'nd1We7dFW2RqxPw8'),
'SONGKICK_ENABLED': (int, 'Songkick', 1), 'SONGKICK_ENABLED': (int, 'Songkick', 1),
'SONGKICK_FILTER_ENABLED': (int, 'Songkick', 0), 'SONGKICK_FILTER_ENABLED': (int, 'Songkick', 0),
@@ -308,7 +309,7 @@ class Config(object):
definition = _CONFIG_DEFINITIONS[key] definition = _CONFIG_DEFINITIONS[key]
if len(definition) == 3: if len(definition) == 3:
definition_type, section, default = definition definition_type, section, default = definition
else: elif len(definition) == 4:
definition_type, section, _, default = definition definition_type, section, _, default = definition
return key, definition_type, section, ini_key, default return key, definition_type, section, ini_key, default
+6 -22
View File
@@ -1,26 +1,17 @@
#import unittest #import unittest
import sys
import mock import mock
from unittest import TestCase from headphones.unittestcompat import TestCase
from mock import MagicMock from mock import MagicMock
import headphones.config import headphones.config
from headphones.config import path from headphones.config import path
def is26():
if sys.version_info[0] == 2 and sys.version_info[1] == 6:
return True
return False
class ConfigPathTest(TestCase): class ConfigPathTest(TestCase):
def test_path(self): def test_path(self):
p = path('/tmp') p = path('/tmp')
#fuckin python 2.6: self.assertIsInstance(p, path)
if not is26(): self.assertIsNotNone(p)
self.assertIsInstance(p, path)
self.assertIsNotNone(p)
self.assertTrue(True)
def test_path_call(self): def test_path_call(self):
s = '/tmp' s = '/tmp'
@@ -41,10 +32,7 @@ class ConfigPathTest(TestCase):
def test_path_repr(self): def test_path_repr(self):
s = '/tmp' s = '/tmp'
p1 = path(s) p1 = path(s)
#fuckin python 2.6: self.assertIn('headphones.config.path', p1.__repr__())
if not is26():
self.assertIn('headphones.config.path', p1.__repr__())
self.assertTrue(True)
# patch required, since Config works ower a # patch required, since Config works ower a
@@ -62,9 +50,7 @@ class ConfigTest(TestCase):
"""Test creating headphones.Config""" """Test creating headphones.Config"""
cf = headphones.config.Config('/tmp/notexist') cf = headphones.config.Config('/tmp/notexist')
#fuckin python 2.6: self.assertIsInstance(cf, headphones.config.Config)
if not is26():
self.assertIsInstance(cf, headphones.config.Config)
self.assertTrue(True) self.assertTrue(True)
def test_write(self, config_obj_fabric_mock): def test_write(self, config_obj_fabric_mock):
@@ -84,6 +70,4 @@ class ConfigTest(TestCase):
general_opts_set = conf_mock['General'].__setitem__.call_args_list general_opts_set = conf_mock['General'].__setitem__.call_args_list
general_opts_set = map(lambda x: x[0][0], general_opts_set) general_opts_set = map(lambda x: x[0][0], general_opts_set)
#fuckin python 2.6: self.assertIn('download_dir', general_opts_set, 'There is no download_dir in ConfigObj (submodule of Config)')
if not is26():
self.assertIn('download_dir', general_opts_set, 'There is no download_dir in ConfigObj (submodule of Config)')
+6
View File
@@ -24,3 +24,9 @@ class NewzbinAPIThrottled(HeadphonesException):
""" """
Newzbin has throttled us, deal with it Newzbin has throttled us, deal with it
""" """
class SoftChrootError(HeadphonesException):
"""
Fatal errors in SoftChroot module
"""
pass
+14
View File
@@ -0,0 +1,14 @@
import os
from headphones.exceptions import SoftChrootError
class SoftChroot(object):
def __init__(self, path):
path = path.strip()
if (not os.path.exists(path) or
not os.path.isdir(path)):
raise SoftChrootError('No such directory: %s' % path)
path = path.strip(os.path.sep) + os.path.sep
self.chroot = path
+38
View File
@@ -0,0 +1,38 @@
import os
import mock
from headphones.unittestcompat import TestCase
from mock import MagicMock
from headphones.softchroot import SoftChroot
from headphones.exceptions import SoftChrootError
class SoftChrootTest(TestCase):
def test_create(self):
""" create headphones.SoftChroot """
cf = SoftChroot('/tmp/')
self.assertIsInstance(cf, SoftChroot)
@mock.patch('headphones.config.ConfigObj', name='ConfigObjMock')
def test_create_on_file(self, config_obj_fabric_mock):
""" create SoftChroot on file, not a directory """
path = os.path.join('tmp', 'notexist', 'asdf', '11', '12', 'np', 'itsssss')
with self.assertRaises(SoftChrootError) as exc:
cf = SoftChroot(path)
self.assertRegexpMatches(str(exc.exception), r'No such directory')
self.assertRegexpMatches(str(exc.exception), path)
@mock.patch('headphones.softchroot', name='SoftChrootMock')
def test_create_on_file(self, config_obj_fabric_mock):
""" create SoftChroot on file, not a directory """
path = os.path.join('tmp', 'notexist', 'asdf', '11', '12', 'np', 'itsssss')
with self.assertRaises(SoftChrootError) as exc:
cf = SoftChroot(path)
self.assertRegexpMatches(str(exc.exception), r'No such directory')
self.assertRegexpMatches(str(exc.exception), path)
+28
View File
@@ -0,0 +1,28 @@
import sys
from unittest import TestCase as TC
def _is26():
if sys.version_info[0] == 2 and sys.version_info[1] == 6:
return True
return False
_dummy = _is26()
def _d(f):
def decorate(self, *args, **kw):
if _dummy:
return self.assertTrue(True)
return f(self, *args, **kw)
return decorate
class TestCase(TC):
"""
Wrapper for python 2.6 stubs
"""
@_d
def assertIsInstance(self, *args, **kw):
return super(TestCase, self).assertIsInstance(*args, **kw)
@_d
def assertIsIn(self, *args, **kw):
return super(TestCase, self).assertIsIn(*args, **kw)
+1
View File
@@ -1369,6 +1369,7 @@ class WebInterface(object):
# Need to convert EXTRAS to a dictionary we can pass to the config: # Need to convert EXTRAS to a dictionary we can pass to the config:
# it'll come in as a string like 2,5,6,8 # it'll come in as a string like 2,5,6,8
extra_munges = { extra_munges = {
"dj-mix": "dj_mix", "dj-mix": "dj_mix",
"mixtape/street": "mixtape_street" "mixtape/street": "mixtape_street"