Refactor config_mock module to not use globals and fix tests

This commit is contained in:
ZyX 2014-08-05 13:39:01 +04:00
parent 66e0999147
commit 0f4e1bafda
5 changed files with 420 additions and 392 deletions

View File

@ -3,37 +3,53 @@ from threading import Lock
from powerline.renderer import Renderer from powerline.renderer import Renderer
from powerline.lib.config import ConfigLoader from powerline.lib.config import ConfigLoader
from powerline import Powerline from powerline import Powerline
from tests.lib import Args, replace_attr
from copy import deepcopy from copy import deepcopy
from time import sleep from time import sleep
from functools import wraps from functools import wraps
import os
access_log = [] class TestHelpers(object):
access_lock = Lock() def __init__(self, config):
self.config = config
self.access_log = []
self.access_lock = Lock()
def loader_condition(self, path):
return (path in self.config) and path
def load_json_config(config_file_path, *args, **kwargs): def find_config_files(self, cfg_path, config_loader, loader_callback):
global access_log if cfg_path.endswith('.json'):
with access_lock: cfg_path = cfg_path[:-5]
access_log.append(config_file_path) if cfg_path.startswith('/'):
try: cfg_path = cfg_path.lstrip('/')
return deepcopy(config_container['config'][config_file_path]) with self.access_lock:
except KeyError: self.access_log.append('check:' + cfg_path)
raise IOError(config_file_path) if cfg_path in self.config:
yield cfg_path
else:
if config_loader:
config_loader.register_missing(self.loader_condition, loader_callback, cfg_path)
raise IOError(('fcf:' if cfg_path.endswith('raise') else '') + cfg_path)
def load_json_config(self, config_file_path, *args, **kwargs):
if config_file_path.endswith('.json'):
config_file_path = config_file_path[:-5]
if config_file_path.startswith('/'):
config_file_path = config_file_path.lstrip('/')
with self.access_lock:
self.access_log.append('load:' + config_file_path)
try:
return deepcopy(self.config[config_file_path])
except KeyError:
raise IOError(config_file_path)
def _find_config_file(config, search_paths, config_file): def pop_events(self):
if config_file.endswith('raise') and config_file not in config: with self.access_lock:
raise IOError('fcf:' + config_file) r = self.access_log[:]
return config_file self.access_log = []
return r
def pop_events():
global access_log
with access_lock:
r = access_log[:]
access_log = []
return r
def log_call(func): def log_call(func):
@ -44,7 +60,7 @@ def log_call(func):
return ret return ret
class Watcher(object): class TestWatcher(object):
events = set() events = set()
lock = Lock() lock = Lock()
@ -109,20 +125,46 @@ class EvenSimplerRenderer(Renderer):
class TestPowerline(Powerline): class TestPowerline(Powerline):
_created = False _created = False
def __init__(self, _helpers, **kwargs):
super(TestPowerline, self).__init__(**kwargs)
self._helpers = _helpers
self.find_config_files = _helpers.find_config_files
@staticmethod @staticmethod
def get_local_themes(local_themes): def get_local_themes(local_themes):
return local_themes return local_themes
@staticmethod
def get_config_paths():
return ['']
def _will_create_renderer(self): def _will_create_renderer(self):
return self.cr_kwargs return self.cr_kwargs
def _pop_events(self):
return self._helpers.pop_events()
renderer = EvenSimplerRenderer renderer = EvenSimplerRenderer
def get_powerline(**kwargs): class TestConfigLoader(ConfigLoader):
def __init__(self, _helpers, **kwargs):
watcher = TestWatcher()
super(TestConfigLoader, self).__init__(
load=_helpers.load_json_config,
watcher=watcher,
watcher_type='test',
**kwargs
)
def get_powerline(config, **kwargs):
helpers = TestHelpers(config)
return get_powerline_raw( return get_powerline_raw(
helpers,
TestPowerline, TestPowerline,
_helpers=helpers,
ext='test', ext='test',
renderer_module='tests.lib.config_mock', renderer_module='tests.lib.config_mock',
logger=Logger(), logger=Logger(),
@ -130,45 +172,42 @@ def get_powerline(**kwargs):
) )
def get_powerline_raw(PowerlineClass, **kwargs): def select_renderer(simpler_renderer=False):
global renderer global renderer
watcher = Watcher() renderer = EvenSimplerRenderer if simpler_renderer else SimpleRenderer
if kwargs.pop('simpler_renderer', False):
renderer = EvenSimplerRenderer
else: def get_powerline_raw(helpers, PowerlineClass, **kwargs):
renderer = SimpleRenderer if not isinstance(helpers, TestHelpers):
helpers = TestHelpers(helpers)
select_renderer(kwargs.pop('simpler_renderer', False))
pl = PowerlineClass( pl = PowerlineClass(
config_loader=ConfigLoader( config_loader=TestConfigLoader(
load=load_json_config, _helpers=helpers,
watcher=watcher,
watcher_type='test',
run_once=kwargs.get('run_once') run_once=kwargs.get('run_once')
), ),
**kwargs **kwargs
) )
pl._watcher = watcher pl._watcher = pl.config_loader.watcher
return pl return pl
config_container = None def swap_attributes(config, powerline_module):
return replace_attr(powerline_module, 'os', Args(
path=Args(
def swap_attributes(cfg_container, powerline_module, replaces): isfile=lambda path: path.lstrip('/').replace('.json', '') in config,
global config_container join=os.path.join,
config_container = cfg_container expanduser=lambda path: path,
if not replaces: realpath=lambda path: path,
replaces = { dirname=os.path.dirname,
'_find_config_file': lambda *args: _find_config_file(config_container['config'], *args), ),
} environ={},
for attr, val in replaces.items(): ))
old_val = getattr(powerline_module, attr)
setattr(powerline_module, attr, val)
replaces[attr] = old_val
return replaces
def add_watcher_events(p, *args, **kwargs): def add_watcher_events(p, *args, **kwargs):
p._watcher._reset(args) if isinstance(p._watcher, TestWatcher):
p._watcher._reset(args)
while not p._will_create_renderer(): while not p._will_create_renderer():
sleep(kwargs.get('interval', 0.1)) sleep(kwargs.get('interval', 0.1))
if not kwargs.get('wait', True): if not kwargs.get('wait', True):

View File

@ -71,6 +71,7 @@ class TestParser(TestCase):
'-c', 'common.spaces=4', '-c', 'common.spaces=4',
'-t', 'default.segment_data.hostname.before=H:', '-t', 'default.segment_data.hostname.before=H:',
'-p', '.', '-p', '.',
'-p', '..',
'-R', 'smth={"abc":"def"}', '-R', 'smth={"abc":"def"}',
], { ], {
'ext': ['shell'], 'ext': ['shell'],
@ -90,7 +91,7 @@ class TestParser(TestCase):
} }
} }
}, },
'config_path': ['.'], 'config_path': ['.', '..'],
'renderer_arg': {'smth': {'abc': 'def'}}, 'renderer_arg': {'smth': {'abc': 'def'}},
}), }),
(['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}), (['shell', '-R', 'arg=true'], {'ext': ['shell'], 'renderer_arg': {'arg': True}}),

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals
from powerline import Powerline from powerline import Powerline
from tests import TestCase from tests import TestCase
from tests.lib.config_mock import select_renderer
from shutil import rmtree from shutil import rmtree
import os import os
import json import json
@ -114,6 +115,7 @@ class WithConfigTree(object):
mkdir_recursive(os.path.dirname(fname)) mkdir_recursive(os.path.dirname(fname))
with open(fname, 'w') as F: with open(fname, 'w') as F:
json.dump(v, F) json.dump(v, F)
select_renderer(simpler_renderer=True)
self.p = TestPowerline( self.p = TestPowerline(
ext='test', ext='test',
renderer_module='tests.lib.config_mock', renderer_module='tests.lib.config_mock',

View File

@ -1,11 +1,12 @@
# vim:fileencoding=utf-8:noet # vim:fileencoding=utf-8:noet
from __future__ import unicode_literals from __future__ import unicode_literals
import powerline as powerline_module
from time import sleep from time import sleep
from tests import TestCase
from tests.lib import replace_item
from tests.lib.config_mock import swap_attributes, get_powerline, pop_events, add_watcher_events
from copy import deepcopy from copy import deepcopy
from functools import wraps
from tests import TestCase
from tests.lib.config_mock import get_powerline, add_watcher_events
config = { config = {
@ -93,201 +94,192 @@ config = {
} }
def with_new_config(func):
@wraps(func)
def f(self):
return func(self, deepcopy(config))
return f
class TestConfigReload(TestCase): class TestConfigReload(TestCase):
def assertAccessEvents(self, *args): def assertAccessEvents(self, p, *args):
self.assertEqual(set(pop_events()), set(args)) events = set()
for event in args:
if ':' not in event:
events.add('check:' + event)
events.add('load:' + event)
else:
events.add(event)
self.assertEqual(set(p._pop_events()), events)
def test_noreload(self): @with_new_config
with get_powerline(run_once=True) as p: def test_noreload(self, config):
with replace_item(globals(), 'config', deepcopy(config)): with get_powerline(config, run_once=True) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1 config['config']['common']['spaces'] = 1
add_watcher_events(p, 'config', wait=False, interval=0.05) add_watcher_events(p, 'config', wait=False, interval=0.05)
# When running once thread should not start # When running once thread should not start
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents() self.assertAccessEvents(p)
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
# Without the following assertion test_reload_colors may fail for
# unknown reason (with AssertionError telling about “config” accessed
# one more time then needed)
pop_events()
def test_reload_main(self): @with_new_config
with get_powerline(run_once=False) as p: def test_reload_main(self, config):
with replace_item(globals(), 'config', deepcopy(config)): with get_powerline(config, run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['common']['spaces'] = 1 config['config']['common']['spaces'] = 1
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config') self.assertAccessEvents(p, 'config')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = 'nonexistent' config['config']['ext']['test']['theme'] = 'nonexistent'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config', 'themes/test/nonexistent') self.assertAccessEvents(p, 'config', 'check:themes/test/nonexistent')
# It should normally handle file missing error # It should normally handle file missing error
self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent']) self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent'])
config['config']['ext']['test']['theme'] = 'default' config['config']['ext']['test']['theme'] = 'default'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config', 'themes/test/default') self.assertAccessEvents(p, 'config', 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['colorscheme'] = 'nonexistent' config['config']['ext']['test']['colorscheme'] = 'nonexistent'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config', 'colorschemes/nonexistent', 'colorschemes/test/__main__', 'colorschemes/test/nonexistent') self.assertAccessEvents(p, 'config', 'check:colorschemes/nonexistent', 'check:colorschemes/test/__main__', 'check:colorschemes/test/nonexistent')
# It should normally handle file missing error # It should normally handle file missing error
self.assertEqual(p.logger._pop_msgs(), [ self.assertEqual(p.logger._pop_msgs(), [
'exception:test:powerline:Failed to load colorscheme: colorschemes/nonexistent', 'exception:test:powerline:Failed to load colorscheme: colorschemes/nonexistent',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/__main__', 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/__main__',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/nonexistent', 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/nonexistent',
'exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent' 'exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent'
]) ])
config['config']['ext']['test']['colorscheme'] = '2' config['config']['ext']['test']['colorscheme'] = '2'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>><None None None>') self.assertEqual(p.render(), '<2 3 1> s <3 4 False>>><1 4 4>g <4 False False>>><None None None>')
self.assertAccessEvents('config', 'colorschemes/2', 'colorschemes/test/__main__', 'colorschemes/test/2') self.assertAccessEvents(p, 'config', 'check:colorschemes/2', 'check:colorschemes/test/__main__', 'colorschemes/test/2')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
config['config']['ext']['test']['theme'] = '2' config['config']['ext']['test']['theme'] = '2'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>') self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>')
self.assertAccessEvents('config', 'themes/test/2') self.assertAccessEvents(p, 'config', 'themes/test/2')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.renderer.local_themes, None) self.assertEqual(p.renderer.local_themes, None)
config['config']['ext']['test']['local_themes'] = 'something' config['config']['ext']['test']['local_themes'] = 'something'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>') self.assertEqual(p.render(), '<2 3 1> t <3 4 False>>><1 4 4>b <4 False False>>><None None None>')
self.assertAccessEvents('config') self.assertAccessEvents(p, 'config')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.renderer.local_themes, 'something') self.assertEqual(p.renderer.local_themes, 'something')
pop_events()
def test_reload_unexistent(self): @with_new_config
with get_powerline(run_once=False) as p: def test_reload_unexistent(self, config):
with replace_item(globals(), 'config', deepcopy(config)): with get_powerline(config, run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['config']['ext']['test']['colorscheme'] = 'nonexistentraise' config['config']['ext']['test']['colorscheme'] = 'nonexistentraise'
add_watcher_events(p, 'config') add_watcher_events(p, 'config')
# It may appear that p.logger._pop_msgs() is called after given # It may appear that p.logger._pop_msgs() is called after given
# exception is added to the mesagges, but before config_loader # exception is added to the mesagges, but before config_loader
# exception was added (this one: # exception was added (this one:
# “exception:test:config_loader:Error while running condition # “exception:test:config_loader:Error while running condition
# function for key colorschemes/test/nonexistentraise: # function for key colorschemes/test/nonexistentraise:
# fcf:colorschemes/test/nonexistentraise”). # fcf:colorschemes/test/nonexistentraise”).
# sleep(0.1) # sleep(0.1)
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
# For colorschemes/{test/,}*raise find_config_file raises # For colorschemes/{test/,}*raise find_config_file raises
# IOError, but it does not do so for colorschemes/test/__main__, # IOError, but it does not do so for check:colorschemes/test/__main__,
# so powerline is trying to load this, but not other # so powerline is trying to load this, but not other
# colorschemes/* # colorschemes/*
self.assertAccessEvents('config', 'colorschemes/test/__main__') self.assertAccessEvents(p, 'config', 'check:colorschemes/test/__main__', 'check:colorschemes/nonexistentraise', 'check:colorschemes/test/nonexistentraise')
self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs())
config['colorschemes/nonexistentraise'] = {} config['colorschemes/nonexistentraise'] = {}
config['colorschemes/test/nonexistentraise'] = { config['colorschemes/test/nonexistentraise'] = {
'groups': { 'groups': {
"str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]}, "str1": {"fg": "col1", "bg": "col3", "attr": ["bold"]},
"str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]}, "str2": {"fg": "col2", "bg": "col4", "attr": ["underline"]},
}, },
} }
while not p._will_create_renderer(): while not p._will_create_renderer():
sleep(0.1) sleep(0.1)
self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><2 4 4>g<4 False False>>><None None None>')
# Same as above # Same as above
self.assertAccessEvents('colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'colorschemes/test/__main__') self.assertAccessEvents(p, 'colorschemes/nonexistentraise', 'colorschemes/test/nonexistentraise', 'check:colorschemes/test/__main__')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
def test_reload_colors(self): @with_new_config
with get_powerline(run_once=False) as p: def test_reload_colors(self, config):
with replace_item(globals(), 'config', deepcopy(config)): with get_powerline(config, run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['colors']['colors']['col1'] = 5 config['colors']['colors']['col1'] = 5
add_watcher_events(p, 'colors') add_watcher_events(p, 'colors')
self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<5 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('colors') self.assertAccessEvents(p, 'colors')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
def test_reload_colorscheme(self): @with_new_config
with get_powerline(run_once=False) as p: def test_reload_colorscheme(self, config):
with replace_item(globals(), 'config', deepcopy(config)): with get_powerline(config, run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3' config['colorschemes/test/default']['groups']['str1']['bg'] = 'col3'
add_watcher_events(p, 'colorschemes/test/default') add_watcher_events(p, 'colorschemes/test/default')
self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 3 1> s<3 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default') self.assertAccessEvents(p, 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
def test_reload_theme(self): @with_new_config
with get_powerline(run_once=False) as p: def test_reload_theme(self, config):
with replace_item(globals(), 'config', deepcopy(config)): with get_powerline(config, run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default') add_watcher_events(p, 'themes/test/default')
self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('themes/test/default') self.assertAccessEvents(p, 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
pop_events()
def test_reload_theme_main(self): @with_new_config
with replace_item(globals(), 'config', deepcopy(config)): def test_reload_theme_main(self, config):
config['config']['common']['interval'] = None config['config']['common']['interval'] = None
with get_powerline(run_once=False) as p: with get_powerline(config, run_once=False) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False) add_watcher_events(p, 'themes/test/default', wait=False)
self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> col3<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('themes/test/default') self.assertAccessEvents(p, 'themes/test/default')
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
self.assertTrue(p._watcher._calls) self.assertTrue(p._watcher._calls)
pop_events()
def test_run_once_no_theme_reload(self): @with_new_config
with replace_item(globals(), 'config', deepcopy(config)): def test_run_once_no_theme_reload(self, config):
config['config']['common']['interval'] = None config['config']['common']['interval'] = None
with get_powerline(run_once=True) as p: with get_powerline(config, run_once=True) as p:
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents('config', 'colors', 'colorschemes/default', 'colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default') self.assertAccessEvents(p, 'config', 'colors', 'check:colorschemes/default', 'check:colorschemes/test/__main__', 'colorschemes/test/default', 'themes/test/default')
config['themes/test/default']['segments']['left'][0]['contents'] = 'col3' config['themes/test/default']['segments']['left'][0]['contents'] = 'col3'
add_watcher_events(p, 'themes/test/default', wait=False) add_watcher_events(p, 'themes/test/default', wait=False)
self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>><None None None>')
self.assertAccessEvents() self.assertAccessEvents(p)
self.assertEqual(p.logger._pop_msgs(), []) self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p._watcher._calls, [])
pop_events()
replaces = {}
def setUpModule():
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
tearDownModule = setUpModule
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,11 +1,8 @@
# vim:fileencoding=utf-8:noet # vim:fileencoding=utf-8:noet
from __future__ import unicode_literals, absolute_import, division from __future__ import unicode_literals, absolute_import, division
import tests.vim as vim_module import tests.vim as vim_module
import powerline as powerline_module
from tests import TestCase from tests import TestCase
from tests.lib import replace_item from tests.lib.config_mock import get_powerline, get_powerline_raw, swap_attributes
from tests.lib.config_mock import swap_attributes, get_powerline
from tests.lib.config_mock import get_powerline_raw
from functools import wraps from functools import wraps
from copy import deepcopy from copy import deepcopy
import sys import sys
@ -123,11 +120,19 @@ config = {
} }
def add_p_arg(func): def with_new_config(func):
@wraps(func) @wraps(func)
def f(self): def f(self):
with get_powerline(run_once=True, simpler_renderer=True) as p: return func(self, deepcopy(config))
func(self, p) return f
def add_args(func):
@wraps(func)
def f(self):
new_config = deepcopy(config)
with get_powerline(new_config, run_once=True, simpler_renderer=True) as p:
func(self, p, new_config)
return f return f
@ -140,166 +145,158 @@ class TestRender(TestCase):
class TestLines(TestRender): class TestLines(TestRender):
@add_p_arg @add_args
def test_without_above(self, p): def test_without_above(self, p, config):
self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}') self.assertRenderEqual(p, '{121} s{24}>>{344}g{34}>{34}<{344}f {--}')
self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', width=10) self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344}f {--}', width=10)
# self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344} f {--}', width=11) # self.assertRenderEqual(p, '{121} s {24}>>{344}g{34}>{34}<{344} f {--}', width=11)
self.assertEqual(list(p.render_above_lines()), []) self.assertEqual(list(p.render_above_lines()), [])
def test_with_above(self): @with_new_config
with replace_item(globals(), 'config', deepcopy(config)): def test_with_above(self, config):
old_segments = deepcopy(config['themes/test/default']['segments']) old_segments = deepcopy(config['themes/test/default']['segments'])
config['themes/test/default']['segments']['above'] = [old_segments] config['themes/test/default']['segments']['above'] = [old_segments]
with get_powerline(run_once=True, simpler_renderer=True) as p: with get_powerline(config, run_once=True, simpler_renderer=True) as p:
self.assertRenderLinesEqual(p, [ self.assertRenderLinesEqual(p, [
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}', '{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
]) ])
self.assertRenderLinesEqual(p, [ self.assertRenderLinesEqual(p, [
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}', '{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
], width=10) ], width=10)
config['themes/test/default']['segments']['above'] = [old_segments] * 2 config['themes/test/default']['segments']['above'] = [old_segments] * 2
with get_powerline(run_once=True, simpler_renderer=True) as p: with get_powerline(config, run_once=True, simpler_renderer=True) as p:
self.assertRenderLinesEqual(p, [ self.assertRenderLinesEqual(p, [
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}', '{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
'{121} s{24}>>{344}g{34}>{34}<{344}f {--}', '{121} s{24}>>{344}g{34}>{34}<{344}f {--}',
]) ])
self.assertRenderLinesEqual(p, [ self.assertRenderLinesEqual(p, [
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}', '{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
'{121} s {24}>>{344}g{34}>{34}<{344}f {--}', '{121} s {24}>>{344}g{34}>{34}<{344}f {--}',
], width=10) ], width=10)
class TestSegments(TestRender): class TestSegments(TestRender):
@add_p_arg @add_args
def test_display(self, p): def test_display(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): config['themes/test/default']['segments']['left'][0]['display'] = False
config['themes/test/default']['segments']['left'][0]['display'] = False config['themes/test/default']['segments']['left'][1]['display'] = True
config['themes/test/default']['segments']['left'][1]['display'] = True config['themes/test/default']['segments']['right'][0]['display'] = False
config['themes/test/default']['segments']['right'][0]['display'] = False self.assertRenderEqual(p, '{344} g{4-}>>{--}')
self.assertRenderEqual(p, '{344} g{4-}>>{--}')
class TestColorschemesHierarchy(TestRender): class TestColorschemesHierarchy(TestRender):
@add_p_arg @add_args
def test_group_redirects(self, p): def test_group_redirects(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('a', 'd1', draw_hard_divider=False),
highlighted_string('a', 'd1', draw_hard_divider=False), highlighted_string('b', 'd2', draw_hard_divider=False),
highlighted_string('b', 'd2', draw_hard_divider=False), highlighted_string('c', 'd3', draw_hard_divider=False),
highlighted_string('c', 'd3', draw_hard_divider=False), highlighted_string('A', 'm1', draw_hard_divider=False),
highlighted_string('A', 'm1', draw_hard_divider=False), highlighted_string('B', 'm2', draw_hard_divider=False),
highlighted_string('B', 'm2', draw_hard_divider=False), highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False), highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False), highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False), highlighted_string('3', 'g3', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}')
self.assertRenderEqual(p, '{78} a{910}b{1112}c{56}A{910}B{1112}C{56}1{78}2{910}3{--}') self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg @add_args
def test_group_redirects_no_main(self, p): def test_group_redirects_no_main(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): del config['colorschemes/test/__main__']
del config['colorschemes/test/__main__'] config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('a', 'd1', draw_hard_divider=False),
highlighted_string('a', 'd1', draw_hard_divider=False), highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False), highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False), highlighted_string('3', 'g3', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}')
self.assertRenderEqual(p, '{78} a{56}1{78}2{910}3{--}') self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg @add_args
def test_group_redirects_no_top_default(self, p): def test_group_redirects_no_top_default(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): del config['colorschemes/default']
del config['colorschemes/default'] config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('c', 'd3', draw_soft_divider=False),
highlighted_string('c', 'd3', draw_soft_divider=False), highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} self.assertRenderEqual(p, '{1112} c{1112}C{--}')
self.assertRenderEqual(p, '{1112} c{1112}C{--}') self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg @add_args
def test_group_redirects_no_test_default(self, p): def test_group_redirects_no_test_default(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): del config['colorschemes/test/default']
del config['colorschemes/test/default'] config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('A', 'm1', draw_hard_divider=False),
highlighted_string('A', 'm1', draw_hard_divider=False), highlighted_string('B', 'm2', draw_hard_divider=False),
highlighted_string('B', 'm2', draw_hard_divider=False), highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False), highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False), highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False), highlighted_string('3', 'g3', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}')
self.assertRenderEqual(p, '{56} A{910}B{1112}C{56}1{78}2{910}3{--}') self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg @add_args
def test_group_redirects_only_main(self, p): def test_group_redirects_only_main(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): del config['colorschemes/default']
del config['colorschemes/default'] del config['colorschemes/test/default']
del config['colorschemes/test/default'] config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('C', 'm3', draw_hard_divider=False),
highlighted_string('C', 'm3', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} # Powerline is not able to work without default colorscheme
# Powerline is not able to work without default colorscheme # somewhere, thus it will output error string
# somewhere, thus it will output error string self.assertRenderEqual(p, 'colorschemes/test/default')
self.assertRenderEqual(p, 'colorschemes/test/default') self.assertEqual(p.logger._pop_msgs(), [
self.assertEqual(p.logger._pop_msgs(), [ 'exception:test:powerline:Failed to load colorscheme: colorschemes/default',
'exception:test:powerline:Failed to load colorscheme: colorschemes/default', 'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default',
'exception:test:powerline:Failed to load colorscheme: colorschemes/test/default', 'exception:test:powerline:Failed to create renderer: colorschemes/test/default',
'exception:test:powerline:Failed to create renderer: colorschemes/test/default', 'exception:test:powerline:Failed to render: colorschemes/test/default',
'exception:test:powerline:Failed to render: colorschemes/test/default', ])
])
@add_p_arg @add_args
def test_group_redirects_only_top_default(self, p): def test_group_redirects_only_top_default(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): del config['colorschemes/test/__main__']
del config['colorschemes/test/__main__'] del config['colorschemes/test/default']
del config['colorschemes/test/default'] config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('1', 'g1', draw_hard_divider=False),
highlighted_string('1', 'g1', draw_hard_divider=False), highlighted_string('2', 'g2', draw_hard_divider=False),
highlighted_string('2', 'g2', draw_hard_divider=False), highlighted_string('3', 'g3', draw_hard_divider=False),
highlighted_string('3', 'g3', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}')
self.assertRenderEqual(p, '{56} 1{78}2{910}3{--}') self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.logger._pop_msgs(), [])
@add_p_arg @add_args
def test_group_redirects_only_test_default(self, p): def test_group_redirects_only_test_default(self, p, config):
with replace_item(globals(), 'config', deepcopy(config)): del config['colorschemes/default']
del config['colorschemes/default'] del config['colorschemes/test/__main__']
del config['colorschemes/test/__main__'] config['themes/test/default']['segments'] = {
config['themes/test/default']['segments'] = { 'left': [
'left': [ highlighted_string('s', 'str1', draw_hard_divider=False),
highlighted_string('s', 'str1', draw_hard_divider=False), ],
], 'right': [],
'right': [], }
} self.assertRenderEqual(p, '{121} s{--}')
self.assertRenderEqual(p, '{121} s{--}') self.assertEqual(p.logger._pop_msgs(), [])
self.assertEqual(p.logger._pop_msgs(), [])
class TestVim(TestCase): class TestVim(TestCase):
@ -307,28 +304,25 @@ class TestVim(TestCase):
# Regression test: test that segment obtains environment from vim, not # Regression test: test that segment obtains environment from vim, not
# from os.environ. # from os.environ.
from powerline.vim import VimPowerline from powerline.vim import VimPowerline
with vim_module._with('environ', TEST='abc'): import powerline as powerline_module
with get_powerline_raw(VimPowerline) as powerline: import vim
window = vim_module.current.window vim.vars['powerline_config_path'] = '/'
window_id = 1 with swap_attributes(config, powerline_module):
winnr = window.number with vim_module._with('environ', TEST='abc'):
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>') with get_powerline_raw(config, VimPowerline) as powerline:
vim_module._environ['TEST'] = 'def' window = vim_module.current.window
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>') window_id = 1
winnr = window.number
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0abc%#Pl_4_192_NONE_None_NONE#>>')
replaces = {} vim_module._environ['TEST'] = 'def'
self.assertEqual(powerline.render(window, window_id, winnr), '%#Pl_3_8404992_4_192_underline#\xa0def%#Pl_4_192_NONE_None_NONE#>>')
def setUpModule(): def setUpModule():
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path'))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path')))
def tearDownModule(): def tearDownModule():
global replaces
replaces = swap_attributes(globals(), powerline_module, replaces)
sys.path.pop(0) sys.path.pop(0)