예제 #1
0
def test_all_three_actions_in_on_modified_block(
    three_watchable_files,
    test_config_directory,
):
    file1, file2, file3 = three_watchable_files
    car_template = test_config_directory / 'templates' / 'a_car.template'
    mercedes_context = test_config_directory / 'context' / 'mercedes.yml'
    tesla_context = test_config_directory / 'context' / 'tesla.yml'

    modules = {
        'car': {
            'on_startup': {
                'import_context': {
                    'from_path': str(mercedes_context),
                },
                'compile': {
                    'content': str(car_template),
                    'target': str(file1),
                },
            },
            'on_modified': {
                str(file2): {
                    'import_context': {
                        'from_path': str(tesla_context),
                    },
                    'compile': {
                        'content': str(car_template),
                        'target': str(file1),
                    },
                    'run': {
                        'shell': 'touch ' + str(file3)
                    },
                },
            },
        },
    }
    module_manager = ModuleManager(modules=modules)

    # Sanity check before beginning testing
    assert not file1.is_file()
    assert not file2.is_file()
    assert not file3.is_file()

    # Now finish tasks, i.e. on_startup block
    module_manager.finish_tasks()
    assert file1.is_file()
    assert not file2.is_file()
    assert not file3.is_file()

    # Check that the correct context is inserted
    with open(file1) as file:
        assert file.read() == 'My car is a Mercedes'

    # Now modify file2 such that the on_modified block is triggered
    file2.write_text('some new content')

    # The on_modified run command should now have been executed
    assert Retry()(lambda: file3.is_file())

    module_manager.exit()
예제 #2
0
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())
예제 #3
0
def test_recompile_templates_when_modified_overridden(
    three_watchable_files,
    test_config_directory,
):
    """
    If a file is watched in a on_modified block, it should override the
    reprocess_modified_files option.
    """
    template, target, touch_target = three_watchable_files
    template.touch()

    modules = {
        'module_name': {
            'on_startup': {
                'compile': {
                    'content': str(template),
                    'target': str(target),
                },
            },
            'on_modified': {
                str(template): {
                    'run': {'shell': 'touch ' + str(touch_target)},
                },
            },
        },
    }

    application_config = {'modules': {'reprocess_modified_files': True}}
    module_manager = ModuleManager(
        config=application_config,
        modules=modules,
        context=Context({
            'section': {1: 'value'},
        }),
        directory=test_config_directory,
    )

    # Sanity check before beginning testing
    with open(template) as file:
        assert file.read() == ''

    assert not target.is_file()

    # Compile the template
    module_manager.finish_tasks()
    with open(target) as file:
        assert file.read() == ''

    # Now write to the template and see if it is *compiled*, but the on_modified
    # command is run instead
    template.write_text('{{ section.2 }}')

    retry = Retry()
    assert retry(lambda: target.read_text() == '')
    assert retry(lambda: touch_target.is_file())

    module_manager.exit()
예제 #4
0
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()
예제 #5
0
def test_filesystem_watcher(watch_dir):
    """
    Test correct callback invocation on directory watching.

    Sometimes the on_modified function is called several times by watchdog,
    for a unknown reason. It might be other tests which interfer. We therefore
    check if the lower bound of calls is satisfied, but do not test the exact
    number of calls to on_modified.
    """
    (
        watched_directory,
        recursive_dir,
        test_file1,
        test_file2,
        dir_watcher,
        event_saver,
    ) = watch_dir

    # Start watching the directory
    dir_watcher.start()

    # Nothing has been modified yet
    assert event_saver.argument is None
    assert event_saver.called == 0

    # Create an empty file
    test_file1.touch()

    # We might have to try several times, as filewatching can be slow
    retry = Retry()

    # New files are not considered "modified"
    assert event_saver.argument is None
    assert event_saver.called == 0

    # But when we write to it, it is considered "modified"
    test_file1.write_text('test_content')

    assert retry(lambda: event_saver.argument == test_file1)
    assert event_saver.called >= 1

    # Create a directory in the watched directory
    recursive_dir.mkdir(parents=True)

    # Subdirectories are not of interest
    assert retry(lambda: event_saver.argument == test_file1)
    assert retry(lambda: event_saver.called >= 1)

    # Create a file in the subdirectory
    test_file2.write_text('test')

    # Both the touch event and the write event are considered of interest
    assert retry(lambda: event_saver.argument == test_file2)
    assert event_saver.called == 2
예제 #6
0
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('')
예제 #7
0
def test_that_no_reprocess_modified_files_does_not_cause_keep_running():
    """ModuleManager without reprocess_modified_files does not keep_running."""
    module_manager = ModuleManager(
        config={
            'modules': {
                'reprocess_modified_files': False,
                'enabled_modules': [{
                    'name': 'A'
                }],
            },
        },
        modules={'A': {}},
    )

    # We have to retry here, as processes from earlier tests might interfer
    assert Retry()(lambda: module_manager.keep_running is False)
예제 #8
0
 def test_killing_old_running_process(self):
     """The same running process should be killed."""
     perpetual_process = psutil.Popen([
         'python',
         '-c',
         '"from time import sleep; sleep(9999999999999)"',
     ])
     pidfile = XDG().data('astrality.pid')
     utils.dump_yaml(
         data=perpetual_process.as_dict(
             attrs=['pid', 'create_time', 'username'],
         ),
         path=pidfile,
     )
     kill_old_astrality_processes()
     assert Retry()(lambda: not perpetual_process.is_running())
예제 #9
0
def test_that_stowed_templates_are_also_watched(three_watchable_files):
    """Stowing template instead of compiling it should still be watched."""
    template, target, _ = three_watchable_files
    template.touch()

    modules = {
        'module_name': {
            'on_startup': {
                'stow': {
                    'content': str(template),
                    'target': str(target),
                    'templates': '(.+)',
                    'non_templates': 'ignore',
                },
            },
        },
    }

    application_config = {'modules': {'reprocess_modified_files': True}}
    module_manager = ModuleManager(
        config=application_config,
        modules=modules,
        context=Context({
            'section': {
                1: 'value'
            },
        }),
    )

    # Sanity check before beginning testing
    with open(template) as file:
        assert file.read() == ''

    assert not target.is_file()

    # Stow the template
    module_manager.finish_tasks()
    with open(target) as file:
        assert file.read() == ''

    # Now write to the template and see if it is recompiled
    template.write_text('{{ section.2 }}')
    assert Retry()(lambda: target.read_text() == 'value')

    module_manager.exit()
예제 #10
0
def test_recompile_templates_when_modified(three_watchable_files):
    template, target, _ = three_watchable_files
    template.touch()

    modules = {
        'module_name': {
            'on_startup': {
                'compile': {
                    'content': str(template),
                    'target': str(target),
                },
            },
        },
    }

    application_config = {'modules': {'reprocess_modified_files': True}}
    module_manager = ModuleManager(
        config=application_config,
        modules=modules,
        context=Context({
            'section': {
                1: 'value'
            },
        }),
    )

    # Sanity check before beginning testing
    with open(template) as file:
        assert file.read() == ''

    assert not target.is_file()

    # Compile the template
    module_manager.finish_tasks()
    with open(target) as file:
        assert file.read() == ''

    # Now write to the template and see if it is recompiled
    template.write_text('{{ section.2 }}')
    assert Retry()(lambda: target.read_text() == 'value')

    module_manager.exit()
    module_manager.directory_watcher.stop()
예제 #11
0
    def test_not_killing_new_procces_with_same_pid(self):
        """The process should not be killed when it is not the original saved"""
        perpetual_process = psutil.Popen([
            'python',
            '-c',
            '"from time import sleep; sleep(9999999999999)"',
        ])

        process_data = perpetual_process.as_dict(
            attrs=['pid', 'create_time', 'username'],
        )
        process_data['create_time'] += 1

        utils.dump_yaml(
            data=process_data,
            path=XDG().data('astrality.pid'),
        )
        kill_old_astrality_processes()
        assert Retry()(lambda: perpetual_process.is_running())
        perpetual_process.kill()
예제 #12
0
def test_on_modified_event_in_module(modules_config):
    (
        modules,
        empty_template,
        empty_template_target,
        touch_target,
        secondary_template,
        secondary_template_target,
    ) = modules_config

    module_manager = ModuleManager(modules=modules)

    # Start the file watcher by invoking the startup command indirectly
    # through finish_tasks() method
    module_manager.finish_tasks()

    # Assert that the template file is really empty as a sanity check
    assert empty_template.read_text() == ''

    # And that target files are not created yet
    assert not touch_target.is_file()
    assert not empty_template_target.is_file()
    assert not secondary_template_target.is_file()

    # Trigger the on_modified event
    empty_template.write_text('new content')

    # And assert that the new template has been compiled
    retry = Retry()
    assert retry(lambda: empty_template_target.is_file())
    assert retry(lambda: empty_template_target.read_text() == 'new content')

    # Assert that also templates from other modules are compiled
    assert retry(lambda: secondary_template_target.is_file())
    assert retry(
        lambda: secondary_template_target.read_text() == 'one\ntwo\nthree',
    )

    # And that the new file has been touched
    assert retry(lambda: touch_target.is_file())
예제 #13
0
def test_that_only_startup_event_block_is_run_on_startup(
    two_test_file_paths,
    test_config_directory,
    freezer,
):
    thursday = datetime(
        year=2018,
        month=2,
        day=15,
        hour=12,
    )
    freezer.move_to(thursday)

    test_file1, test_file2 = two_test_file_paths
    modules = {
        'A': {
            'event_listener': {'type': 'weekday'},
            'on_startup': {
                'run': [{'shell': 'touch ' + str(test_file1)}],
            },
            'on_event': {
                'run': [{'shell': 'touch ' + str(test_file2)}],
            },
        },
    }

    module_manager = ModuleManager(
        modules=modules,
    )

    # 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()

    retry = Retry()
    assert retry(lambda: test_file1.is_file())
    assert retry(lambda: not test_file2.is_file())
예제 #14
0
def test_importing_context_on_modification(
    three_watchable_files,
    test_config_directory,
):
    """Test that context values are imported in on_modified blocks."""
    file1, *_ = three_watchable_files
    mercedes_context = test_config_directory / 'context' / 'mercedes.yml'

    modules = {
        'module_name': {
            'on_modified': {
                str(file1): {
                    'import_context': {
                        'from_path': str(mercedes_context),
                    },
                },
            },
        },
    }
    module_manager = ModuleManager(
        modules=modules,
        context=Context({
            'car': {
                'manufacturer': 'Tesla'
            },
        }),
    )
    module_manager.finish_tasks()

    # Sanity check before modifying file1
    assert module_manager.application_context['car']['manufacturer'] == 'Tesla'

    # After modifying file1, Mercedes should have been imported
    file1.touch()
    file1.write_text('new content, resulting in importing Mercedes')
    assert Retry()(lambda: module_manager.application_context['car'][
        'manufacturer'] == 'Mercedes', )