def test_copying_in_on_modified_block( action_block_factory, create_temp_files, module_factory, ): """Module should copy properly.""" file1, file2, file3, file4 = create_temp_files(4) file2.write_text('original') file4.write_text('some other content') action_block = action_block_factory(copy=[ { 'content': str(file1), 'target': str(file2) }, { 'content': str(file3), 'target': str(file4) }, ], ) module = module_factory(on_modified=action_block, path=Path('/a/b/c')) module_manager = ModuleManager() module_manager.modules = {'test': module} module_manager.on_modified(Path('/a/b/c')) # Check if content has been copied assert file2.read_text() == file1.read_text() assert file4.read_text() == file3.read_text()
def test_missing_template_file( self, default_global_options, _runtime, caplog, ): application_config = { 'module/test_module': { 'on_startup': { 'compile': [ { 'source': '/not/existing' }, ], }, }, } application_config.update(default_global_options) application_config.update(_runtime) module_manager = ModuleManager(application_config) caplog.clear() module_manager.finish_tasks() assert 'Could not compile template "/not/existing" '\ 'to target "' in caplog.record_tuples[0][2]
def test_symlinking_in_on_startup_block( action_block_factory, module_factory, create_temp_files, ): """ModuleManager should symlink properly.""" file1, file2, file3, file4 = create_temp_files(4) file2.write_text('original') action_block = action_block_factory(symlink=[ { 'content': str(file1), 'target': str(file2) }, { 'content': str(file3), 'target': str(file4) }, ], ) module = module_factory(on_startup=action_block) module_manager = ModuleManager() module_manager.modules = {'test': module} module_manager.finish_tasks() assert file2.is_symlink() assert file2.resolve() == file1 assert file4.is_symlink() assert file4.resolve() == file3
def test_that_shell_filter_is_run_from_config_directory(test_config_directory): shell_filter_template = Path(__file__).parents[1] \ / 'templates' / 'shell_filter_working_directory.template' shell_filter_template_target = Path( '/tmp/astrality/shell_filter_working_directory.template', ) modules = { 'A': { 'on_startup': { 'compile': [ { 'content': str(shell_filter_template), 'target': str(shell_filter_template_target), }, ], }, }, } module_manager = ModuleManager(modules=modules) module_manager.execute(action='compile', block='on_startup') with open(shell_filter_template_target) as compiled: assert compiled.read() == str(test_config_directory) os.remove(shell_filter_template_target)
def test_compilation_of_template( self, simple_application_config, module, conf, caplog, ): simple_application_config['module/test_module']['event_listener'][ 'type'] = 'solar' compiled_template_content = 'some text\n' + os.environ[ 'USER'] + '\nFuraMono Nerd Font' module_manager = ModuleManager(simple_application_config) directory = module_manager.config_directory caplog.clear() module_manager.compile_templates('on_startup') template_file = str( (directory / '../templates/test_template.conf').resolve()) compiled_template = str( list( module_manager.modules['test_module']\ .performed_compilations()[Path(template_file)]\ )[0] ) with open('/tmp/compiled_result', 'r') as file: compiled_result = file.read() assert compiled_template_content == compiled_result assert caplog.record_tuples == [ ('astrality', logging.INFO, f'[Compiling] Template: "{template_file}" -> Target: "{compiled_template}"' ), ]
def test_that_shell_filter_is_run_from_config_directory( default_global_options, _runtime, test_config_directory, ): shell_filter_template = Path(__file__).parents[ 1] / 'templates' / 'shell_filter_working_directory.template' shell_filter_template_target = Path( '/tmp/astrality/shell_filter_working_directory.template') config = { 'module/A': { 'on_startup': { 'compile': [{ 'source': str(shell_filter_template), 'target': str(shell_filter_template_target), }], }, }, } config.update(default_global_options) config.update(_runtime) module_manager = ModuleManager(config) module_manager.compile_templates('on_startup') with open(shell_filter_template_target) as compiled: assert compiled.read() == str(test_config_directory) os.remove(shell_filter_template_target)
def test_compiling_template_with_specific_permissions( test_config_directory, tmpdir, specified_permission, expected_permission, ): template = test_config_directory / 'templates' / 'empty.template' target = Path(tmpdir) / 'target' modules = { 'test': { 'on_startup': { 'compile': { 'content': str(template), 'target': str(target), 'permissions': specified_permission, }, }, }, } module_manager = ModuleManager(modules=modules, ) module_manager.finish_tasks() assert (target.stat().st_mode & 0o777) == expected_permission
def test_direct_invocation_of_modifed_method_of_module_manager(modules_config): ( modules, empty_template, empty_template_target, touch_target, secondary_template, secondary_template_target, ) = modules_config module_manager = ModuleManager(modules=modules) # PS: Disabling the directory watcher is not necessary, as it is done in # the startup method. # Now write new text to the template empty_template.write_text('new content') # And trigger the modified method manually module_manager.file_system_modified(empty_template) # And assert that the new template has been compiled assert empty_template_target.is_file() with open(empty_template_target) as file: assert file.read() == 'new content' # And that the new file has been touched assert Retry()(lambda: touch_target.is_file())
def test_stowing( action_block_factory, create_temp_files, module_factory, ): """ModuleManager should stow properly.""" template, target = create_temp_files(2) template.write_text('{{ env.EXAMPLE_ENV_VARIABLE }}') symlink_target = template.parent / 'symlink_me' symlink_target.touch() action_block = action_block_factory(stow={ 'content': str(template.parent), 'target': str(target.parent), 'templates': r'file(0).temp', 'non_templates': 'symlink', }, ) module = module_factory(on_exit=action_block, ) module_manager = ModuleManager() module_manager.modules = {'test': module} module_manager.exit() # Check if template has been compiled assert Path(target.parent / '0').read_text() == 'test_value' # Check if non_template has been symlinked assert (template.parent / 'symlink_me').resolve() == symlink_target
def test_compiling_template_with_specific_permissions( default_global_options, _runtime, test_config_directory, tmpdir, specified_permission, expected_permission, ): template = test_config_directory / 'templates' / 'empty.template' target = Path(tmpdir) / 'target' application_config = { 'module/test': { 'on_startup': { 'compile': { 'source': str(template), 'target': str(target), 'permissions': specified_permission, }, }, }, } application_config.update(default_global_options) application_config.update(_runtime) module_manager = ModuleManager(application_config) module_manager.finish_tasks() assert (target.stat().st_mode & 0o777) == expected_permission
def test_hot_reloading( test_template_targets, test_config_directory, ): template_target1, template_target2 = test_template_targets config1 = test_config_directory / 'modules1.yml' config2 = test_config_directory / 'modules2.yml' target_config = test_config_directory / 'modules.yml' # Copy the first configuration into place shutil.copy(str(config1), str(target_config)) modules1 = utils.compile_yaml( config1, context={}, ) application_config1 = {'astrality': {'hot_reload_config': True}} module_manager = ModuleManager( config=application_config1, modules=modules1, directory=test_config_directory, ) # Before beginning, the template should not be compiled assert not template_target1.is_file() # But when we finalize tasks, it should be compiled module_manager.finish_tasks() assert template_target1.is_file() # Also check that the filewatcher has been started assert module_manager.directory_watcher.observer.is_alive() # We now "edit" the configuration file shutil.copy(str(config2), str(target_config)) # Since hot reloading is enabled, the new template target should be # compiled, and the old one cleaned up retry = Retry() assert retry(lambda: template_target2.is_file()) assert retry(lambda: not template_target1.is_file()) # And we switch back again shutil.copy(str(config1), str(target_config)) assert retry(lambda: template_target1.is_file()) assert retry(lambda: not template_target2.is_file()) # Cleanup config file if target_config.is_file(): os.remove(target_config) # Stop the filewatcher module_manager.directory_watcher.stop()
def test_that_running_processes_causes_keep_running(): """If shell commands are running, keep_running should be True.""" module_manager = ModuleManager(modules={ 'A': { 'run': { 'shell': 'sleep 10' } }, }, ) module_manager.finish_tasks() assert module_manager.keep_running is True
def test_that_cleanup_cli_works( method, create_temp_files, patch_xdg_directory_standard, ): """--cleanup module_name, all module created files should be deleted""" ( template1, template2, template3, target1, target2, target3, ) = create_temp_files(6) for template in (template1, template2, template3): template.write_text('new content') for target in (target1, target2, target3): target.write_text('original content') modules = { 'A': { method: [ { 'content': str(template1), 'target': str(target1), }, { 'content': str(template2), 'target': str(target2), }, ], }, 'B': { method: { 'content': str(template3), 'target': str(target3), }, }, } module_manager = ModuleManager(modules=modules) module_manager.finish_tasks() for target in (target1, target2, target3): assert target.resolve().read_text() == 'new content' bin_script = str(Path(__file__).parents[3] / 'bin' / 'astrality') data_home = 'XDG_DATA_HOME="' + str(patch_xdg_directory_standard) + '/.." ' command = data_home + bin_script + ' --cleanup A --cleanup B' os.system(command) for target in (target1, target2, target3): assert target.resolve().read_text() == 'original content'
def test_that_only_startup_event_block_is_run_on_startup( two_test_file_paths, test_config_directory, default_global_options, _runtime, freezer, ): thursday = datetime( year=2018, month=2, day=15, hour=12, ) freezer.move_to(thursday) test_file1, test_file2 = two_test_file_paths application_config = { 'module/A': { 'event_listener': { 'type': 'weekday' }, 'on_startup': { 'run': [{ 'shell': 'touch ' + str(test_file1) }], }, 'on_event': { 'run': [{ 'shell': 'touch ' + str(test_file2) }], }, }, } application_config.update(default_global_options) application_config.update(_runtime) module_manager = ModuleManager(application_config) # Before call to finish_tasks, no actions should have been performed assert not test_file1.is_file() and not test_file2.is_file() # Now call finish_tasks for the first time, only startup event block should # be run module_manager.finish_tasks() time.sleep(0.5) assert test_file1.is_file() assert not test_file2.is_file() friday = datetime( year=2018, month=2, day=16, hour=12, )
def test_using_finish_tasks_on_example_configuration( self, conf, modules, context, ): module_manager = ModuleManager( config=conf, modules=modules, context=context, ) module_manager.finish_tasks()
def test_compilation_of_template( self, valid_module_section, simple_application_config, module, conf, caplog, ): valid_module_section[ 'test_module' ][ 'event_listener' ][ 'type' ] = 'solar' compiled_template_content = 'some text\n' + os.environ['USER'] \ + '\nFuraMono Nerd Font' module_manager = ModuleManager( config=simple_application_config, modules=valid_module_section, context=Context({ 'fonts': {1: 'FuraMono Nerd Font'}, }), ) directory = module_manager.config_directory caplog.clear() module_manager.execute(action='compile', block='on_startup') template_file = str( (directory / '../templates/test_template.conf').resolve(), ) compiled_template = str( list( module_manager.modules['test_module'] .performed_compilations()[Path(template_file)], )[0], ) with open('/tmp/compiled_result', 'r') as file: compiled_result = file.read() assert compiled_template_content == compiled_result assert caplog.record_tuples == [ ( 'astrality.compiler', logging.INFO, f'[Compiling] Template: "{template_file}" ' f'-> Target: "{compiled_template}"', ), ]
def test_that_setup_block_is_only_executed_once(tmpdir): """Setup blocks in modules should only be performed once.""" touched = Path(tmpdir, 'touched.tmp') modules = { 'A': { 'on_setup': { 'run': { 'shell': f'touch {touched}', }, }, }, } module_manager = ModuleManager(modules=modules) # The touched file should not exist before we have done anything assert not touched.exists() # After finishing tasks, the file should be touched module_manager.finish_tasks() assert touched.exists() # We now create a new object lifetime del module_manager touched.unlink() # The setup block should now *not* be executed module_manager = ModuleManager(modules=modules) module_manager.finish_tasks() assert not touched.exists()
def test_hot_reloading(test_template_targets, default_global_options, _runtime, test_config_directory): template_target1, template_target2 = test_template_targets config1 = test_config_directory / 'astrality1.yml' config2 = test_config_directory / 'astrality2.yml' target_config = test_config_directory / 'astrality.yml' temp_directory = Path('/tmp/astrality') # Copy the first configuration into place shutil.copy(str(config1), str(target_config)) application_config1 = dict_from_config_file( config1, context={}, ) application_config1.update(default_global_options) application_config1.update(_runtime) application_config1['config/astrality']['hot_reload_config'] = True module_manager = ModuleManager(application_config1) # Before beginning, the template should not be compiled assert not template_target1.is_file() # But when we finalize tasks, it should be compiled module_manager.finish_tasks() assert template_target1.is_file() # Also check that the filewatcher has been started assert module_manager.directory_watcher.observer.is_alive() # We now "edit" the configuration file shutil.copy(str(config2), str(target_config)) time.sleep(0.7) # Since hot reloading is enabled, the new template target should be # compiled, and the old one cleaned up assert template_target2.is_file() assert not template_target1.is_file() # And we switch back again shutil.copy(str(config1), str(target_config)) time.sleep(0.7) assert template_target1.is_file() assert not template_target2.is_file() # Cleanup config file if target_config.is_file(): os.remove(target_config)
def test_correct_relative_paths_used_in_external_module( temp_test_files, test_config_directory, ): application_config = { 'modules': { 'modules_directory': 'test_modules', 'enabled_modules': [{ 'name': 'using_all_actions::*' }], }, } module_manager = ModuleManager(config=application_config) ( compile_target, touch_target, watch_touch_target, watched_file, ) = temp_test_files # Sanity check before testing for file in ( compile_target, touch_target, watch_touch_target, ): assert not file.is_file() # Finish task and see if context import, compilation, and run has been # correctly run relative to the module directory path module_manager.finish_tasks() with open(compile_target, 'r') as file: assert file.read() == "Vietnam's capitol is Ho Chi Minh City" assert touch_target.is_file() # Now modify the observed file, and see if on_modified is triggered watched_file.write_text('This watched file has been modified') retry = Retry() assert retry( lambda: compile_target.read_text() == "Vietnam's capitol is Hanoi", ) assert retry(lambda: watch_touch_target.is_file()) touch_target.unlink() compile_target.unlink() watch_touch_target.unlink() watched_file.write_text('')
def test_not_using_list_when_specifiying_trigger_action( conf_path, default_global_options, ): application_config = { 'module/A': { 'on_startup': { 'trigger': { 'block': 'on_event', }, }, 'on_event': { 'run': [{ 'shell': 'echo on_event' }], }, }, '_runtime': { 'config_directory': conf_path, 'temp_directory': Path('/tmp/astrality'), }, } application_config.update(default_global_options) module_manager = ModuleManager(application_config) # Check that all run commands have been imported into startup block result = module_manager.modules['A'].run('on_startup', default_timeout=1) assert result == (( 'echo on_event', 'on_event', ), )
def _module_manager_factory( *modules, context=Context(), ) -> ModuleManager: """Return ModuleManager object with given modules and context.""" module_manager = ModuleManager(context=context, ) module_manager.modules = {module.name: module for module in modules} # Insert correct context for all actions for module in modules: for block in module.all_action_blocks(): for action_type in ActionBlock.action_types: for actions in getattr(block, f'_{action_type}_actions'): actions.context_store = context return module_manager
def test_missing_template_file(self, caplog): modules = { 'test_module': { 'on_startup': { 'compile': [ {'content': '/not/existing'}, ], }, }, } module_manager = ModuleManager(modules=modules) caplog.clear() module_manager.finish_tasks() assert 'Could not compile template "/not/existing" '\ 'to target "' in caplog.record_tuples[0][2]
def test_not_using_list_when_specifiying_trigger_action(conf_path): modules = { 'A': { 'on_startup': { 'trigger': {'block': 'on_event'}, }, 'on_event': { 'run': [{'shell': 'echo on_event'}], }, }, } module_manager = ModuleManager( modules=modules, directory=conf_path, ) # Check that all run commands have been imported into startup block result = module_manager.modules['A'].execute( action='run', block='on_startup', ) assert result == ( ('echo on_event', 'on_event'), )
def test_enabling_of_modules_defined_different_places( default_global_options, _runtime, ): application_config = { 'config/modules': { 'modules_directory': 'freezed_modules', 'enabled_modules': [ { 'name': 'south_america::brazil' }, { 'name': 'india' }, ], }, 'module/india': {}, # Enabled 'module/pakistan': {}, # Not enabled } application_config.update(default_global_options) application_config.update(_runtime) module_manager = ModuleManager(application_config) assert len(module_manager.modules) == 2 assert 'south_america::brazil' in module_manager.modules assert 'india' in module_manager.modules
def test_using_three_different_module_sources( test_config_directory, delete_jakobgm, ): modules_directory = test_config_directory / 'freezed_modules' application_config = { 'modules': { 'modules_directory': str(modules_directory), 'enabled_modules': [ {'name': 'north_america::*'}, {'name': 'github::jakobgm/test-module.astrality'}, {'name': 'italy'}, ], }, } modules = { 'italy': {}, 'spain': {}, } module_manager = ModuleManager( config=application_config, modules=modules, ) assert len(module_manager.modules) == 4 assert 'north_america::USA' in module_manager.modules assert 'github::jakobgm/test-module.astrality::botswana' \ in module_manager.modules assert 'github::jakobgm/test-module.astrality::ghana' \ in module_manager.modules assert 'italy' in module_manager.modules
def test_that_external_modules_are_brought_in( test_config_directory, default_global_options, _runtime, ): application_config = { 'config/modules': { 'modules_directory': 'test_modules', 'enabled_modules': [ { 'name': 'thailand::thailand' }, { 'name': '*' }, ], }, 'module/cambodia': { 'enabled_modules': True, }, } application_config.update(default_global_options) application_config.update(_runtime) module_manager = ModuleManager(application_config) thailand_path = test_config_directory / 'test_modules' / 'thailand' assert tuple(module_manager.modules.keys()) == ( 'thailand::thailand', 'cambodia', )
def test_that_external_module_contexts_are_imported_correctly( test_config_directory, ): application_config = { 'modules': { 'modules_directory': 'test_modules', 'enabled_modules': [{ 'name': 'module_with_context::*' }], }, } context = Context({ 'china': { 'capitol': 'beijing', }, }) module_manager = ModuleManager( config=application_config, context=context, ) expected_context = Context({ 'laos': { 'capitol': 'vientiane' }, 'china': { 'capitol': 'beijing' }, }) assert module_manager.application_context == expected_context
def test_that_external_modules_are_brought_in(test_config_directory): application_config = { 'modules': { 'modules_directory': 'test_modules', 'enabled_modules': [ { 'name': 'thailand::thailand' }, { 'name': '*' }, ], }, } modules = { 'cambodia': { 'enabled_modules': True, }, } module_manager = ModuleManager( config=application_config, modules=modules, ) assert tuple(module_manager.modules.keys()) == ( 'thailand::thailand', 'cambodia', )
def test_enabling_of_modules_defined_different_places(): application_config = { 'modules': { 'modules_directory': 'freezed_modules', 'enabled_modules': [ { 'name': 'south_america::brazil' }, { 'name': 'india' }, ], }, } modules = { 'india': {}, # Enabled 'pakistan': {}, # Not enabled } module_manager = ModuleManager( config=application_config, modules=modules, ) assert len(module_manager.modules) == 2 assert 'south_america::brazil' in module_manager.modules assert 'india' in module_manager.modules
def test_that_external_module_contexts_are_imported_correctly( test_config_directory, default_global_options, _runtime, ): application_config = { 'config/modules': { 'modules_directory': 'test_modules', 'enabled_modules': [{ 'name': 'module_with_context::*', }], }, 'context/china': { 'capitol': 'beijing', }, } application_config.update(default_global_options) application_config.update(_runtime) module_manager = ModuleManager(application_config) expected_context = { 'laos': { 'capitol': 'vientiane' }, 'china': { 'capitol': 'beijing' }, } assert len(module_manager.application_context) == 2 for key, value in expected_context.items(): assert module_manager.application_context[key] == value