def _module_factory( name='test', on_startup=None, on_modified=None, on_exit=None, path=None, module_directory=test_config_directory / 'test_modules' / 'using_all_actions', replacer=lambda x: x, context_store={}, ) -> Module: """Return module with specified action blocks and config.""" module = Module( name=name, module_config={}, module_directory=module_directory, replacer=replacer, context_store=context_store, ) if on_startup: module.action_blocks['on_startup'] = on_startup if on_exit: module.action_blocks['on_exit'] = on_exit if on_modified: module.action_blocks['on_modified'][path] = on_modified return module
def test_recursive_module_requirements(): """Missing dependencies should propagate.""" moduleA = Module( name='A', module_config={'requires': [{ 'module': 'B' }]}, module_directory=Path(__file__).parent, ) moduleB = Module( name='B', module_config={'requires': [{ 'module': 'C' }]}, module_directory=Path(__file__).parent, ) moduleC = Module( name='C', module_config={'requires': [{ 'module': 'D' }]}, module_directory=Path(__file__).parent, ) assert Requirement.pop_missing_module_dependencies(modules={ 'A': moduleA, 'B': moduleB, 'C': moduleC }, ) == {}
def test_module_requires_option(caplog): """Test that modules are disabled when they don't satisfy `requires`.""" # Simple module that satisfies the requirements does_satisfy_requiremnets = { 'enabled': True, 'requires': { 'shell': 'command -v cd' }, } assert Module.valid_module( name='satisfies', config=does_satisfy_requiremnets, requires_timeout=1, requires_working_directory=Path('/'), ) # Simple module that does not satisfy requirements does_not_satisfy_requirements = { 'requires': { 'shell': 'command -v does_not_exist' }, } assert not Module.valid_module( name='does_not_satisfy', config=does_not_satisfy_requirements, requires_timeout=1, requires_working_directory=Path('/'), ) assert ( 'astrality.module', logging.WARNING, '[module/does_not_satisfy] Module requirements: ' 'Unsuccessful command: "command -v does_not_exist", !', ) in caplog.record_tuples # Test failing one of many requirements does_not_satisfy_one_requirement = { 'requires': [ { 'shell': 'command -v cd' }, { 'shell': 'command -v does_not_exist' }, ], } caplog.clear() assert not Module.valid_module( name='does_not_satisfy', config=does_not_satisfy_one_requirement, requires_timeout=1, requires_working_directory=Path('/'), ) assert ( 'astrality.module', logging.WARNING, RegexCompare( r'\[module/does_not_satisfy\] Module requirements: .+ ' 'Unsuccessful command: "command -v does_not_exist", !', ), ) in caplog.record_tuples
def test_valid_class_section_with_wrongly_sized_dict( self, valid_module_section): invalid_module_section = valid_module_section invalid_module_section.update({'module/valid2': {'enabled': True}}) with pytest.raises(RuntimeError): Module.valid_class_section( section=invalid_module_section, requires_timeout=2, requires_working_directory=Path('/'), )
def test_using_default_static_event_listener_when_no_event_listener_is_given( self, test_config_directory): static_module = Module( module_config={'module/static': {}}, module_directory=test_config_directory, ) assert isinstance(static_module.event_listener, event_listener.Static)
def test_valid_class_section_method_with_valid_section( self, valid_module_section): assert Module.valid_class_section( section=valid_module_section, requires_timeout=2, requires_working_directory=Path('/'), ) == True
def module( valid_module_section, test_config_directory, ): return Module( module_config=valid_module_section, module_directory=test_config_directory, )
def test_that_module_block_is_persisted(): """Module should create a 'setup' action block.""" module_config = { 'on_setup': { 'run': { 'shell': 'echo first time!', }, }, } params = { 'name': 'test', 'module_config': module_config, 'module_directory': Path(__file__).parent, } module = Module(**params) assert isinstance( module.get_action_block(name='on_setup'), SetupActionBlock, ) assert module.execute(action='run', block='on_setup') == (( 'echo first time!', 'first time!', ), ) # After creating this module again, the run action should not be performed. del module module = Module(**params) assert module.execute(action='run', block='on_setup') == tuple()
def test_valid_class_section_method_with_valid_section( self, valid_module_section, ): assert Module.valid_module( name='test', config=valid_module_section, requires_timeout=2, requires_working_directory=Path('/'), ) is True
def test_using_default_static_event_listener_when_no_event_listener_given( self, test_config_directory, ): static_module = Module( name='static', module_config={}, module_directory=test_config_directory, ) assert isinstance(static_module.event_listener, event_listener.Static)
def test_valid_class_section_method_with_invalid_section(self): invalid_module_section = { 'context/fonts': { 'some_key': 'some_value', } } assert Module.valid_class_section( section=invalid_module_section, requires_timeout=2, requires_working_directory=Path('/'), ) == False
def module( valid_module_section, test_config_directory, ): return Module( name='test_module', module_config=valid_module_section['test_module'], module_directory=test_config_directory, context_store=Context({ 'fonts': {1: 'FuraMono Nerd Font'}, }), )
def test_valid_class_section_method_with_disabled_module_section(self): disabled_module_section = { 'enabled': False, 'on_startup': {'run': ['test']}, 'on_event': {'run': ['']}, 'on_exit': {'run': ['whatever']}, } assert Module.valid_module( name='test', config=disabled_module_section, requires_timeout=2, requires_working_directory=Path('/'), ) is False
def test_module_without_event_listener_does_not_need_to_keep_running(): """on_event without event listener need not to keep running.""" module = Module( name='test', module_config={ 'on_event': { 'run': { 'shell': 'modified!' }, }, }, module_directory=Path('/'), ) assert module.keep_running is False
def test_requiring_a_global_module(): """You should be able to require global modules.""" moduleA = Module( name='A', module_config={'requires': [{ 'module': 'B' }]}, module_directory=Path(__file__).parent, ) assert moduleA.depends_on == ('B', ) moduleB = Module( name='B', module_config={'run': { 'shell': 'echo hi!' }}, module_directory=Path(__file__).parent, ) assert moduleB.depends_on == tuple() moduleC = Module( name='C', module_config={'requires': [{ 'module': 'D' }]}, module_directory=Path(__file__).parent, ) assert moduleC.depends_on == ('D', ) assert Requirement.pop_missing_module_dependencies(modules={ 'A': moduleA, 'B': moduleB, 'C': moduleC }, ) == { 'A': moduleA, 'B': moduleB }
def test_module_that_needs_to_keep_running_due_to_on_modified(): """on_modified block needs to keep running.""" module = Module( name='test', module_config={ 'on_modified': { 'some/path': { 'run': { 'shell': 'modified!' }, }, }, }, module_directory=Path('/'), ) assert module.keep_running is True
def test_module_that_does_not_need_to_keep_running(): """on_startup block and on_exit block does not need to keep running.""" module = Module( name='test', module_config={ 'run': { 'shell': 'hi!' }, 'on_exit': { 'run': { 'shell': 'modified!' }, }, }, module_directory=Path('/'), ) assert module.keep_running is False
def test_module_that_needs_to_keep_running_due_to_on_event(): """on_event with event listener need to keep running.""" module = Module( name='test', module_config={ 'event_listener': { 'type': 'weekday' }, 'on_event': { 'run': { 'shell': 'modified!' }, }, }, module_directory=Path('/'), ) assert module.keep_running is True
def test_defining_on_startup_block_at_root_indentation(caplog): """Root indentation actions should be promoted to on_startup.""" # Test that no root actions are not affected module_config = { 'on_startup': { 'run': [{'shell': 'echo on_startup'}], }, } assert Module.prepare_on_startup_block( module_name='test', module_config=module_config, ) == { 'on_startup': { 'run': [{'shell': 'echo on_startup'}], }, } # Test that actions are moved into empty block module_config = { 'run': [{'shell': 'touch stuff'}], 'compile': {'source': 'some/path'}, } assert Module.prepare_on_startup_block( module_name='test', module_config=module_config, ) == { 'on_startup': { 'run': [{'shell': 'touch stuff'}], 'compile': {'source': 'some/path'}, }, } # Test that overwriting values are logged caplog.clear() module_config = { 'run': [{'shell': 'echo overwritten'}], 'on_startup': { 'run': [{'shell': 'echo original'}], }, } assert Module.prepare_on_startup_block( module_name='test', module_config=module_config, ) == { 'on_startup': { 'run': [{'shell': 'echo overwritten'}], }, } assert caplog.record_tuples[0][1] == logging.ERROR # Test that we only have partial overwrites module_config = { 'stow': [{'content': 'new_stow'}], 'copy': {'content': 'new_copy'}, 'on_startup': { 'run': [{'shell': 'run'}], 'stow': {'content': 'old_stow'}, 'copy': {'content': 'old_copy'}, }, 'on_exit': {}, } assert Module.prepare_on_startup_block( module_name='test', module_config=module_config, ) == { 'on_startup': { 'run': [{'shell': 'run'}], 'stow': [{'content': 'new_stow'}], 'copy': {'content': 'new_copy'}, }, 'on_exit': {}, } # Test that prepare_on_startup_block is actually used module_config = { 'run': [{'shell': 'echo overwritten'}], 'on_startup': { 'run': [{'shell': 'echo original'}], }, } module = Module( name='test_module', module_config=module_config, module_directory=Path('/'), ) assert module.execute(action='run', block='on_startup') \ == (('echo overwritten', 'overwritten'),)